前言:
接着上一篇的博客,在搜索出手机内的.pdf格式文件后,实现pdf文件的预览的方式有很多,
1.Android PdfViewer项目地址:
/barteksc/AndroidPdfViewer 功能很强大, 使用也比较广, 亲测可以使用. 唯一的缺点 :添加到项目中 会使apk增加10M左右, 这是最不能接受的, 故弃用.
2.PdfViewPager:
项目地址: /voghDev/PdfViewPager 可加载assets/SD卡/URL(在线预览) , 优点: 使用比较方便, 也不大
3.mozilla开源的PDF.js
项目地址:GitHub - mozilla/pdf.js: PDF Reader in JavaScript
是将pdf.js和相关的文件全部下载下来并拷贝的工程中的assets目录,apk会增加4M左右
4.腾讯的tbs
项目地址:腾讯浏览服务
这种方式是实现本地预览,把pdf文件下载到本地或直接加载本地的pdf文件
优点:使用方便,集成简单,包不大,可以远程依赖(ps:本文采用的是这种方式)
缺点:不能很好地支持部分64手机,Androidx兼容也不是很好.
5.实现的效果图如下:
6.跳转事件代码
//跳转到文件预览pdfAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {@Overridepublic void onItemClick(BaseQuickAdapter adapter, View view, int position) {String fileUrl = Objects.requireNonNull(pdfAdapter.getItem(position)).getFilePath();String fileName = Objects.requireNonNull(pdfAdapter.getItem(position)).getFileName();PDFPreViewActivity.actionStart(PDFSearchActivity.this, fileUrl, fileName);}});
7.PDFPreViewActivity代码:
/*** @作者: njb* @时间: /9/11 20:31* @描述: pdf文件预览*/public class PDFPreViewActivity extends AppCompatActivity implements TbsReaderView.ReaderCallback {private TbsReaderView mTbsReaderView;private String mFileName;private TextView tv_download;private long mRequestId;private DownloadObserver mDownloadObserver;private ProgressBar progressBar_download;private DownloadManager mDownloadManager;private String fileName;private String fileUrl;private String TAG = PDFPreViewActivity.class.getSimpleName();private RelativeLayout rootRl;private TextView tv_title;private ImageView iv_back;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_pdf_preview);initIntent();initTbsReaderView();mTbsReaderView = new TbsReaderView(this, this);rootRl.addView(mTbsReaderView, new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));if ((fileUrl == null) || (fileUrl.length() <= 0)) {Toast.makeText(PDFPreViewActivity.this, "获取文件url出错了", Toast.LENGTH_SHORT).show();return;}mFileName = PDFUtil.parseName(fileUrl);requestPermission();isDown();}private void initIntent() {if (getIntent() != null && getIntent().getExtras() != null) {fileUrl = getIntent().getExtras().getString("fileUrl");fileName = getIntent().getExtras().getString("fileName");}}//初始化TbsReaderView 5-3private void initTbsReaderView() {rootRl = findViewById(R.id.rl_tbsView);tv_download = findViewById(R.id.tv_download);progressBar_download = findViewById(R.id.progressBar_download);tv_title = findViewById(R.id.tv_title);tv_title.setText("PDF文件预览");iv_back = findViewById(R.id.iv_back);iv_back.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {finish();}});}private void requestPermission() {RxPermissions rxPermissions = new RxPermissions(this);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {rxPermissions.requestEach(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE).subscribe(new Consumer<Permission>() {@Overridepublic void accept(Permission permission) {if (permission.granted) {// 用户已经同意该权限Log.d(TAG, permission.name + " is granted.");} else if (permission.shouldShowRequestPermissionRationale) {// 用户拒绝了该权限,没有选中『不再询问』(Never ask again),那么下次再次启动时,还会提示请求权限的对话框Log.d(TAG, permission.name + " is denied. More info should be provided.");} else {// 用户拒绝了该权限,并且选中『不再询问』Log.d(TAG, permission.name + " is denied.");}}});}}private void isDown() {if (isLocalExist() || fileUrl.contains("storage")) {tv_download.setText("打开文件");tv_download.setVisibility(View.GONE);displayFile();} else {if (!fileUrl.contains("http")) {new AlertDialog.Builder(PDFPreViewActivity.this).setTitle("温馨提示:").setMessage("文件的url地址不合法哟,无法进行下载").setCancelable(false).setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {return;}}).create().show();}startDownload();}}@TargetApi(Build.VERSION_CODES.GINGERBREAD)private void queryDownloadStatus() {DownloadManager.Query query = new DownloadManager.Query().setFilterById(mRequestId);Cursor cursor = null;try {cursor = mDownloadManager.query(query);if (cursor != null && cursor.moveToFirst()) {// 已经下载的字节数long currentBytes = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));// 总需下载的字节数long totalBytes = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));// 状态所在的列索引int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));tv_download.setText("下载中...(" + PDFUtil.FormetFileSize(currentBytes)+ "/" + PDFUtil.FormetFileSize(totalBytes) + ")");// 将当前下载的字节数转化为进度位置int progress = (int) ((currentBytes * 1.0) / totalBytes * 100);progressBar_download.setProgress(progress);Log.i("downloadUpdate: ", currentBytes + " " + totalBytes + " "+ status + " " + progress);if (DownloadManager.STATUS_SUCCESSFUL == status&& tv_download.getVisibility() == View.VISIBLE) {tv_download.setVisibility(View.GONE);tv_download.performClick();if (isLocalExist()) {tv_download.setVisibility(View.GONE);progressBar_download.setVisibility(View.GONE);displayFile();}}}} finally {if (cursor != null) {cursor.close();}}}/*** 加载显示文件内容*/private void displayFile() {Bundle bundle = new Bundle();if (isLocalExist()) {bundle.putString("filePath", getLocalFile().getPath());}if (fileUrl.contains("storage")) {bundle.putString("filePath", fileUrl);}bundle.putString("tempPath", Environment.getExternalStorageDirectory().getPath());boolean result = mTbsReaderView.preOpen(PDFUtil.getFileType(mFileName), false);if (result) {mTbsReaderView.openFile(bundle);} else {File file = new File(getLocalFile().getPath());if (file.exists()) {Intent openintent = new Intent();openintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);String type = PDFUtil.getMIMEType(file);// 设置intent的data和Type属性。openintent.setDataAndType(Uri.fromFile(file), type);// 跳转startActivity(openintent);finish();}}}/*** 下载文件*/@SuppressLint("NewApi")private void startDownload() {mDownloadObserver = new DownloadObserver(new Handler());getContentResolver().registerContentObserver(Uri.parse("content://downloads/my_downloads"), true,mDownloadObserver);mDownloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);//将含有中文的url进行encodeString mFileUrl = toUtf8String(fileUrl);try {DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mFileUrl));request.setDestinationInExternalPublicDir(Constants.DOWNLOAD_PDF_PATH, mFileName);request.allowScanningByMediaScanner();request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);mRequestId = mDownloadManager.enqueue(request);} catch (Exception e) {e.printStackTrace();}}/*** 跳转页面** @param context* @param fileUrl 文件url* @param fileName 文件名*/public static void actionStart(Context context, String fileUrl, String fileName) {Intent intent = new Intent(context, PDFPreViewActivity.class);intent.putExtra("fileUrl", fileUrl);intent.putExtra("fileName", fileName);context.startActivity(intent);}/*** 是否为本地文件** @return*/private boolean isLocalExist() {return getLocalFile().exists();}/*** 本地文件目录** @return*/private File getLocalFile() {return new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), mFileName);}@Overridepublic void onCallBackAction(Integer integer, Object o, Object o1) {}private class DownloadObserver extends ContentObserver {private DownloadObserver(Handler handler) {super(handler);}@Overridepublic void onChange(boolean selfChange, Uri uri) {queryDownloadStatus();}}@Overrideprotected void onDestroy() {super.onDestroy();mTbsReaderView.onStop();if (mDownloadObserver != null) {getContentResolver().unregisterContentObserver(mDownloadObserver);}}}
8.activity_pdf_preview.xml布局代码
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#f5f5f5"android:orientation="vertical"><include layout="@layout/toolbar" /><RelativeLayoutandroid:id="@+id/rl_tbsView"android:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/tv_download"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="开始下载"android:textColor="@android:color/black"android:textSize="13sp" /><ProgressBarandroid:id="@+id/progressBar_download"style="?android:attr/progressBarStyleHorizontal"android:layout_width="match_parent"android:layout_height="3dp"android:layout_below="@+id/tv_download"android:layout_marginLeft="12dp"android:layout_marginRight="12dp"android:layout_marginTop="6dp"android:max="100"android:progressDrawable="@drawable/progressbar_color" /></RelativeLayout></LinearLayout>
9.build.gradle配置
implementation fileTree(include: ['*.jar'], dir: 'libs')implementation 'androidx.appcompat:appcompat:1.1.0'implementation 'androidx.constraintlayout:constraintlayout:1.1.3'testImplementation 'junit:junit:4.12'androidTestImplementation 'androidx.test:runner:1.2.0'androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'//Rx权限implementation 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.5@aar'implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.40'implementation 'androidx.recyclerview:recyclerview:1.0.0'implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'implementation 'io.reactivex.rxjava2:rxjava:2.2.3'implementation "com.tencent.tbs.tbssdk:sdk:43697"
10.Manifest权限配置
<uses-permission android:name="android.permission.INTERNET"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><uses-permission android:name="android.permission.READ_PHONE_STATE" /><uses-permission android:name="android.permission.READ_SETTINGS" /><uses-permission android:name="android.permission.WRITE_SETTINGS" /><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /><!-- 硬件加速对X5视频播放非常重要,建议开启 --><uses-permission android:name="android.permission.GET_TASKS" /><uses-permission android:name="android.permission.VIBRATE" /><uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" /><uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
11.遇到的问题:
11.1 64位手机so加载失败,由于腾讯x5浏览器官方的sdk库文件就提供了一种类型的so库,所以在APP的build.gradle目录下配置支持其他手机类型,配置如下:
ndk {abiFilters "armeabi", "armeabi-v7a", "x86", "mips"}
如果不指定so的目录,默认就会默认匹配main下的jniLibs目录:
11.2 x5浏览器初始化成功,但是加载失败,有些手机不兼容,解决方法如下:
使用远程依赖的方式加载 ,目前Androidx加载第一次可能会失败,多加载几次就好了.
12.最后,项目的源码下载地址为:
PDFSearch: Android实现手机内PDF文件搜索
13.csdn下载地址:没有积分的同学可以去码云下载.
PDFSearch.zip-Android文档类资源-CSDN下载