Android文档阅读之Office文档阅读的方案实现

Android文档阅读(1)Office文档阅读的方案实现

本文开始做一些查看网络或者本地文档的功能,对于一些用到简单打开office文档应用,本文可能会适合,方案中会用到NoHTTP的下载功能,没了解过的小伙伴们可以移步到NoHTTP的GitHub上了解一下:

NoHTTP文档:https://github.com/yanzhenjie/NoHttp

其中我们会用到几个下载监听和下载队列的类CallServer、HttpListener、HttpResponseListener

其中CallServer.java的代码为:

import android.app.Activity;

import com.yanzhenjie.nohttp.NoHttp;
import com.yanzhenjie.nohttp.download.DownloadListener;
import com.yanzhenjie.nohttp.download.DownloadQueue;
import com.yanzhenjie.nohttp.download.DownloadRequest;
import com.yanzhenjie.nohttp.rest.OnResponseListener;
import com.yanzhenjie.nohttp.rest.Request;
import com.yanzhenjie.nohttp.rest.RequestQueue;

/**
 * @Description:
 * @Encode: UTF-8
 * Created by zzj on 2018/4/4.
 */

public class CallServer {
    private final int MAX_DOWNLOAD_QUEUE = 10;
    private final int MAX_REQUEST_QUEUE = 10;

    private static CallServer sInstance;

    public static CallServer getInstance() {
        if (sInstance == null)
            synchronized (CallServer.class) {
                if (sInstance == null)
                    sInstance = new CallServer();
            }
        return sInstance;
    }

    private RequestQueue mRequestQueue;
    private DownloadQueue mDownloadQueue;


    private CallServer() {
        mRequestQueue = NoHttp.newRequestQueue(MAX_REQUEST_QUEUE);
        mDownloadQueue = NoHttp.newDownloadQueue(MAX_DOWNLOAD_QUEUE);
    }

    public <T> void request(int what, Request<T> request, OnResponseListener<T> listener) {
        mRequestQueue.add(what, request, listener);
    }

    public <T> void request(Activity activity, int what, Request<T> request, HttpListener<T> callback, boolean canCancel, boolean isLoading) {
        mRequestQueue.add(what, request, new HttpResponseListener<>(activity, request, callback, canCancel, isLoading));
    }

    public void download(int what, DownloadRequest request, DownloadListener listener) {
        mDownloadQueue.add(what, request, listener);
    }

    public void stop(DownloadRequest request){
        mDownloadQueue.stop();
    }

}

HttpListener.java的代码如下:

import com.yanzhenjie.nohttp.rest.Response;

/**
 * @Description:
 * @Encode: UTF-8
 * Created by zzj on 2018/4/4.
 */

public interface HttpListener<T> {

    void onSucceed(int what, Response<T> response);

    void onFailed(int what, Response<T> response);

}

HttpResponseListener.java代码如下:

import android.app.Activity;

import com.yanzhenjie.nohttp.rest.OnResponseListener;
import com.yanzhenjie.nohttp.rest.Request;
import com.yanzhenjie.nohttp.rest.Response;

/**
 * @Description:
 * @Encode: UTF-8
 * Created by zzj on 2018/4/4.
 */

public class HttpResponseListener<T> implements OnResponseListener<T> {

    private Activity mActivity;
    /**
     * Dialog.
     */
    /**
     * Request.
     */
    private Request<?> mRequest;
    /**
     * 结果回调.
     */
    private HttpListener<T> callback;

    /**
     * @param activity     context用来实例化dialog.
     * @param request      请求对象.
     * @param httpCallback 回调对象.
     * @param canCancel    是否允许用户取消请求.
     * @param isLoading    是否显示dialog.
     */
    public HttpResponseListener(Activity activity, Request<?> request, HttpListener<T> httpCallback, boolean canCancel, boolean isLoading) {
        this.mActivity = activity;
        this.mRequest = request;
        this.callback = httpCallback;
    }

    /**
     * 开始请求, 这里显示一个dialog.
     */
    @Override
    public void onStart(int what) {
    }

    /**
     * 结束请求, 这里关闭dialog.
     */
    @Override
    public void onFinish(int what) {
    }

    /**
     * 成功回调.
     */
    @Override
    public void onSucceed(int what, Response<T> response) {
        if (callback != null) {
            // 这里判断一下http响应码,这个响应码问下你们的服务端你们的状态有几种,一般是200成功。
            // w3c标准http响应码:http://www.w3school.com.cn/tags/html_ref_httpmessages.asp

            callback.onSucceed(what, response);
        }
    }

    /**
     * 失败回调.
     */
    @Override
    public void onFailed(int what, Response<T> response) {
        if (callback != null)
            callback.onFailed(what, response);
    }

}

注意提醒一下,使用NoHTTP记得初始化一下,并且加上以下权限:


    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Office文档的阅读

做Office文本的打开,网上方案可能会有一大堆,有些需要自己手动去解析文档的各个节点,对于只是需要简单的阅读,可能不需要把工作量加到这么大,我们可以变通一下。Android自动的WebView本身不支持解析Office文档,不像IOS提供的WebView,可以做到功能很强大,可以直接打开网络文档,所以作为一名Android开发者,有时候会比较头疼。本文采用的方案是首先采取调起本地阅读器,如果检测不到本地有阅读器,则需要用到一个备选方案,采用微软服务器地址拼接网络文档地址来解析返回,然后直接丢进给WebView去展示。

先看下载指示layout_data_download.xml布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rl_download_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white">

    <ImageView
        android:id="@+id/iv_data_cover"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="100dp" />

    <TextView
        android:id="@+id/tv_file_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/iv_data_cover"
        android:layout_marginTop="18dp"
        android:ellipsize="end"
        android:gravity="center"
        android:maxLines="2"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:text="测试文件.txt"
        android:textColor="#24272B"
        android:textSize="16sp" />

    <Button
        android:id="@+id/btn_download"
        android:layout_width="130dp"
        android:layout_height="34dp"
        android:layout_below="@+id/tv_file_name"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="50dp"
        android:text="下载"
        android:textSize="14sp"
        android:visibility="visible" />

    <TextView
        android:id="@+id/tv_download_progress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tv_file_name"
        android:layout_marginTop="40dp"
        android:gravity="center"
        android:paddingLeft="20dp"
        android:paddingRight="20dp"
        android:text="下载中(6.1MB/13.7MB)"
        android:textColor="#B1B1B1"
        android:textSize="14sp"
        android:visibility="gone" />

    <ProgressBar
        android:id="@+id/pb_download"
        style="@style/progressBarDownload"
        android:layout_width="match_parent"
        android:layout_height="4dp"
        android:layout_below="@+id/tv_download_progress"
        android:layout_marginLeft="64dp"
        android:layout_marginRight="64dp"
        android:layout_marginTop="12dp"
        android:max="100"
        android:visibility="gone" />

</RelativeLayout>

style.xml progressBarDownload的代码如下:

    <style name="progressBarDownload" parent="android:Widget.ProgressBar.Horizontal">
        <item name="android:indeterminateOnly">false</item>
        <item name="android:progressDrawable">@drawable/progress_download</item>
        <item name="android:minHeight">4dp</item>
        <item name="android:maxHeight">4dp</item>
    </style>

其中drawable样式progress_download如下:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@android:id/background"
        android:drawable="@drawable/shape_pb_download_bg" />
    <item android:id="@android:id/secondaryProgress">
        <scale
            android:drawable="@drawable/shape_pb_download_second"
            android:scaleWidth="100%" />
    </item>
    <item android:id="@android:id/progress">
        <scale
            android:drawable="@drawable/shape_pb_download_second"
            android:scaleWidth="100%" />
    </item>
</layer-list>

progress的背景shape_pb_download_bg.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <size
        android:width="250dp"
        android:height="4dp" />
    <solid android:color="#E8E8E8" />
    <corners android:radius="4dp" />
</shape>

progress的进度shape_pb_download_second.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <size
        android:width="250dp"
        android:height="4dp" />
    <solid android:color="#57E727" />
    <corners android:radius="4dp" />
</shape>

上面一大堆xml代码都是下载进度显示,对于不需要提示,只要能下载下来的话,可能直接省略这些,然后在代码和布局中直接不处理就好,小编只是让下载进度更直观,所以加上这些。

activity的布局act_reader_office.xml如下,直接一个WebView和一个下载进度显示的layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone" />

    <include
        layout="@layout/layout_data_download"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/title"
        android:visibility="gone" />

</RelativeLayout>

好了,前期工作准备完了,我们看下activity的具体实现:


import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.text.format.Formatter;
import android.util.Log;
import android.view.View;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.yanzhenjie.nohttp.Headers;
import com.yanzhenjie.nohttp.NoHttp;
import com.yanzhenjie.nohttp.download.DownloadListener;
import com.yanzhenjie.nohttp.download.DownloadRequest;

import java.io.File;

/**
 * @Description: office 文档阅读页面
 * @Author: zzj
 * @Date: 2018/9/6 16:15
 * @Version: 1.0.0
 */
public class OfficeReaderActivity extends Activity {
    //测试数据,微软文档解析服务器的测试url
    //DOC: http://view.officeapps.live.com/op/view.aspx?src=newteach.pbworks.com%2Ff%2Fele%2Bnewsletter.docx
    //EXCEL: http://view.officeapps.live.com/op/view.aspx?src=http%3A%2F%2Flearn.bankofamerica.com%2Fcontent%2Fexcel%2FWedding_Budget_Planner_Spreadsheet.xlsx
    //PPT: http://view.officeapps.live.com/op/view.aspx?src=http%3a%2f%2fvideo.ch9.ms%2fbuild%2f2011%2fslides%2fTOOL-532T_Sutter.pptx

    //微软解析服务器地址
    private static final String MICROSOFT_SERVER = "http://view.officeapps.live.com/op/view.aspx?src=";
    //下载地址
    private static final String ROOT_PATH = "/mnt/sdcard/office";
    private String url = "";
    private String title = "";
    private String fileSize;

    private DownloadRequest downloadRequest;

    private RelativeLayout rlDownloadContainer;
    private TextView tvDlFileName;
    private TextView tvDlProgress;
    private ProgressBar pbDownload;
    private Button btnDownload;
    private ImageView ivCover;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.act_reader_office);

        //url可以自己去构造,我这边直接用测试URL
        url = "http://view.officeapps.live.com/op/view.aspx?src=newteach.pbworks.com%2Ff%2Fele%2Bnewsletter.docx";
        fileSize = Formatter.formatFileSize(this, getIntent().getIntExtra("fileSize", 0));
  
        downloadOffice(url, title);
    }


    /**
    * 当检测不到本地有阅读器时,初始化WebView,通过URL来解析显示
    */
    private void initWebView() {
        WebView urlWebView = findViewById(R.id.webView);
        urlWebView.setVisibility(View.VISIBLE);
        urlWebView.setWebViewClient(new AppWebViewClients());
        urlWebView.getSettings().setJavaScriptEnabled(true);
        urlWebView.getSettings().setUseWideViewPort(true);
        urlWebView.loadUrl(MICROSOFT_SERVER + url);
    }

    private void downloadOffice(String url, String fileName) {
        File file = new File(ROOT_PATH + "/" + fileName);
        //先检测本地是否已经有这个文件,有的话直接打开,没的话就下载回来
        if (file.exists()) {
            setData(file.getPath());
            return;
        }
        
        showDownloadView(fileName);
    }

    private void showDownloadView(String fileName) {
        rlDownloadContainer = findViewById(R.id.rl_download_container);
        tvDlFileName = findViewById(R.id.tv_file_name);
        tvDlProgress = findViewById(R.id.tv_download_progress);
        btnDownload = findViewById(R.id.btn_download);
        pbDownload = findViewById(R.id.pb_download);
        ivCover = findViewById(R.id.iv_data_cover);
        tvDlFileName.setText(fileName);
        rlDownloadContainer.setVisibility(View.VISIBLE);
        //R.string.download为字符“下载”
        btnDownload.setText(String.format("%s(%s)", getString(R.string.download), fileSize));
        //点击下载按钮,开启下载请求,并加入到下载队列当中
        btnDownload.setOnClickListener(v -> {
            if (downloadRequest == null) {
                downloadRequest = NoHttp.createDownloadRequest(url, ROOT_PATH , fileName, true, false);
                CallServer.getInstance().download(0, downloadRequest, downloadListener);
            }
        });
    }

    /**
    * WPS适配比较麻烦,需要按照官方需要的信息传,所以我们这边也适配一下WPS,传入相应的数据
    */
    private void setData(String path) {
        Intent intent = new Intent();
        Bundle bundle = new Bundle();
        bundle.putString(WpsModel.OPEN_MODE, WpsModel.OpenMode.READ_ONLY);
        //打开模式
        bundle.putBoolean(WpsModel.SEND_SAVE_BROAD, true);
        //关闭时是否发送广播
        bundle.putString(WpsModel.THIRD_PACKAGE, getApplication().getPackageName());
        //第三方应用的包名,用于对改应用合法性的验证
        bundle.putBoolean(WpsModel.CLEAR_TRACE, true);
        //清除打开记录
        //bundle.putBoolean(CLEAR_FILE, true);
        //关闭后删除打开文件
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.setAction(android.content.Intent.ACTION_VIEW);
        File file = new File(path);
        Uri uri = Uri.fromFile(file);
        intent.setDataAndType(uri, mimeType);
        intent.putExtras(bundle);
        //7.0以上需要权限
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
        try {
            startActivity(intent);
        } catch (ActivityNotFoundException e) {
            //找不到打开的应用就用webView打开
            initWebView();
        }
    }

    /**
    * 下载进度监听
    */
    private final DownloadListener downloadListener = new DownloadListener() {
        @Override
        public void onDownloadError(int what, Exception exception) {
            if (rlDownloadContainer != null) {
                rlDownloadContainer.setVisibility(View.GONE);
            }
        }

        @Override
        public void onStart(int what, boolean isResume, long rangeSize, Headers responseHeaders, long allCount) {
        	//下载开始,隐藏相应的View和显示进度条
            if (btnDownload != null) {
                btnDownload.setVisibility(View.GONE);
                tvDlProgress.setVisibility(View.VISIBLE);
                pbDownload.setVisibility(View.VISIBLE);
            }
        }

        @Override
        public void onProgress(int what, int progress, long fileCount, long speed) {
        	//更新下载进度
            pbDownload.setProgress(progress);
            tvDlProgress.setText(String.format("%s(%s/%s)", getResources().getString(R.string.downloading),
                    Formatter.formatFileSize(OfficeReaderActivity.this, fileCount), fileSize));
        }

        @Override
        public void onFinish(int what, String filePath) {
        	//下载完成
            setData(filePath);
            rlDownloadContainer.setVisibility(View.GONE);
        }

        @Override
        public void onCancel(int what) {
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //退出时如果下载还没完成,需要手动取消,避免内存泄漏和报错
        if (downloadRequest != null) {
            downloadRequest.cancel();
        }
    }


    class AppWebViewClients extends WebViewClient {

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            // TODO Auto-generated method stub
            view.loadUrl(url);
            return true;
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            // TODO Auto-generated method stub
            super.onPageFinished(view, url);
            //编写 javaScript方法
            Log.d("zzj", "onPageFinished() url = " + url);
        }
    }

    //适配WPS Android客户端调起
    public class WpsModel {
        public static final String OPEN_MODE = "OpenMode";// 打开文件的模式。
        public static final String SEND_SAVE_BROAD = "SendSaveBroad";// 文件保存时是否发送广播。
        public static final String SEND_CLOSE_BROAD = "SendCloseBroad";// 文件关闭时是否发送广播
        public static final String THIRD_PACKAGE = "ThirdPackage";// 第三方的包名,关闭的广播会包含该项。
        public static final String CLEAR_BUFFER = "ClearBuffer";// 关闭文件时是否请空临时文件。
        public static final String CLEAR_TRACE = "ClearTrace";// 关闭文件时是否删除使用记录。
        public static final String CLEAR_FILE = "ClearFile";// 关闭文件时是否删除打开的文件。
        public static final String VIEW_PROGRESS = "ViewProgress";// 文件上次查看的进度。
        public static final String AUTO_JUMP = "AutoJump";// 是否自动跳转到上次查看的进度。
        public static final String SAVE_PATH = "SavePath";// 文件保存路径。
        public static final String VIEW_SCALE = "ViewScale";// 文件上次查看的视图的缩放。
        public static final String VIEW_SCALE_X = "ViewScrollX";// 文件上次查看的视图的X坐标。
        public static final String VIEW_SCALE_Y = "ViewScrollY";// 文件上次查看的视图的Y坐标。
        public static final String USER_NAME = "UserName";// 批注的作者。
        public static final String HOMEKEY_DOWN = "HomeKeyDown";// 监听home键并发广播
        public static final String BACKKEY_DOWN = "BackKeyDown";// 监听back键并发广播
        public static final String ENTER_REVISE_MODE = "EnterReviseMode";// 以修订模式打开文档
        public static final String CACHE_FILE_INVISIBLE = "CacheFileInvisible";// Wps生成的缓存文件外部是否可见

        public class OpenMode {
            public static final String NORMAL = "Normal";// 只读模式
            public static final String READ_ONLY = "ReadOnly";// 正常模式
            public static final String READ_MODE = "ReadMode";// 打开直接进入阅读器模式
            // 仅Word、TXT文档支持
            public static final String SAVE_ONLY = "SaveOnly";// 保存模式(打开文件,另存,关闭)
            // 仅Word、TXT文档支持
        }

        public class ClassName {
            public static final String NORMAL = "cn.wps.moffice.documentmanager.PreStartActivity2";
            // 普通版
            public static final String ENGLISH = "cn.wps.moffice.documentmanager.PreStartActivity2";
            // 英文版
            public static final String ENTERPRISE = "cn.wps.moffice.documentmanager.PreStartActivity2";
            // 企业版
        }

        public class PackageName {
            public static final String NORMAL = "cn.wps.moffice_eng";// 普通版
            public static final String ENGLISH = "cn.wps.moffice_eng";// 英文版
        }

        public class Reciver {
            public static final String ACTION_BACK = "com.kingsoft.writer.back.key.down";// 返回键广播
            public static final String ACTION_HOME = "com.kingsoft.writer.home.key.down";// Home键广播
            public static final String ACTION_SAVE = "cn.wps.moffice.file.save";// 保存广播
            public static final String ACTION_CLOSE = "cn.wps.moffice.file.close";// 关闭文件广播
        }
    }

}

整个流程大概可以总结为:

 

结语

本文篇幅可能会有点长,主要是先介绍到NoHTTP下载这一块,这一块在下载功能中非常简单易用,剩下的就是office文档的打开阅读,写的可能不太好,欢迎拍砖指出错误哈~

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值