
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
新闻热点
疑难解答