Android实现PDF格式文件预览

  • Post author:
  • Post category:其他


前言:

接着上一篇的博客,在搜索出手机内的.pdf格式文件后,实现pdf文件的预览的方式有很多,

1.Android PdfViewer项目地址:


https://github.com/barteksc/AndroidPdfViewer

功能很强大, 使用也比较广, 亲测可以使用. 唯一的缺点 :添加到项目中 会使apk增加10M左右, 这是最不能接受的, 故弃用.

2.PdfViewPager:

项目地址:

https://github.com/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.实现的效果图如下:

img

6.跳转事件代码

//跳转到文件预览
pdfAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
    @Override
    public 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
 * @时间: 2019/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;
​
    @Override
    protected 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-3
    private 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() {
            @Override
            public 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>() {
                        @Override
                        public 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() {
                            @Override
                            public 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进行encode
        String 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);
    }
​
    @Override
    public void onCallBackAction(Integer integer, Object o, Object o1) {
​
    }
​
​
    private class DownloadObserver extends ContentObserver {
​
        private DownloadObserver(Handler handler) {
            super(handler);
        }
​
        @Override
        public void onChange(boolean selfChange, Uri uri) {
            queryDownloadStatus();
        }
    }
​
​
    @Override
    protected 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="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#f5f5f5"
    android:orientation="vertical">
​
    <include layout="@layout/toolbar" />
​
    <RelativeLayout
        android:id="@+id/rl_tbsView"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
​
        <TextView
            android: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" />
​
        <ProgressBar
            android: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目录:

img

11.2 x5浏览器初始化成功,但是加载失败,有些手机不兼容,解决方法如下:

使用远程依赖的方式加载 ,目前Androidx加载第一次可能会失败,多加载几次就好了.

img

12.最后,项目的源码下载地址为:


PDFSearch: Android实现手机内PDF文件搜索

13.csdn下载地址:没有积分的同学可以去码云下载.


PDFSearch.zip-Android文档类资源-CSDN下载



版权声明:本文为u012556114原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。