include 'armeabi-v7a' //如果想包含其他cpu平台使用的so,修改这里即可 } } }
重新编译,生成的安装包,仅剩5M左右了。
注意:如果项目中还有其他so库,要根据项目实际需求,认真思考如何取舍了。
6、实现PDF阅读功能
很简单,因为android-pdf-viewer源码中已经实现了该功能,我们写一份精简版的吧。
6.1、功能点:
1、可加载assets中的pdf文件 2、可加载uri类型的pdf文件(如果是线上的pdf文件,可通过网络库先下载到本地,取其uri,本次Demo就不写网络下载了) 3、pdf的基本展示功能(使用android-pdf-viewer的控件实现:PDFView) 4、可跳转至目录页面(目录数据可通过intent直接传递过去) 5、可跳转至预览页面(pdf文件信息可通过intent直接传递过去) 6、根据目录页面、预览页面带回的页码,跳转至指定的pdf页面
6.2、代码实现
重点内容:
1、PDFView控件的使用;(比较简单,详见代码) 2、如何从PDF文件中获得目录信息;(如何获得目录信息、什么时机获取,详见代码)
PDF阅读页面的代码:PDFActivity
/**
- UI页面:PDF阅读
-
- 主要功能:
- 1、接收传递过来的pdf文件(包括assets中的文件名、文件uri)
- 2、显示PDF文件
- 3、接收目录页面、预览页面返回的PDF页码,跳转到指定的页面
-
- 作者:齐行超
- 日期:2019.08.07 */ public class PDFActivity extends AppCompatActivity implements OnPageChangeListener, OnLoadCompleteListener, OnPageErrorListener { //PDF控件 PDFView pdfView; //按钮控件:返回、目录、缩略图 Button btn_back, btn_catalogue, btn_preview; //页码 Integer pageNumber = 0; //PDF目录集合 List catelogues;
//pdf文件名(限:assets里的文件) String assetsFileName; //pdf文件uri Uri uri;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); UIUtils.initWindowStyle(getWindow(), getSupportActionBar());//设置沉浸式 setContentView(R.layout.activity_pdf);
initView();//初始化view setEvent();//设置事件 loadPdf();//加载PDF文件 }
/**
- 初始化view */ private void initView() { pdfView = findViewById(R.id.pdfView); btn_back = findViewById(R.id.btn_back); btn_catalogue = findViewById(R.id.btn_catalogue); btn_preview = findViewById(R.id.btn_preview); }
/**
- 设置事件 */ private void setEvent() { //返回 btn_back.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { PDFActivity.this.finish(); } }); //跳转目录页面 btn_catalogue.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(PDFActivity.this, PDFCatelogueActivity.class); intent.putExtra("catelogues", (Serializable) catelogues); PDFActivity.this.startActivityForResult(intent, 200); } }); //跳转缩略图页面 btn_preview.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(PDFActivity.this, PDFPreviewActivity.class); intent.putExtra("AssetsPdf", assetsFileName); intent.setData(uri); PDFActivity.this.startActivityForResult(intent, 201); } }); }
/**
- 加载PDF文件 */ private void loadPdf() { Intent intent = getIntent(); if (intent != null) { assetsFileName = intent.getStringExtra("AssetsPdf"); if (assetsFileName != null) { displayFromAssets(assetsFileName); } else { uri = intent.getData(); if (uri != null) { displayFromUri(uri); } } } }
/**
- 基于assets显示 PDF 文件
- @param fileName 文件名称 */ private void displayFromAssets(String fileName) { pdfView.fromAsset(fileName) .defaultPage(pageNumber) .onPageChange(this) .enableAnnotationRendering(true) .onLoad(this) .scrollHandle(new DefaultScrollHandle(this)) .spacing(10) // 单位 dp .onPageError(this) .pageFitPolicy(FitPolicy.BOTH) .load(); }
/**
- 基于uri显示 PDF 文件
- @param uri 文件路径 */ private void displayFromUri(Uri uri) { pdfView.fromUri(uri) .defaultPage(pageNumber) .onPageChange(this) .enableAnnotationRendering(true) .onLoad(this) .scrollHandle(new DefaultScrollHandle(this)) .spacing(10) // 单位 dp .onPageError(this) .load(); }
/**
- 当成功加载PDF:
- 1、可获取PDF的目录信息
- @param nbPages the number of pages in this PDF file */ @Override public void loadComplete(int nbPages) { //获得文档书签信息 List<PdfDocument.Bookmark> bookmarks = pdfView.getTableOfContents(); if (catelogues != null) { catelogues.clear(); } else { catelogues = new ArrayList<>(); } //将bookmark转为目录数据集合 bookmarkToCatelogues(catelogues, bookmarks, 1); }
/**
- 将bookmark转为目录数据集合(递归)
- @param catelogues 目录数据集合
- @param bookmarks 书签数据
- @param level 目录树级别(用于控制树节点位置偏移) */ private void bookmarkToCatelogues(List catelogues, List<PdfDocument.Bookmark> bookmarks, int level) { for (PdfDocument.Bookmark bookmark : bookmarks) { TreeNodeData nodeData = new TreeNodeData(); nodeData.setName(bookmark.getTitle()); nodeData.setPageNum((int) bookmark.getPageIdx()); nodeData.setTreeLevel(level); nodeData.setExpanded(false); catelogues.add(nodeData); if (bookmark.getChildren() != null && bookmark.getChildren().size() > 0) { List treeNodeDatas = new ArrayList<>(); nodeData.setSubset(treeNodeDatas); bookmarkToCatelogues(treeNodeDatas, bookmark.getChildren(), level + 1); } } }
@Override public void onPageChanged(int page, int pageCount) { pageNumber = page; }
@Override public void onPageError(int page, Throwable t) { }
/**
- 从缩略图、目录页面带回页码,跳转到指定PDF页面
- @param requestCode
- @param resultCode
- @param data */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { int pageNum = data.getIntExtra("pageNum", 0); if (pageNum > 0) { pdfView.jumpTo(pageNum); } } }
@Override protected void onDestroy() { super.onDestroy(); //是否内存 if (pdfView != null) { pdfView.recycle(); } } }
PDF阅读页面的布局文件:activity_pdf.xml
<com.github.barteksc.pdfviewer.PDFView android:id="@+id/pdfView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/rl_top"/>
7、PDF目录树的实现
目录树的数据(目录名称、页码...),已在上个页面获取了,所以此页面只需考虑目录树控件的实现。
注意:之所以没在这个页面单独获取目录树的数据,主要考虑到android-pdfview、pdfium内存占用太大了,不想再次创建Pdf的相关对象。
7.1、PDF目录树效果图
7.2、树形控件如何实现?
安卓默认没有树形控件,不过我们可以使用RecyclerView或ListView实现。 如上图所示:
列表每一行为一条目录数据,主要包括:名称、页码; 如果有子目录,则出现箭头图片,该项可折叠、展开,箭头方向随之改变; 子目录的名称文本随目录树级别递增向右偏移;
当前Demo实现方式为RecyclerView,应该如何实现上面的效果? 可在adapter中处理页面效果、事件效果: 1、列表项内容展示
1、使用垂直线性布局管理器; 2、每个item包含:箭头图片(如果有子目录,则显示)、命令名称文本、页码文本;
2、折叠效果
1、控制adapter数据集合的内容即可,如果某节点折叠了,就把对应的子目录数据删除即可, 反之,加上,再notifyDataSetChanged通知数据源改变; 2、除此之外,还需有一个状态来标记当前节点是展开还是折叠,用于控制箭头图片方向的显示;
3、目录文本向右偏移效果
可通过目录树层级 * 固定左侧间隔(如: 20dp),然后为目录的textview控件设置偏移即可;
目录树层级树如何获取? 可选方案: 1、递归集合自动获取(需要遍历,效率低一点,如果是可编辑的目录结构,建议选择) 2、创建数据的时候,直接写死(因当前demo的PDF目录结构不会被编辑,所以直接选择这个方案吧)
7.3、代码实现:
树形控件的数据对象TreeNodeData:
/**
- 树形控件数据类(会用于页面间传输,所以需实现Serializable 或 Parcelable)
- 作者:齐行超
- 日期:2019.08.07 */ public class TreeNodeData implements Serializable { //名称 private String name; //页码 private int pageNum; //是否已展开(用于控制树形节点图片显示,即箭头朝向图片) private boolean isExpanded; //展示级别(1级、2级...,用于控制树形节点缩进位置) private int treeLevel; //子集(用于加载子节点,也用于判断是否显示箭头图片,如集合不为空,则显示) private List subset;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getPageNum() { return pageNum; }
public void setPageNum(int pageNum) { this.pageNum = pageNum; }
public boolean isExpanded() { return isExpanded; }
public void setExpanded(boolean expanded) { isExpanded = expanded; }
public int getTreeLevel() { return treeLevel; }
public void setTreeLevel(int treeLevel) { this.treeLevel = treeLevel; }
public List getSubset() { return subset; }
public void setSubset(List subset) { this.subset = subset; } }
树形控件适配器 : TreeAdapter
/**
- 树形控件适配器
- 作者:齐行超
- 日期:2019.08.07 */ public class TreeAdapter extends RecyclerView.Adapter<TreeAdapter.TreeNodeViewHolder> { //上下文 private Context context; //数据 public List data; //展示数据(由层级结构改为平面结构) public List displayData; //treelevel间隔(dp) private int maginLeft; //委托对象 private TreeEvent delegate;
/**
- 构造函数
- @param context 上下文
- @param data 数据 */ public TreeAdapter(Context context, List data) { this.context = context; this.data = data; maginLeft = UIUtils.dip2px(context, 20); displayData = new ArrayList<>();
//数据转为展示数据 dataToDiaplayData(data); }
/**
- 数据转为展示数据
- @param data 数据 */ private void dataToDiaplayData(List data) { for (TreeNodeData nodeData : data) { displayData.add(nodeData); if (nodeData.isExpanded() && nodeData.getSubset() != null) { dataToDiaplayData(nodeData.getSubset()); } } }
/**
- 数据集合转为可显示的集合 */ private void reDataToDiaplayData() { if (this.data == null || this.data.size() == 0) { return; } if(displayData == null){ displayData = new ArrayList<>(); }else{ displayData.clear(); } dataToDiaplayData(this.data); notifyDataSetChanged(); }
@Override public TreeNodeViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(context).inflate(R.layout.tree_item, null); return new TreeNodeViewHolder(view); }
@Override public void onBindViewHolder(TreeNodeViewHolder holder, int position) { final TreeNodeData data = displayData.get(position); //设置图片 if (data.getSubset() != null) { holder.img.setVisibility(View.VISIBLE); if (data.isExpanded()) { holder.img.setImageResource(R.drawable.arrow_h); } else { holder.img.setImageResource(R.drawable.arrow_v); } } else { holder.img.setVisibility(View.INVISIBLE); } //设置图片偏移位置 RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) holder.img.getLayoutParams(); int ratio = data.getTreeLevel() <= 0? 0 : data.getTreeLevel()-1; params.setMargins(maginLeft * ratio, 0, 0, 0); holder.img.setLayoutParams(params);
//显示文本 holder.title.setText(data.getName()); holder.pageNum.setText(String.valueOf(data.getPageNum()));
//图片点击事件 holder.img.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //控制树节点展开、折叠 data.setExpanded(!data.isExpanded()); //刷新数据源 reDataToDiaplayData(); } }); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //回调结果 if(delegate!=null){ delegate.onSelectTreeNode(data); } } }); }
@Override public int getItemCount() { return displayData.size(); }
/**
- 定义RecyclerView的ViewHolder对象 */ class TreeNodeViewHolder extends RecyclerView.ViewHolder { ImageView img; TextView title; TextView pageNum;
public TreeNodeViewHolder(View view) { super(view); img = view.findViewById(R.id.iv_arrow); title = view.findViewById(R.id.tv_title); pageNum = view.findViewById(R.id.tv_pagenum); } }
/**
- 接口:Tree事件 / public interface TreeEvent{ /*
- 当选择了某tree节点
- @param data tree节点数据 */ void onSelectTreeNode(TreeNodeData data); }
/**
- 设置Tree的事件
- @param treeEvent Tree的事件对象 */ public void setTreeEvent(TreeEvent treeEvent){ this.delegate = treeEvent; } }
PDF目录树页面:PDFCatelogueActivity
/**
- UI页面:PDF目录
-
- 1、用于显示Pdf目录信息
- 2、点击tree item,带回Pdf页码到前一个页面
-
- 作者:齐行超
- 日期:2019.08.07 */ public class PDFCatelogueActivity extends AppCompatActivity implements TreeAdapter.TreeEvent {
RecyclerView recyclerView; Button btn_back;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); UIUtils.initWindowStyle(getWindow(), getSupportActionBar()); setContentView(R.layout.activity_catelogue);
initView();//初始化控件 setEvent();//设置事件 loadData();//加载数据 }
/**
- 初始化控件 */ private void initView() { btn_back = findViewById(R.id.btn_back); recyclerView = findViewById(R.id.rv_tree); }
/**
- 设置事件 */ private void setEvent() { btn_back.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { PDFCatelogueActivity.this.finish(); } }); }
/**
- 加载数据 */ private void loadData() { //从intent中获得传递的数据 Intent intent = getIntent(); List catelogues = (List) intent.getSerializableExtra("catelogues");
//使用RecyclerView加载数据 LinearLayoutManager llm = new LinearLayoutManager(this); llm.setOrientation(LinearLayoutManager.VERTICAL); recyclerView.setLayoutManager(llm); TreeAdapter adapter = new TreeAdapter(this, catelogues); adapter.setTreeEvent(this); recyclerView.setAdapter(adapter); }
/**
- 点击tree item,带回Pdf页码到前一个页面
- @param data tree节点数据 */ @Override public void onSelectTreeNode(TreeNodeData data) { Intent intent = new Intent(); intent.putExtra("pageNum", data.getPageNum()); setResult(Activity.RESULT_OK, intent); finish(); } }
PDF目录树的布局文件:activity_catelogue.xml
<android.support.v7.widget.RecyclerView android:id="@+id/rv_tree" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/rl_top" />
8、PDF预览缩略图
这个功能算是本Demo中最为复杂的一个了:
如何将PDF某页面的内容转成图片?(默认是无法从pdfview中获得页面图片的) 如何减少图片内存的占用?(用户可能快速滑动列表,实时读取、显示多张图片) 如何优化PDF预览缩略图列表的滑动体验?(图片的获取需要一定时间) 如何合理的及时释放内存占用?
8.1、PDF预览缩略图列表的效果图
8.2、功能分析
1、如何将PDF某页面的内容转成图片?
查看android-pdfview的源码,无法通过PDFView控件获得某页面的图片,所以只能分析pdfium sdk的API了,如下图:
pdfium的renderPageBitmap方法可以将页面渲染成图片,不过需要传递一系列参数,而且要小心OutOfMemoryError。
那么,我们需要在代码中获取或者创建PdfiumCore对象,调用该方法,传递PdfDocument等参数,当bitmap使用完后,应及时释放掉。
2、如何减少内存的占用?
内存主要包括: 1、pdfium sdk加载pdf文件产生的内存(我们无法优化) 2、android-pdfview产生的内存(如果有需要,可改其源码) 3、我们将pdf页面转为缩略图,而产生的内存(必须优化,否则,容易oom)
3.1、当PdfiumCore、PdfDocument不再使用时,应及时关闭; 3.2、当缩略图不再使用时,应及时释放; 3.3、可使用LruCache临时缓存缩略图,防止重复调用renderPageBitmap获取图片; 3.4、LruCache应合理管控,当预览页面关闭时,必须清空缓存,以释放内存; 3.5、创建图片时,应使用RGB_565,能节约内存开销(一个像素点,占2字节) 3.6、创建图片时,应尽可能小的指定图片的宽高,能看清就行(图片占用的内存 = 宽 * 高 * 一个像素点占的字节数)
3、如何优化PDF预览缩略图列表的滑动体验?
查看pdfium源码,调用renderPageBitmap方法之前,还必须确保对应的页面已被打开,即调用了openPage方法。然而,这两个方法都需要一定时间才能执行完成的。
那么,如果我们直接在主线程中让每个RecylerVew的item分别调用renderPageBitmap方法,滑动列表时,会感觉特别卡,所以该方法只能放在子线程中调用了。
那么问题又来了,那么多子线程应该如何管控?
1、考虑CPU的占用,应使用线程池控制子线程并发、阻塞; 2、考虑到用户滑动速度,有可能某线程正执行或者阻塞着呢,页面已经滑过去了,那么,即使该线程加载出来了图片,也无法显示到列表中。所以对于RecyclerView已不可见的Item项对应的线程,应及时取消,防止做无用功,也节省了内存和cpu开销。
8.3、功能实现
预览缩略图工具类:PreviewUtils
/**
- 预览缩略图工具类
- 1、pdf页面转为缩略图
- 2、图片缓存管理(仅保存到内存,可使用LruCache,注意空间大小控制)
- 3、多线程管理(线程并发、阻塞、Future任务取消)
- 作者:齐行超
- 日期:2019.08.08 */ public class PreviewUtils { //图片缓存管理 private ImageCache imageCache; //单例 private static PreviewUtils instance; //线程池 ExecutorService executorService; //线程任务集合(可用于取消任务) HashMap<String, Future> tasks;
/**
- 单例(仅主线程调用,无需做成线程安全的)
- @return PreviewUtils实例对象 */ public static PreviewUtils getInstance() { if (instance == null) { instance = new PreviewUtils(); } return instance; }
/**
- 默认构造函数 */ private PreviewUtils() { //初始化图片缓存管理对象 imageCache = new ImageCache(); //创建并发线程池(建议最大并发数大于1屏grid item的数量) executorService = Executors.newFixedThreadPool(20); //创建线程任务集合,用于取消线程执行 tasks = new HashMap<>(); }
/**
- 从pdf文件中加载图片
- @param context 上下文
- @param imageView 图片控件
- @param pdfiumCore pdf核心对象
- @param pdfDocument pdf文档对象
- @param pdfName pdf文件名称
- @param pageNum pdf页码 */ public void loadBitmapFromPdf(final Context context, final ImageView imageView, final PdfiumCore pdfiumCore, final PdfDocument pdfDocument, final String pdfName, final int pageNum) { //判断参数合法性 if (imageView == null || pdfiumCore == null || pdfDocument == null || pageNum < 0) { return; }
try { //缓存key final String keyPage = pdfName + pageNum;
//为图片控件设置标记 imageView.setTag(keyPage);
Log.i("PreViewUtils", "加载pdf缩略图:" + keyPage);
//获得imageview的尺寸(注意:如果使用正常控件尺寸,太占内存了) /int w = imageView.getMeasuredWidth(); int h = imageView.getMeasuredHeight(); final int reqWidth = w == 0 ? UIUtils.dip2px(context,100) : w; final int reqHeight = h == 0 ? UIUtils.dip2px(context,150) : h;/
//内存大小= 图片宽度 * 图片高度 * 一个像素占的字节数(RGB_565 所占字节:2) //注意:如果使用正常控件尺寸,太占内存了,所以此处指定四缩略图看着会模糊一点 final int reqWidth = 100; final int reqHeight = 150;
//从缓存中取图片 Bitmap bitmap = imageCache.getBitmapFromLruCache(keyPage); if (bitmap != null) { imageView.setImageBitmap(bitmap); return; }
//使用线程池管理子线程 Future future = executorService.submit(new Runnable() { @Override public void run() { //打开页面(调用renderPageBitmap方法之前,必须确保页面已open,重要) pdfiumCore.openPage(pdfDocument, pageNum);
//调用native方法,将Pdf页面渲染成图片 final Bitmap bm = Bitmap.createBitmap(reqWidth, reqHeight, Bitmap.Config.RGB_565); pdfiumCore.renderPageBitmap(pdfDocument, bm, pageNum, 0, 0, reqWidth, reqHeight);
//切回主线程,设置图片 if (bm != null) { //将图片加入缓存 imageCache.addBitmapToLruCache(keyPage, bm);
//切回主线程加载图片 new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { if (imageView.getTag().toString().equals(keyPage)) { imageView.setImageBitmap(bm); Log.i("PreViewUtils", "加载pdf缩略图:" + keyPage + "......已设置!!"); } } }); } } });
//将任务添加到集合 tasks.put(keyPage, future); } catch (Exception ex) { ex.printStackTrace(); } }
/**
- 取消从pdf文件中加载图片的任务
- @param keyPage 页码 */ public void cancelLoadBitmapFromPdf(String keyPage) { if (keyPage == null || !tasks.containsKey(keyPage)) { return; } try { Log.i("PreViewUtils", "取消加载pdf缩略图:" + keyPage); Future future = tasks.get(keyPage); if (future != null) { future.cancel(true); Log.i("PreViewUtils", "取消加载pdf缩略图:" + keyPage + "......已取消!!"); } } catch (Exception ex) { ex.printStackTrace(); } }
/**
- 获得图片缓存对象
- @return 图片缓存 */ public ImageCache getImageCache(){ return imageCache; }
/**
- 图片缓存管理 */ public class ImageCache { //图片缓存 private LruCache<String, Bitmap> lruCache;
//构造函数 public ImageCache() { //初始化 lruCache //int maxMemory = (int) Runtime.getRuntime().maxMemory(); //int cacheSize = maxMemory/8; int cacheSize = 1024 * 1024 * 30;//暂时设定30M lruCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight(); } }; }
/**
- 从缓存中取图片
- @param key 键
- @return 图片 */ public synchronized Bitmap getBitmapFromLruCache(String key) { if(lruCache!= null) { return lruCache.get(key); } return null; }
/**
- 向缓存中加图片
- @param key 键
- @param bitmap 图片 */ public synchronized void addBitmapToLruCache(String key, Bitmap bitmap) { if (getBitmapFromLruCache(key) == null) { if (lruCache!= null && bitmap != null) lruCache.put(key, bitmap); } }
/**
- 清空缓存 */ public void clearCache(){ if(lruCache!= null){ lruCache.evictAll(); } } } }
grid列表适配器: GridAdapter
/**
- grid列表适配器
- 作者:齐行超
- 日期:2019.08.08 */ public class GridAdapter extends RecyclerView.Adapter<GridAdapter.GridViewHolder> {
Context context; PdfiumCore pdfiumCore; PdfDocument pdfDocument; String pdfName; int totalPageNum;
public GridAdapter(Context context, PdfiumCore pdfiumCore, PdfDocument pdfDocument, String pdfName, int totalPageNum) { this.context = context; this.pdfiumCore = pdfiumCore; this.pdfDocument = pdfDocument; this.pdfName = pdfName; this.totalPageNum = totalPageNum; }
@Override public GridViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(context).inflate(R.layout.grid_item, null); return new GridViewHolder(view); }
@Override public void onBindViewHolder(GridViewHolder holder, int position) { //设置PDF图片 final int pageNum = position; PreviewUtils.getInstance().loadBitmapFromPdf(context, holder.iv_page, pdfiumCore, pdfDocument, pdfName, pageNum); //设置PDF页码 holder.tv_pagenum.setText(String.valueOf(position)); //设置Grid事件 holder.iv_page.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(delegate!=null){ delegate.onGridItemClick(pageNum); } } }); return; }
@Override public void onViewDetachedFromWindow(GridViewHolder holder) { super.onViewDetachedFromWindow(holder); try { //item不可见时,取消任务 if(holder.iv_page!=null){ PreviewUtils.getInstance().cancelLoadBitmapFromPdf(holder.iv_page.getTag().toString()); }
//item不可见时,释放bitmap (注意:本Demo使用了LruCache缓存来管理图片,此处可注释掉) /Drawable drawable = holder.iv_page.getDrawable(); if (drawable != null) { Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); if (bitmap != null && !bitmap.isRecycled()) { bitmap.recycle(); bitmap = null; Log.i("PreViewUtils","销毁pdf缩略图:"+holder.iv_page.getTag().toString()); } }/ }catch (Exception ex){ ex.printStackTrace(); } }
@Override public int getItemCount() { return totalPageNum; }
class GridViewHolder extends RecyclerView.ViewHolder { ImageView iv_page; TextView tv_pagenum;
public GridViewHolder(View itemView) { super(itemView); iv_page = itemView.findViewById(R.id.iv_page); tv_pagenum = itemView.findViewById(R.id.tv_pagenum); } }
/**
- 接口:Grid事件 / public interface GridEvent{ /*
- 当选择了某Grid项
- @param position tree节点数据 */ void onGridItemClick(int position); }
/**
- 设置Grid事件
- @param event Grid事件对象 */ public void setGridEvent(GridEvent event){ this.delegate = event; }
//Grid事件委托 private GridEvent delegate; }
PDF预览缩略图页面:PDFPreviewActivity
/**
- UI页面:PDF预览缩略图(注意:此页面,需多关注内存管控)
-
- 1、用于显示Pdf缩略图信息
- 2、点击缩略图,带回Pdf页码到前一个页面
-
- 作者:齐行超
- 日期:2019.08.07 */ public class PDFPreviewActivity extends AppCompatActivity implements GridAdapter.GridEvent {
RecyclerView recyclerView; Button btn_back; PdfiumCore pdfiumCore; PdfDocument pdfDocument; String assetsFileName;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); UIUtils.initWindowStyle(getWindow(), getSupportActionBar()); setContentView(R.layout.activity_preview);
initView();//初始化控件 setEvent(); loadData(); }
/**
- 初始化控件 */