Andorid实现pdf批注、笔记demo
1、兴建andorid项目
2、导入pdf包
implementation ‘com.lonelypluto:pdf-viewer:1.0.5’
1、创建页面和java类
1.1创建OutlineAdapter
package com.example.myapplication.adapter;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.artifex.mupdfdemo.OutlineItem;
import com.example.myapplication.R;
/**
* @Description: 文件目录adapter
* @author: ZhangYW
* @time: 2019/1/22 11:17
*/
public class OutlineAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext;
private OutlineItem mList[];
public OutlineAdapter(Context context, OutlineItem mList[]){
this.mContext = context;
this.mList = mList;
}
/**
* 渲染具体的ViewHolder
* @param parent ViewHolder的容器
* @param viewType 一个标志,我们根据该标志可以实现渲染不同类型的ViewHolder
* @return
*/
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(mContext).inflate(R.layout.outline_item,parent,false);
return new OutlineViewHolder(itemView);
}
/**
* 绑定ViewHolder的数据。
* @param holder
* @param position 数据源list的下标
*/
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
OutlineItem bean = mList[position];
if (null == bean)
return;
OutlineViewHolder viewHolder = (OutlineViewHolder) holder;
int level = bean.level;
if (level > 8) level = 8;
String space = "";
for (int i=0; i<level;i++)
space += " ";
String text = space + bean.title;
viewHolder.tv_title.setText(text);
viewHolder.tv_page.setText(String.valueOf(bean.page+1));
}
@Override
public int getItemCount() {
return mList.length;
}
public static class OutlineViewHolder extends RecyclerView.ViewHolder{
public TextView tv_title;
public TextView tv_page;
public OutlineViewHolder(View itemView){
super(itemView);
tv_title = (TextView) itemView.findViewById(R.id.outline_item_title);
tv_page = (TextView) itemView.findViewById(R.id.outline_item_page);
}
}
}
1.2创建OutlineAdapter的outline_item.xml页面
<?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="wrap_content"
android:clickable="true"
android:focusable="true"
android:background="?selectableItemBackground"
>
<TextView
android:id="@+id/outline_item_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_alignParentStart="true"
android:layout_toStartOf="@+id/outline_item_page"
android:maxLines="1"
android:layout_centerVertical="true"
android:paddingStart="8dp"
android:text=""
android:textColor="@color/rv_item_line_tv"
android:textSize="18sp"
/>
<TextView
android:id="@+id/outline_item_page"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/outline_item_title"
android:layout_alignBottom="@+id/outline_item_title"
android:layout_alignParentEnd="true"
android:paddingEnd="8dp"
android:text=""
android:textColor="@color/rv_item_line_tv"
android:textSize="18sp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@+id/outline_item_title"
android:layout_marginTop="10dp"
android:background="@color/rv_item_line_bg"
/>
</RelativeLayout>
1.3创建OnRecyclerItemClickListener
package com.example.myapplication.widget;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
/**
* RecyclerView监听事件
*/
public abstract class OnRecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
private GestureDetectorCompat mGestureDetector;
private RecyclerView recyclerView;
public OnRecyclerItemClickListener(RecyclerView recyclerView) {
this.recyclerView = recyclerView;
mGestureDetector = new GestureDetectorCompat(recyclerView.getContext(), new ItemTouchHelperGestureListener());
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
mGestureDetector.onTouchEvent(e);
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
mGestureDetector.onTouchEvent(e);
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child != null) {
RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);
onItemClick(vh);
}
return true;
}
@Override
public void onLongPress(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child != null) {
RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);
onItemLongClick(vh);
}
}
}
public abstract void onItemClick(RecyclerView.ViewHolder vh);
public abstract void onItemLongClick(RecyclerView.ViewHolder vh);
}
1.4创建OutlineActivity
package com.example.myapplication.activity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import com.artifex.mupdfdemo.OutlineActivityData;
import com.artifex.mupdfdemo.OutlineItem;
import com.example.myapplication.R;
import com.example.myapplication.adapter.OutlineAdapter;
import com.example.myapplication.widget.OnRecyclerItemClickListener;
/**
* @Description: 目录
* @author: ZhangYW
* @time: 2019/3/8 10:29
*/
public class OutlineActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private OutlineAdapter adapter;
private OutlineItem mItems[];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_outline);
initData();
initView();
setListener();
}
/**
* 初始化数据
*/
private void initData(){
mItems = OutlineActivityData.get().items;
}
/**
* 初始化布局
*/
private void initView(){
adapter = new OutlineAdapter(this,mItems);
recyclerView = (RecyclerView)findViewById(R.id.outline_rv);
recyclerView.setHasFixedSize(true);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);//HORIZONTAL 水平
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
}
/**
* 设置点击事件
*/
private void setListener(){
recyclerView.addOnItemTouchListener(new OnRecyclerItemClickListener(recyclerView) {
@Override
public void onItemClick(RecyclerView.ViewHolder vh) {
OutlineActivityData.get().position = vh.getLayoutPosition();
setResult(mItems[vh.getLayoutPosition()].page);
finish();
}
@Override
public void onItemLongClick(RecyclerView.ViewHolder vh) {
}
});
}
}
1.5创建OutlineActivity的activity_outline.xml页面
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.artifex.mupdfdemo.activity.OutlineActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/outline_rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
1.6创建AllPDFActivity
package com.example.myapplication.activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewAnimator;
import com.artifex.mupdfdemo.Annotation;
import com.artifex.mupdfdemo.Hit;
import com.artifex.mupdfdemo.MuPDFAlert;
import com.artifex.mupdfdemo.MuPDFCore;
import com.artifex.mupdfdemo.MuPDFPageAdapter;
import com.artifex.mupdfdemo.MuPDFReaderView;
import com.artifex.mupdfdemo.MuPDFReaderViewListener;
import com.artifex.mupdfdemo.MuPDFView;
import com.artifex.mupdfdemo.OutlineActivityData;
import com.artifex.mupdfdemo.OutlineItem;
import com.artifex.mupdfdemo.ReaderView;
import com.artifex.mupdfdemo.SavePdf;
import com.artifex.mupdfdemo.SearchTask;
import com.artifex.mupdfdemo.SearchTaskResult;
import com.artifex.mupdfdemo.widget.VDHDeepLayout;
import com.example.myapplication.R;
import com.lonelypluto.pdflibrary.utils.SharedPreferencesUtil;
import java.util.concurrent.Executor;
public class AllPDFActivity extends AppCompatActivity {
private static final String TAG = AllPDFActivity.class.getSimpleName();
private final int OUTLINE_REQUEST = 0;
private String filePath = Environment.getExternalStorageDirectory() + "/pdf_t1.pdf"; // 文件路径
// private String filePath = Environment.getExternalStorageDirectory() + "/t"; // 文件路径
private String fileName;// 文件名
private AlertDialog.Builder mAlertBuilder;
static private AlertDialog.Builder gAlertBuilder;
private MuPDFCore muPDFCore;// 加载mupdf.so文件
private MuPDFReaderView muPDFReaderView;// 显示pdf的view
private boolean mAlertsActive = false;
private AsyncTask<Void, Void, MuPDFAlert> mAlertTask;
private AlertDialog mAlertDialog;
// tools
private ViewAnimator mTopBarSwitcher;// 工具栏动画
private ImageButton mLinkButton;// 超链接
private ImageButton mOutlineButton;// 目录
private ImageButton mSearchButton;// 搜索
private ImageButton mAnnotButton;// 注释
// tools 搜索框
private EditText mSearchText;// 搜索内容输入框
private ImageButton mSearchBack;// 搜索内容上一个
private ImageButton mSearchFwd;// 搜索内容下一个
// tools 注释类型
private TextView mAnnotTypeText;// 注释类型
// tools 底部布局
private TextView mPageNumberView;// 页数
private SeekBar mPageSlider;// 底部拖动条
private int mPageSliderRes;// 拖动条的个数
private boolean mButtonsVisible;// 是否显示工具栏
private TopBarMode mTopBarMode = TopBarMode.Main;// 工具栏类型
private AcceptMode mAcceptMode;// 工具栏注释类型
private SearchTask mSearchTask;// 搜索线程
private boolean mLinkHighlight = false;// 是否高亮显示
private Button btn_change_hv;// 切换横竖显示
private boolean ischangeHV = false;// 横竖切换
private Button btn_linkhighlightcolor;// 设置超链接颜色
private Button btn_searchtextcolor;// 设置搜索文字颜色
private Button btn_paintcolor;// 设置画笔颜色
private Button btn_paintstrokewidth;// 设置画笔粗细
private Button btn_sign;// 电子签章
private Button btn_save;// 保存
private VDHDeepLayout vdhDeepLayout;
private ImageView iv_sign;
private ImageView iv_test;
private SavePdfTask savePdfTask;
/*
* 用于存储的异步,并上传更新
* */
class SavePdfTask extends AsyncTask {
SavePdf savePdf;
public SavePdfTask(SavePdf savePdf) {
this.savePdf = savePdf;
}
@Override
protected Object doInBackground(Object[] params) {
savePdf.addText();
return null;
}
@Override
protected void onPostExecute(Object o) {
Log.e(TAG, "存储完成");
try {
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_all_pdf);
initView();
}
private void initView() {
SharedPreferencesUtil.init(getApplication());
muPDFReaderView = (MuPDFReaderView)findViewById(R.id.open_pdf_mupdfreaderview);
initToolsView();
mAlertBuilder = new AlertDialog.Builder(this);
// keep a static copy of this that other classes can use
gAlertBuilder = mAlertBuilder;
String path = filePath;
// 通过MuPDFCore打开pdf文件
muPDFCore = openFile(path);
// 搜索设为空
SearchTaskResult.set(null);
// 判断如果core为空,提示不能打开文件
if (muPDFCore == null) {
AlertDialog alert = mAlertBuilder.create();
alert.setTitle(com.lonelypluto.pdflibrary.R.string.cannot_open_document);
alert.setButton(AlertDialog.BUTTON_POSITIVE, getString(com.lonelypluto.pdflibrary.R.string.dismiss),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
alert.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
finish();
}
});
alert.show();
return;
}
createUI();
//切换横竖显示
btn_change_hv = (Button)findViewById(R.id.btn_change_hv);
btn_change_hv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (ischangeHV) {
muPDFReaderView.setHorizontalScrolling(ischangeHV);
btn_change_hv.setText("横");
ischangeHV = false;
} else {
muPDFReaderView.setHorizontalScrolling(ischangeHV);
btn_change_hv.setText("竖");
ischangeHV = true;
}
}
});
// 改变超链接颜色
btn_linkhighlightcolor = (Button)findViewById(R.id.btn_linkhighlightcolor);
btn_linkhighlightcolor.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setLinkHighlightColor(ContextCompat.getColor(AllPDFActivity.this, com.lonelypluto.pdflibrary.R.color.link_bg));
}
});
// 改变搜索文字颜色
btn_searchtextcolor = (Button)findViewById(R.id.btn_searchtextcolor);
btn_searchtextcolor.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setSearchTextColor(ContextCompat.getColor(AllPDFActivity.this, com.lonelypluto.pdflibrary.R.color.search_bg));
}
});
// 设置画笔颜色
btn_paintcolor = (Button)findViewById(R.id.btn_set_paint_color);
btn_paintcolor.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int c = ContextCompat.getColor(AllPDFActivity.this, com.lonelypluto.pdflibrary.R.color.rv_item_line_bg);
Log.e(TAG, "color = " + c);
setColor(c);
setInkColor(0xFF0000FF);
}
});
// 设置画笔粗细
btn_paintstrokewidth = (Button)findViewById(R.id.btn_set_paint_strokewidth);
btn_paintstrokewidth.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setPaintStrockWidth(20.0f);
}
});
// 电子签章
btn_sign = (Button)findViewById(R.id.btn_sign);
btn_sign.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG, "btn_sign");
vdhDeepLayout.setVisibility(View.VISIBLE);
}
});
// 保存
btn_save = (Button)findViewById(R.id.btn_save);
btn_save.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG, "btn_save");
String in_path = filePath;
String out_path = in_path.substring(0, in_path.length() - 4) + "_t2.pdf";
SavePdf savePdf = new SavePdf(in_path, out_path);
savePdf.setScale(muPDFReaderView.getCurrentScale());
savePdf.setPageNum(muPDFReaderView.getDisplayedViewIndex() + 1);
savePdf.setWidthScale(1.0f * muPDFReaderView.getScaleX() / muPDFReaderView.getDisplayedView().getWidth());//计算宽偏移的百分比
savePdf.setHeightScale(1.0f * muPDFReaderView.getScaleY() / muPDFReaderView.getDisplayedView().getHeight());//计算长偏移的百分比
Log.e(TAG, "scaleX = " + muPDFReaderView.getScaleX() + " " + muPDFReaderView.getDisplayedView().getWidth());
savePdf.setWH(iv_sign.getX(), iv_sign.getY());
// savePdf.setWidthScale(0);
// savePdf.setHeightScale(0);
//计算分辨率密度
DisplayMetrics metric = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metric);
float density = metric.density;
savePdf.setDensity(density);
Bitmap bitmap = getBitmap(AllPDFActivity.this, com.lonelypluto.pdflibrary.R.mipmap.ic_launcher);
savePdf.setBitmap(bitmap);
// Bitmap bitmap = Bitmap.createBitmap(vdhDeepLayout.getWidth(), vdhDeepLayout.getHeight(),
// Bitmap.Config.ARGB_8888);
// Canvas canvas = new Canvas(bitmap);
// vdhDeepLayout.draw(canvas);
// savePdf.setBitmap(bitmap);
Log.e(TAG, "iv_p = " + iv_sign.getX() + " " + iv_sign.getY());
Log.e(TAG, "屏幕 = " + vdhDeepLayout.getWidth() + " " + vdhDeepLayout.getHeight());
savePdfTask = new SavePdfTask(savePdf);
savePdfTask.execute();
}
});
vdhDeepLayout = (VDHDeepLayout)findViewById(R.id.VDHDeepLayout);
iv_sign = (ImageView)findViewById(R.id.iv_sign);
iv_test = (ImageView)findViewById(R.id.iv_test);
}
private static Bitmap getBitmap(Context context,int vectorDrawableId) {
Bitmap bitmap=null;
if (Build.VERSION.SDK_INT>Build.VERSION_CODES.LOLLIPOP){
Drawable vectorDrawable = context.getDrawable(vectorDrawableId);
bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
vectorDrawable.draw(canvas);
}else {
bitmap = BitmapFactory.decodeResource(context.getResources(), vectorDrawableId);
}
return bitmap;
}
public void setColor(int color) {
int red = (color & 0xff0000) >> 16;
int green = (color & 0x00ff00) >> 8;
int blue = (color & 0x0000ff);
Log.e(TAG, "r = " + red + " g = " + green + " b = " + blue);
}
/**
* 初始化工具栏
*/
private void initToolsView() {
mTopBarSwitcher = (ViewAnimator) findViewById(R.id.switcher);
mLinkButton = (ImageButton) findViewById(R.id.linkButton);
mAnnotButton = (ImageButton) findViewById(R.id.reflowButton);
mOutlineButton = (ImageButton) findViewById(R.id.outlineButton);
mSearchButton = (ImageButton) findViewById(R.id.searchButton);
mSearchText = (EditText) findViewById(R.id.searchText);
mSearchBack = (ImageButton) findViewById(R.id.searchBack);
mSearchFwd = (ImageButton) findViewById(R.id.searchForward);
mAnnotTypeText = (TextView) findViewById(R.id.annotType);
mPageNumberView = (TextView) findViewById(R.id.pageNumber);
mPageSlider = (SeekBar) findViewById(R.id.pageSlider);
mTopBarSwitcher.setVisibility(View.INVISIBLE);
mPageNumberView.setVisibility(View.INVISIBLE);
mPageSlider.setVisibility(View.INVISIBLE);
}
/**
* 打开文件
* @param path 文件路径
* @return
*/
private MuPDFCore openFile(String path) {
int lastSlashPos = path.lastIndexOf('/');
fileName = new String(lastSlashPos == -1
? path
: path.substring(lastSlashPos + 1));
Log.e(TAG, "filename = " + fileName);
Log.e(TAG, "Trying to open " + path);
try {
muPDFCore = new MuPDFCore(this, path);
// 新建:删除旧的目录数据
OutlineActivityData.set(null);
} catch (Exception e) {
Log.e(TAG, "openFile catch:" + e.toString());
return null;
} catch (OutOfMemoryError e) {
// out of memory is not an Exception, so we catch it separately.
Log.e(TAG, "openFile catch: OutOfMemoryError " + e.toString());
return null;
}
return muPDFCore;
}
private void createUI() {
if (muPDFCore == null)
return;
// Set up the page slider
int smax = Math.max(muPDFCore.countPages() - 1, 1);
mPageSliderRes = ((10 + smax - 1) / smax) * 2;
// Now create the UI.
// First create the document view
muPDFReaderView.setListener(new MuPDFReaderViewListener() {
@Override
public void onMoveToChild(int i) {
if (muPDFCore == null)
return;
mPageNumberView.setText(String.format("%d / %d", i + 1,
muPDFCore.countPages()));
mPageSlider.setMax((muPDFCore.countPages() - 1) * mPageSliderRes);
mPageSlider.setProgress(i * mPageSliderRes);
}
@Override
public void onTapMainDocArea() {
if (!mButtonsVisible) {
showButtons();
} else {
if (mTopBarMode == TopBarMode.Main)
hideButtons();
}
}
@Override
public void onDocMotion() {
hideButtons();
}
@Override
public void onHit(Hit item) {
switch (mTopBarMode) {
case Annot:
if (item == Hit.Annotation) {
showButtons();
mTopBarMode = TopBarMode.Delete;
mTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());
}
break;
case Delete:
mTopBarMode = TopBarMode.Annot;
mTopBarSwitcher.setDisplayedChild(mTopBarMode.ordinal());
// fall through
default:
// Not in annotation editing mode, but the pageview will
// still select and highlight hit annotations, so
// deselect just in case.
MuPDFView pageView = (MuPDFView) muPDFReaderView.getDisplayedView();
if (pageView != null)
pageView.deselectAnnotation();
break;
}
}
});
muPDFReaderView.setAdapter(new MuPDFPageAdapter(this, muPDFCore));
// 设置view的背景色
muPDFReaderView.setBackgroundColor(ContextCompat.getColor(this, com.lonelypluto.pdflibrary.R.color.muPDFReaderView_bg));
mSearchTask = new SearchTask(this, muPDFCore) {
@Override