1. 程式人生 > >Android 實現PDF檔案的檢視(本地和網路檔案)

Android 實現PDF檔案的檢視(本地和網路檔案)

PDF檔案大家應該不陌生,說的簡單點,其實我認為就是一個全圖片的Word,裡面的每個頁面都可以看做是一張帶文字的圖片,接下來我們看看在Android應用裡如何讀取和檢視PDF檔案內容。

1 在專案的gradle中增加如下程式碼:

compile 'com.joanzapata.pdfview:android-pdfview:[email protected]'

先看看佈局檔案程式碼

<?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:orientation="vertical">

    <include layout="@layout/title_all" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.joanzapata.pdfview.PDFView
            android:id="@+id/pdf"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <TextView
            android:id="@+id/tv_page"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:textColor="@color/colorAccent"
            android:layout_alignParentTop="true"
            android:textSize="18sp" />
    </RelativeLayout>
</LinearLayout>

其中PDFView控制元件就是pdfview庫裡面的檢視PDF的控制元件,接下來宣告並初始化後檢視本地Asserts資源下的PDF檔案,程式碼如下

pdf.fromAsset("abcd.pdf")
        .defaultPage(1)
        .showMinimap(false)
        .enableSwipe(true)
        .onPageChange(this)
        .load();

這樣就可以檢視pdf檔案內容了,但是如果是網上線上檢視PDF檔案該如何實現呢?

我的思路是這樣:

第一步,把PDF檔案下載下來;

第二步,把檔案儲存在手機儲存中;

第三步,讀取手機SD卡的PDF檔案。

接下來就一步一步實現,當然下載、儲存、讀取SD卡的內容首先就得有許可權,相關許可權的申請就不多說了,基本功你懂得!

下載的話方式很多,你可以用任何可以下載檔案的網路框架,我用的是okhttp下載的,程式碼如下

/**
 * 下載
 */
private void downloadFile() {
    OkHttpUtils.build().download(path, savePath, new OkHttpUtils.OnDownloadListener() {
        @Override
        public void onDownloadSuccess(File file) {
            tvPage.setText("載入完成正在開啟..");
            showPdf(file.getPath());
        }
        @Override
        public void onDownloading(int progress) {
            Log.i("JAVA", "onDownloading"+progress);
            tvPage.setText("正在載入("+progress+"/100)");
        }
        @Override
        public void onDownloadFailed() {
            Log.i("JAVA", "onDownloadFailed");
            tvPage.setText("載入失敗..");
        }
    });
}

下載方法中path是網路上PDF檔案的額下載全路徑,savePath是下載下來的檔案儲存的目錄,onDownloadSuccess是下載完成後的回撥方法,回撥的是下載的檔案,file.getPath()可以獲取這個檔案儲存的路徑地址,okhttp我自己封裝了一個工具類,程式碼奉上

**
 * OkHttp3工具類
 * 2018/9/11
 */
public class OkHttpUtils {
    private static OkHttpClient client;
    private static OkHttpUtils okHttpUtils;
    private OkHttpCallback callback;

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1://異常
                    IOException e = (IOException) msg.obj;
                    LogUtils.i("ruin", "e--> " + e.toString());
                    callback.onError(e);
                    break;
                case 2://成功
                    String result = (String) msg.obj;
                    callback.onResponse(result);
                    break;
            }
        }
    };

    /**
     * http請求
     */
    public static OkHttpUtils build() {
        OkHttpClient.Builder sBuilder = new OkHttpClient.Builder();
        client = sBuilder.build();
        okHttpUtils = new OkHttpUtils();
        return okHttpUtils;
    }

    //設定回撥方法
    public OkHttpUtils setCallback(OkHttpCallback callback) {
        this.callback = callback;
        return okHttpUtils;
    }

    //post請求
    public OkHttpUtils postOkHttp(String url, Map<String, Object> params) {
        FormBody.Builder builder = new FormBody.Builder();
        String temp = "";
        for (String key : params.keySet()) {
            builder.add(key, String.valueOf(params.get(key)));
            temp += (key + "=" + String.valueOf(params.get(key)) + "&");
        }
        LogUtils.i("LogUtils", "url =" + url + "?" + temp);
        FormBody formBody = builder.build();
        Request request = new Request.Builder()
                .url(url)
                .post(formBody)
                .build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Message msg = Message.obtain();
                msg.what = 1;
                msg.obj = e;
                handler.sendMessage(msg);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String result = response.body().string();
                Message msg = Message.obtain();
                msg.what = 2;
                msg.obj = result;
                handler.sendMessage(msg);
            }
        });
        return okHttpUtils;
    }


    public OkHttpUtils postAsync(String url, Map<String, Object> params) {
        RequestBody requestBody;
        if (params == null) {
            params = new HashMap<>();
        }
        /**
         * OKhttp3.0之後版本
         */
        FormBody.Builder builder = new FormBody.Builder();
        /**
         * 在這對新增的引數進行遍歷,map遍歷有四種方式,如果想要了解的可以網上查詢
         */
        String temp = "";
        for (Map.Entry<String, Object> map : params.entrySet()) {
            String key = map.getKey();
            Object value;
            value = map.getValue() == null ? "" : map.getValue();
            builder.add(key, String.valueOf(value));
            temp += (key + "=" + String.valueOf(value));
        }
        LogUtils.i("LogUtils", "url =" + url + "?" + temp);
        requestBody = builder.build();
        final Request request = new Request.Builder().url(url).post(requestBody).build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Message msg = Message.obtain();
                msg.what = 1;
                msg.obj = e;
                handler.sendMessage(msg);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String result = response.body().string();
                Message msg = Message.obtain();
                msg.what = 2;
                msg.obj = result;
                handler.sendMessage(msg);
            }
        });
        return okHttpUtils;
    }

    //請求回撥介面
    public interface OkHttpCallback {
        void onError(Exception e);
        void onResponse(String result);
    }

    /**
     * 判斷是否有網路連線
     */
    private boolean isNetworkConnected(@NonNull Context context) {
        ConnectivityManager mConnectivityManager =
                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
        return mNetworkInfo != null && mNetworkInfo.isAvailable();
    }

    /**
     * @param url      下載連線
     * @param saveDir  儲存下載檔案的SDCard目錄
     * @param listener 下載監聽
     */
    public void download(final String url, final String saveDir, final OnDownloadListener listener) {
        Request request = new Request.Builder().url(url).build();
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                // 下載失敗
                listener.onDownloadFailed();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                InputStream is = null;
                byte[] buf = new byte[2048];
                int len = 0;
                FileOutputStream fos = null;
                // 儲存下載檔案的目錄
                String savePath = isExistDir(saveDir);
                try {
                    is = response.body().byteStream();
                    long total = response.body().contentLength();
                    File file = new File(savePath, getNameFromUrl(url));
                    fos = new FileOutputStream(file);
                    long sum = 0;
                    while ((len = is.read(buf)) != -1) {
                        fos.write(buf, 0, len);
                        sum += len;
                        int progress = (int) (sum * 1.0f / total * 100); // 下載中
                        listener.onDownloading(progress);
                    }
                    fos.flush(); // 下載完成
                    listener.onDownloadSuccess(file);
                } catch (Exception e) {
                    listener.onDownloadFailed();
                } finally {
                    try {
                        if (is != null) {
                            is.close();
                        }
                    } catch (IOException e) {
                    }
                    try {
                        if (fos != null) {
                            fos.close();
                        }
                    } catch (IOException e) {
                    }
                }
            }
        });
    }

    /**
     * 判斷下載目錄是否存在
     */
    private String isExistDir(String saveDir) throws IOException {
        // 下載位置
        File downloadFile = new File(Environment.getExternalStorageDirectory(), saveDir);
        if (!downloadFile.mkdirs()) {
            downloadFile.createNewFile();
        }
        String savePath = downloadFile.getAbsolutePath();
        return savePath;
    }

    /**
     * 從下載連線中解析出檔名
     */
    @NonNull
    private String getNameFromUrl(String url) {
        return url.substring(url.lastIndexOf("/") + 1);
    }

    public interface OnDownloadListener {
        /**
         * 下載成功
         */
        void onDownloadSuccess(File file);
        /**
         * 下載進度
         */
        void onDownloading(int progress);
        /**
         * 下載失敗
         */
        void onDownloadFailed();
    }
}

當然要用okhttp下載需要新增okhttp庫檔案,我用的是okhttp3

compile 'com.squareup.okhttp3:okhttp:3.9.0'

在上面下載成功的回撥方法裡傳入下載的PDF檔案路徑開啟檔案 showPdf(file.getPath());

private void showPdf(String fileName) {
    if (TextUtils.isEmpty(fileName)) {
        Toast.makeText(this, "檔案不存在", Toast.LENGTH_SHORT).show();
    } else {
        try {
            pdf.fromFile(new File(fileName))
                    .defaultPage(1)
                    .showMinimap(false)
                    .enableSwipe(true)
                    .onLoad(new OnLoadCompleteListener() {
                        @Override
                        public void loadComplete(int nbPages) {
                            float pageWidth = pdf.getOptimalPageWidth();
                            float viewWidth = pdf.getWidth();
                            pdf.zoomTo(viewWidth / pageWidth);
                            pdf.loadPages();
                        }
                    })
                    .onPageChange(this)
                    .load();
        } catch (FileNotFoundException e) {
            Toast.makeText(this, "檔案不存在", Toast.LENGTH_SHORT).show();
            File file = new File(fileName);
            FileUtils.deleteFile(file);
        }
    }
}

開啟之後就可以查看了,和檢視Asserts資源下的PDF檔案基本一樣了,有一個點差點忘了就是實現OnPageChangeListener介面,然後在重寫的onPageChanged方法中顯示當前檢視的頁面是多少頁和一共有多少頁

@Override
public void onPageChanged(int page, int pageCount) {
    String pager = "頁碼(" + page + "/" + pageCount + ")";
    tvPage.setText(pager);
}

好了,pdf檔案檢視就這樣完成了,誰有更好的方法可以互相推薦學習。