首页 > 系统 > Android > 正文

Android自定义控件实例(1)——自定义控件之组合控件,包含书签的pdf阅读器

2019-11-09 17:18:40
字体:
来源:转载
供稿:网友
前言我们知道,在android端显示文档内容时,大多都是将文档转换为html页面,然后加载到webview中进行展示。这种展示方法好处在于可以结合html5、CSS和js做出非常美观的文档。但是对于html5、css和js不熟悉的人,可就有点蛋疼了。由于项目的需要,我基于github开源的AndroidTreeView和AndroidPdfViewer两个控件,通过组合形成了本文的“带书签的pdf”控件,通过点击书签,能够展开和收起书签,同时跳转到相应页面,也勉强够用了。自定义View简介常见的Android自定义View主要有两种类型:组合控件:通过组合现有的Android的基础控件,以达到复用的目的。比如试题控件(TextView+VideoGroup)和本文将要介绍的“带书签的PdfView”控件等,这种自定义View的难点在于程序的逻辑处理;完全自定义控件:继承自View、TextureView或SurfaceView,然后重写核心的回调方法,以View为例,按需复写其构造、onMeasure、onLayout、onTouchEvent、onDraw、onAttachedToWindow、onDetachedFromWindow等方法,这种自定义View的难点在于程序的设计、效率优化和排版。MyPdfViewer实战本文的开发环境为AndroidStudio2.2.2。先上一张效果图,如下:1、新建项目:     新建一个Android Phone and Tablet项目,命名为PDFExample。然后添加一个Android Library类库(Module),在这个库里实现我们的带书签的PdfViewer控件。2、添加依赖     在library类库的build.gradle中添加对AndroidTreeView和AndroidPdfViewer的依赖:     compile 'com.github.barteksc:android-pdf-viewer:2.1.0'     compile 'com.github.bmelnychuk:atv:1.2.+'3、添加属性     自定义带书签的控件的属性包括是否显示书签、书签宽度权重和内容宽度权重三个属性。如下:<resources>    <declare-styleable name="MyPDFView">        <attr name="bookmark_visiable" format="boolean"/>        <attr name="bookmark_weight" format="dimension"/>        <attr name="content_weight" format="dimension"/>    </declare-styleable></resources>4、书签节点Holder的实现     通过AndroidTreeView控件作者的介绍可知,自定义书签节点时需要继承TreeNode.BaseNodeViewHolder类,并重写createNodeView()方法。我们的书签节点由TextView和两个ImageView三部分构成,TextView展示书签的内容,第一个ImageView指示书签是展开还是收起。     展示书签内容的代码在loadComplete()方法中,该方法调用getTree(),通过递归展示所有的书签。java详细代码如下:
package com.***.library;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.widget.ImageView;import android.widget.TextView;import com.github.barteksc.pdfviewer.PDFView;import com.unnamed.b.atv.model.TreeNode;public class BookMarkTreeItemHolder extends TreeNode.BaseNodeViewHolder<BookMarkTreeItemHolder.IconTreeItem> {    PRivate TextView tvValue;    private ImageView arrowView;    public BookMarkTreeItemHolder(Context context){        super(context);    }    @Override    public View createNodeView(final TreeNode node, final IconTreeItem value) {        final LayoutInflater inflater = LayoutInflater.from(context);        final View view = inflater.inflate(R.layout.layout_bookmark_node, null, false);        tvValue = (TextView) view.findViewById(R.id.node_value);        tvValue.setText(value.text);        final ImageView iconView = (ImageView) view.findViewById(R.id.icon);        iconView.setImageResource(R.drawable.bookmark);        arrowView = (ImageView) view.findViewById(R.id.arrow_icon);        if(value.isLeaf){            arrowView.setVisibility(View.INVISIBLE);        }        return view;    }    @Override    public void toggle(boolean active) {        if(mNode.isLeaf()){//如果是叶节点,隐藏展开或收起图片            arrowView.setVisibility(View.INVISIBLE);        } else {            arrowView.setVisibility(View.VISIBLE);            if(active){                arrowView.setRotation(90);            } else {                arrowView.setRotation(0);            }        }    }    @Override    public int getContainerStyle() {        return R.style.TreeNodeStyleCustom;    }    /**    * 书签节点类    */    public static class IconTreeItem {        public boolean isLeaf;//是否是叶节点        public long pageIndex; //该节点所在页面        public String text;//节点内容        public IconTreeItem(boolean isLeaf, long pageIndex, String text) {            this.isLeaf = isLeaf;            this.pageIndex = pageIndex;            this.text = text;        }    }}     书签节点布局代码如下:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:minHeight="40dp">    <ImageView        android:id="@+id/arrow_icon"        android:layout_width="23px"        android:layout_height="23px"        android:layout_alignParentLeft="true"        android:layout_centerHorizontal="true"        android:layout_centerVertical="true"        android:layout_marginLeft="10px"        android:src="@drawable/right_arrow" />    <ImageView        android:id="@+id/icon"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignWithParentIfMissing="true"        android:layout_centerHorizontal="true"        android:layout_centerVertical="true"        android:layout_marginLeft="10px"        android:layout_toRightOf="@id/arrow_icon"        android:src="@drawable/bookmark" />    <TextView        android:id="@+id/node_value"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_centerVertical="true"        android:layout_marginLeft="10dp"        android:layout_toRightOf="@+id/icon"        android:textSize="16sp" /></RelativeLayout>5、自定义MyPDFViewer     自定义MyPDFViewer,继承自LinearLayout,并实现com.github.barteksc.pdfviewer.listener.OnLoadCompleteListener。代码如下:
package com.***.library;import android.content.Context;import android.content.res.TypedArray;import android.util.AttributeSet;import android.util.Log;import android.widget.LinearLayout;import com.github.barteksc.pdfviewer.PDFView;import com.github.barteksc.pdfviewer.listener.OnLoadCompleteListener;import com.shockwave.pdfium.PdfDocument;import com.unnamed.b.atv.model.TreeNode;import com.unnamed.b.atv.view.AndroidTreeView;import java.io.File;import java.util.List;/*** Created by JKWANG-PC on 2017/1/4.*/public class MyPDFViewer extends LinearLayout implements OnLoadCompleteListener {    private LinearLayout bookMarkContainer;//书签布局    private PDFView pdfView;    private List<PdfDocument.Bookmark> bookMarks;    private AndroidTreeView treeView;    private boolean bookMarkVisiable = true;    private int bookMarkWeight = 1;//书签的宽度,为权重值    private int contentWeight = 3;//pdf文档的宽度,为权重值    private String pdfPath;    private String passWord;    public MyPDFViewer(Context context) {        this(context, null);    }    public MyPDFViewer(Context context, AttributeSet attrs) {        super(context, attrs);        setOrientation(HORIZONTAL);        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyPDFViewer);        bookMarkVisiable = typedArray.getBoolean(R.styleable.MyPDFViewer_bookmark_visiable, true);        bookMarkWeight = typedArray.getInteger(R.styleable.MyPDFViewer_bookmark_weight, 1);        contentWeight = typedArray.getInteger(R.styleable.MyPDFViewer_content_weight, 3);        bookMarkContainer = new LinearLayout(getContext());        bookMarkContainer.setOrientation(VERTICAL);        pdfView = new PDFView(getContext(), null);        //在代码中编写布局        addView(bookMarkContainer);        addView(pdfView);    }    public void load(){        if(pdfPath.isEmpty() || pdfPath == null){            return;        }        bookMarkContainer.setLayoutParams(new LayoutParams(0, LayoutParams.MATCH_PARENT, bookMarkWeight));        bookMarkContainer.setVisibility(bookMarkVisiable ? VISIBLE : GONE);        pdfView.setLayoutParams(new LayoutParams(0, LayoutParams.MATCH_PARENT, contentWeight));        pdfView.fromFile(new File(pdfPath))                .pages(null) // all pages are displayed by default                .enableSwipe(true)                .swipeHorizontal(false)                .enableDoubletap(true)                .defaultPage(0)                .enableAnnotationRendering(true)                .password(passWord)//pdf打开密码                .scrollHandle(null)                .onLoad(this)                .load();        pdfView.zoomTo(1.75f);        pdfView.setMaxZoom(1.75f);        pdfView.setMinZoom(1.75f);    }    public boolean isBookMarkVisiable() {        return bookMarkVisiable;    }    public MyPDFViewer setBookMarkVisiable(boolean bookMarkVisiable) {        this.bookMarkVisiable = bookMarkVisiable;        return this;    }    public int getBookMarkWeight() {        return bookMarkWeight;    }    public MyPDFViewer setBookMarkWeight(int bookMarkWeight) {        this.bookMarkWeight = bookMarkWeight;        return this;    }    public int getContentWeight() {        return contentWeight;    }    public MyPDFViewer setContentWeight(int contentWeight) {        this.contentWeight = contentWeight;        return this;    }    public String getPdfPath() {        return pdfPath;    }    public MyPDFViewer setPdfPath(String pdfPath) {        this.pdfPath = pdfPath;        return this;    }    public String getPassWord() {        return passWord;    }    public MyPDFViewer setPassWord(String passWord) {        this.passWord = passWord;        return this;    }    @Override    public void loadComplete(int nbPages) {        Log.e("tag", "load complete");        bookMarks = pdfView.getTableOfContents();        TreeNode root = TreeNode.root();        for (int i = 0, n = bookMarks.size(); i < n; ++i) {            TreeNode parent = new TreeNode(new BookMarkTreeItemHolder.IconTreeItem(bookMarks.get(i).getChildren().size() > 0 ? false : true, bookMarks.get(i).getPageIdx(), bookMarks.get(i).getTitle()))                    .setViewHolder(new BookMarkTreeItemHolder(getContext()));            getTree(bookMarks.get(i).getChildren(), parent);//递归获取所有书签            root.addChild(parent);        }        treeView = new AndroidTreeView(getContext(), root);        treeView.setDefaultAnimation(true);        treeView.setDefaultViewHolder(BookMarkTreeItemHolder.class);        treeView.setDefaultContainerStyle(R.style.TreeNodeStyleCustom);        treeView.setDefaultNodeClickListener(nodeClickListener);//绑定书签节点点击事件        bookMarkContainer.addView(treeView.getView());    }    private TreeNode.TreeNodeClickListener nodeClickListener = new TreeNode.TreeNodeClickListener() {        @Override        public void onClick(TreeNode node, Object value) {            BookMarkTreeItemHolder.IconTreeItem item = (BookMarkTreeItemHolder.IconTreeItem) value;            pdfView.jumpTo((int) item.pageIndex);//点击书签节点时,跳转到指定页面        }    };    //递归获取所有书签节点    public void getTree(List<PdfDocument.Bookmark> bookmarks, TreeNode parent) {        for (int i = 0, n = bookmarks.size(); i < n; ++i) {            TreeNode child = new TreeNode(new BookMarkTreeItemHolder.IconTreeItem(bookmarks.get(i).hasChildren() ? false : true, bookmarks.get(i).getPageIdx(), bookmarks.get(i).getTitle()))                    .setViewHolder(new BookMarkTreeItemHolder(getContext()));            if (bookmarks.get(i).hasChildren()) {                getTree(bookmarks.get(i).getChildren(), child);            }            parent.addChild(child);        }    }}6、MyPDFViewer应用     在app的MainActivity调用自定义的MyPDFViewe控件,代码如下:
public class MainActivity extends AppCompatActivity {    private MyPDFViewer pdfView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        pdfView=(MyPDFViewer)findViewById(R.id.pdfView);        pdfView.setPdfPath(Environment.getExternalStorageDirectory().getPath() + "/Download/疯狂Android讲义 第3版.PDF")                //.setPassWord("1234")                .setBookMarkWeight(2)                .setContentWeight(5)                .load();        Log.e("load:","load complete");    }}至此,关于自定义MyPDFViewer组合控件就完成了,非常简单,但是比较实用。参考资料:1、https://zhuanlan.zhihu.com/p/219956332、AndroidTreeView: https://github.com/bmelnychuk/AndroidTreeView3、AndroidPdfViewer: https://github.com/barteksc/AndroidPdfViewer
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表