Android 打开 office 文档的几种方式

您所在的位置:网站首页 ios如何打开doc文件 Android 打开 office 文档的几种方式

Android 打开 office 文档的几种方式

2023-10-19 20:41| 来源: 网络整理| 查看: 265

Android 在开发项目的过程中遇到了关于office 文件打开浏览的问题,在解决完后,在这里做个记录,也给各位未接触过的小伙伴们一点方向(会在文末附上该项目的git链接地址)。在查阅相关资料后,给出了几种方案:

方案一:用谷歌或微软的url链接格式拼接后,通过控件webView内置预览。

方案二:使用第三方手机软件WPS 打开本地文档。

方案三:使用国内腾讯 X5内核浏览控件 打开本地文档。

在自己亲自尝试后,方案一由于国内Android手机各种魔改和需要翻墙不可行,故介绍其它两种可行的方案与其缺陷。

结论先行: 方案二:通过第三方软件打开,若手机设备未安装WPS等能打开文档的相关软件则无法调用打开 方案三:能在自己APP内置打开,若手机设备未安装QQ或微信,则无法调用打开 方案三的基础上的优化:将x5内核放在自己工程中或者后台服务器上下载下来,静态集成进来,有大佬已经写过相关文章,亲测可用,就不献丑了,链接如下:Android静态集成X5内核

点击跳转无效的话直接使用以下链接: https://blog.csdn.net/qq_34205629/article/details/122375262#comments_20014428

方案二效果图如下:

wps打开录屏.gif

一般情况是由后台生成文件链接,我们下载保存到手机中再打开,由于没有服务器支持,我这里是将assets文件夹的test.docx文件保存到手机中去。再调用WPS打开文件

在这之前借鉴了大佬的博客: https://blog.csdn.net/qq_31939617/article/details/83443440?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-1

开始上代码: 由于我们要将文件写入到手机中,需要写入权限

读取目标文件可能会遇到7.0以上手机 FileUriExposedException ,这里动态申请权限和解决FileUri异常相信大多是博客写的很详细,我就不赘诉了

权限初始化判断授权,6.0以上动态申请

checkPermissions();

checkPermissions 方法

private void checkPermissions() { //检查是否获得权限 if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { //没有获得授权,申请授权 if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { //弹窗解释为何需要该权限,再次请求权限 Toast.makeText(MainActivity.this, "请授权,否则无法存储test.docx 文档", Toast.LENGTH_LONG).show(); //跳转到应用设置界面 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivity(intent); } else { //不需要解释为何需要授权直接请求授权 ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_CALL_PHONE); } } else { //获得授权,将文件写入到 saveFileToPhone(); } }

onRequestPermissionsResult 权限申请回调:

@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case MY_PERMISSIONS_REQUEST_CALL_PHONE: { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { //授权成功,写入文件 saveFileToPhone(); } else { //授权失败 Toast.makeText(this, "授权失败!", Toast.LENGTH_LONG).show(); } break; } } }

动态申请权限成功之后直接调用 saveFileToPhone() 将 assets中的文件保存复制到手机中

private void saveFileToPhone() { FileUtils.getInstance(getApplicationContext()).copyAssetsToSD("", "officeShow/office").setFileOperateCallback(new FileUtils.FileOperateCallback() { @Override public void onSuccess() { Toast.makeText(MainActivity.this,"保存成功,可以打开啦",Toast.LENGTH_SHORT).show(); fileUrl= Environment.getExternalStorageDirectory()+"/officeShow/office/test.docx"; } @Override public void onFailed(String error) { Toast.makeText(MainActivity.this,"保存失败:"+error,Toast.LENGTH_SHORT).show(); } }); }

工具类代码如下:

public class FileUtils { private static FileUtils instance; private static final int SUCCESS = 1; private static final int FAILED = 0; private Context context; private FileOperateCallback callback; private volatile boolean isSuccess; private String errorStr; public static FileUtils getInstance(Context context) { if (instance == null) instance = new FileUtils(context); return instance; } private FileUtils(Context context) { this.context = context; } private Handler handler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (callback != null) { if (msg.what == SUCCESS) { callback.onSuccess(); } if (msg.what == FAILED) { callback.onFailed(msg.obj.toString()); } } } }; public FileUtils copyAssetsToSD(final String srcPath, final String sdPath) { new Thread(new Runnable() { @Override public void run() { copyAssetsToDst(context, srcPath, sdPath); if (isSuccess) handler.obtainMessage(SUCCESS).sendToTarget(); else handler.obtainMessage(FAILED, errorStr).sendToTarget(); } }).start(); return this; } public void setFileOperateCallback(FileOperateCallback callback) { this.callback = callback; } private void copyAssetsToDst(Context context, String srcPath, String dstPath) { try { String fileNames[] = context.getAssets().list(srcPath); if (fileNames.length > 0) { File file = new File(Environment.getExternalStorageDirectory(), dstPath); if (!file.exists()) file.mkdirs(); for (String fileName : fileNames) { if (!srcPath.equals("")) { // assets 文件夹下的目录 copyAssetsToDst(context, srcPath + File.separator + fileName, dstPath + File.separator + fileName); } else { // assets 文件夹 copyAssetsToDst(context, fileName, dstPath + File.separator + fileName); } } } else { File outFile = new File(Environment.getExternalStorageDirectory(), dstPath); InputStream is = context.getAssets().open(srcPath); FileOutputStream fos = new FileOutputStream(outFile); byte[] buffer = new byte[1024]; int byteCount; while ((byteCount = is.read(buffer)) != -1) { fos.write(buffer, 0, byteCount); } fos.flush(); is.close(); fos.close(); } isSuccess = true; } catch (Exception e) { e.printStackTrace(); errorStr = e.getMessage(); isSuccess = false; } } public interface FileOperateCallback { void onSuccess(); void onFailed(String error); } }

保存成功之后,通过点击按钮启动意图打开手机中的第三方软件:WPS,打开之前先通过isInstall函数判断是否已安装

private boolean isInstall(Context context, String packageName) { final PackageManager packageManager = context.getPackageManager(); // 获取所有已安装程序的包信息 List pinfo = packageManager.getInstalledPackages(0); for (int i = 0; i < pinfo.size(); i++) { if (pinfo.get(i).packageName.equalsIgnoreCase(packageName)) return true; } return false; } @Override public void onClick(View view) { switch (view.getId()){ case R.id.btn_openWithWPS: if (!isInstall(this, "cn.wps.moffice_eng")) { Toast.makeText(this,"请下载安装WPS",Toast.LENGTH_SHORT).show(); return; } startActivity(getWordFileIntent(fileUrl)); break; } }

若已经安装,则通过getWordFileIntent(fileUrl) 启动意图

private Intent getWordFileIntent(String Path) { File file = new File(Path); Intent intent = new Intent("android.intent.action.VIEW"); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); Uri uri; //Uri uri = Uri.fromFile(file);//解决FileUriExposedException if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { uri = FileProvider.getUriForFile(getApplicationContext(), getPackageName() + ".fileprovider", new File(String.valueOf(file))); } else { uri = Uri.fromFile(new File(String.valueOf(file))); } String type = getMIMEType(file); if (type.contains("pdf") || type.contains("vnd.ms-powerpoint") || type.contains("vnd.ms-word") || type.contains("vnd.ms-excel") || type.contains("text/plain") || type.contains("text/html")) { if (isInstall(this, "cn.wps.moffice_eng")) { intent.setClassName("cn.wps.moffice_eng", "cn.wps.moffice.documentmanager.PreStartActivity2"); intent.setData(uri); } else { intent.addCategory("android.intent.category.DEFAULT"); intent.setDataAndType(uri, type); } } else { intent.addCategory("android.intent.category.DEFAULT"); intent.setDataAndType(uri, type); } return intent; } /** * 判断文件类型 */ private static String getMIMEType(File f) { String type = ""; String fName = f.getName(); /* 取得扩展名 */ String end = fName.substring(fName.lastIndexOf(".") + 1, fName.length()).toLowerCase(); /* 依扩展名的类型决定MimeType */ if (end.equals("pdf")) { type = "application/pdf"; } else if (end.equals("m4a") || end.equals("mp3") || end.equals("mid") || end.equals("xmf") || end.equals("ogg") || end.equals("wav")) { type = "audio/*"; } else if (end.equals("3gp") || end.equals("mp4")) { type = "video/*"; } else if (end.equals("jpg") || end.equals("gif") || end.equals("png") || end.equals("jpeg") || end.equals("bmp")) { type = "image/*"; } else if (end.equals("apk")) { type = "application/vnd.android.package-archive"; } else if (end.equals("pptx") || end.equals("ppt")) { type = "application/vnd.ms-powerpoint"; } else if (end.equals("docx") || end.equals("doc")) { type = "application/vnd.ms-word"; } else if (end.equals("xlsx") || end.equals("xls")) { type = "application/vnd.ms-excel"; } else if (end.equals("txt")) { type = "text/plain"; } else if (end.equals("html") || end.equals("htm")) { type = "text/html"; } else { //如果无法直接打开,就跳出软件列表给用户选择 type = "*/*"; } return type; }

至此,用手机第三方打开WPS已经实现了

接下来实现方案三的功能,参考大佬博客:https://blog.csdn.net/Andy_l1/article/details/78218078?locationNum=5&fps=1 效果图如下

tbs内置浏览office.gif

腾讯浏览服务官网地址 现在引用下腾讯官方文档说明 与贴上它在Android浏览器的文件能力支持情况

x5相关说明.png office_支持文档.png

目前TSB还不支持在线预览功能,只支持本地文件打开。现在将相关的库依赖到开发项目app目录下的gradle中

api 'com.tencent.tbs.tbssdk:sdk:43903'

添加网络权限和网络监视管理权限

初始化QBSDK

private void initQBSDK() { QbSdk.initX5Environment(this, new QbSdk.PreInitCallback() { @Override public void onCoreInitFinished() { Log.e("QbSdk", "QbSdk onCoreInitFinished"); } @Override public void onViewInitFinished(boolean b) { ifInitSuccess = b; Log.e("QbSdk", "QbSdk 初始化是否成功:" + b); } }); QbSdk.setDownloadWithoutWifi(true);//设置支持非Wifi下载 }

在docx文件成功写入手机和QbSdk初始化成功后,跳转到新的页面展示

case R.id.btn_openWithTbs: if(!ifInitSuccess){ Toast.makeText(this, "初始化失败,请查看原因", Toast.LENGTH_SHORT).show(); return; } startActivity(new Intent(this,TbsX5ReadOfficeActivity.class) .putExtra("fileUrl",fileUrl) ); break;

现在来看看加载office的TbsX5ReadOfficeActivity,贴上整个类的代码:

public class TbsX5ReadOfficeActivity extends AppCompatActivity implements TbsReaderView.ReaderCallback { RelativeLayout rootRl; private TbsReaderView tbsReaderView; private String fileUrl; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tbs_x5_read_office); fileUrl = getIntent().getStringExtra("fileUrl"); initTbs(); } private void initTbs() { rootRl = findViewById(R.id.rootRl); tbsReaderView = new TbsReaderView(this, this); rootRl.addView(tbsReaderView, new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT)); Bundle bundle = new Bundle(); bundle.putString("filePath", fileUrl); //加载插件保存的路径 bundle.putString("tempPath", Environment.getExternalStorageDirectory() + File.separator + "temp"); boolean b = tbsReaderView.preOpen("docx", false); if (b) { tbsReaderView.openFile(bundle); } } @Override public void onCallBackAction(Integer integer, Object o, Object o1) { } }

大概流程就是通过上个界面传递过来的文件路径,在tbs初始化后将TbsReaderView 添加到RelativeLayout rootRl中做其子View,通过bundle把文件传给x5,打开的事情交由x5处理,tbsReaderView.preOpen("docx", false) 的第一个参数是目标文件格式,启动相应的格式插件去打开。 至此两个方案的实现均以给出,希望能给小伙伴们借鉴思路,如有讲解错误,希望能不吝惜指正。 这是我项目的github地址



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3