前言
最近需要在应用内打开pdf,word,excel,这些东西,用的是uniapp开发,在ios上因为ios的浏览器内核本身就支持这些office套件预览,所以直接开就行,android不行,产品又不希望我们是跳外部应用打开该文档,所以只能做一些与android原生交互的操作来着完成需求
如何打开
目前能找到比较好的方案是腾讯的tbs,用的是这个dalao整合的版本:https://github.com/hanlyjiang/AndroidDocumentViewer
该方案的问题以及解决方法
但这个方案不知道为什么腾讯的tbs一直提示插件加载中,一直加载不出来,需要用户手动按一下按钮再点击加载才能正常加载,或者当前页面被销毁,然后重新回来,才能正常加载,对此我的计划是进来页面后,延迟一段时间自动触发一下取消加载,再自动触发一下加载按钮,让可以正常加载先,但找这俩按钮爬半天粪坑,这里吧代码交代一下
//在你的tbsReadView读取完文件后调用
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// 获取取消加载按钮
TextView stop = (TextView) getViewChildView(new int[]{0, 1, 0, 0}).getChildAt(1);
stop.performClick();
}
}, 2000);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// 获取重新加载按钮
TextView rest = (TextView) getViewChildView(new int[]{0, 1, 1, 0}).getChildAt(1);
rest.performClick();
}
}, 2500);
// 获取tbs的子view
private ViewGroup getViewChildView(int[] ints) {
ViewGroup v = null;
for (int i : ints) {
Log.e(TAG, "getViewChildView: " + i);
if (v == null) {
v = (ViewGroup) mTbsReaderView.getChildAt(i);
} else {
v = (ViewGroup) v.getChildAt(i);
}
}
return v;
}
完整代码
我吧上面那个项目的TBSFileViewActivity做了修改
package com.hanlyjiang.library.fileviewer.tbs;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.artifex.mupdf.R;
import com.hanlyjiang.library.utils.FileViewerUtils;
import com.tencent.smtt.sdk.TbsReaderView;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* 此 Activity 使用 TBS (腾讯浏览服务)查看文件
* <br/> 默认支持常见文件类型
* <br/> <b>默认支持类型:</b>
* <li>doc</li>
* <li>docx</li>
* <li>ppt</li>
* <li>pptx</li>
* <li>xls</li>
* <li>xlsx</li>
* <li>txt</li>
* <li>pdf</li>
* <li>epub</li>
* <br/>
* intent参数: filePath - 文件路径
*
* @author hanlyjiang
*/
public class TBSFileViewActivity extends AppCompatActivity implements TbsReaderView.ReaderCallback {
public static final String FILE_PATH = "filePath";
public static final String FILE_NAME = "title";
private static final String TAG = "TBSFileViewActivity";
private TbsReaderView mTbsReaderView;
private FrameLayout rootViewParent;
private ViewGroup errorHandleLayout;
private String filePath;
private String fileName;
public static void viewFile(Context context, String localPath, String fileName) {
Intent intent = new Intent(context, TBSFileViewActivity.class);
intent.putExtra(FILE_PATH, localPath);
intent.putExtra(FILE_NAME, fileName);
context.startActivity(intent);
}
public static String getFileName(String filePath) {
if (filePath == null) {
return "";
}
int lastSlashIndex = filePath.lastIndexOf("/") + 1;
if (lastSlashIndex == -1) {
return filePath;
}
int lastDotFromSlashIndex = filePath.indexOf(".", lastSlashIndex);
if (lastDotFromSlashIndex == -1) {
return filePath.substring(lastSlashIndex);
}
return filePath.substring(lastSlashIndex, lastDotFromSlashIndex);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tbs_file_view_layout);
rootViewParent = (FrameLayout) findViewById(R.id.fl_rootview);
errorHandleLayout = (ViewGroup) findViewById(R.id.ll_error_handle);
initErrorHandleLayout(errorHandleLayout);
handleIntent();
if (TextUtils.isEmpty(filePath) || !new File(filePath).isFile()) {
Toast.makeText(this, getString(R.string.file_not_exist), Toast.LENGTH_SHORT).show();
finish();
}
if (fileName != null && !fileName.equals("")) {
getSupportActionBar().setTitle(fileName);
} else {
getSupportActionBar().setTitle(getFileName(filePath));
}
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mTbsReaderView = new TbsReaderView(this, this);
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
);
mTbsReaderView.setLayoutParams(layoutParams);
rootViewParent.addView(mTbsReaderView);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// 获取取消加载按钮
TextView stop = (TextView) getViewChildView(new int[]{0, 1, 0, 0}).getChildAt(1);
stop.performClick();
}
}, 2000);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// 获取重新加载按钮
TextView rest = (TextView) getViewChildView(new int[]{0, 1, 1, 0}).getChildAt(1);
rest.performClick();
}
}, 2500);
displayFile(filePath);
}
private void initErrorHandleLayout(ViewGroup errorHandleLayout) {
findViewById(R.id.btn_retry_with_tbs).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
displayFile(filePath);
}
});
findViewById(R.id.btn_view_with_other_app).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FileViewerUtils.viewFile4_4(v.getContext(), filePath);
}
});
}
// 获取tbs的子view
private ViewGroup getViewChildView(int[] ints) {
ViewGroup v = null;
for (int i : ints) {
Log.e(TAG, "getViewChildView: " + i);
if (v == null) {
v = (ViewGroup) mTbsReaderView.getChildAt(i);
} else {
v = (ViewGroup) v.getChildAt(i);
}
}
return v;
}
// 遍历view,和它的子view,方便查看那个按钮在那一行
private void getViews(ViewGroup view, HashMap<String, List<View>> map, String id) {
List<View> l = new ArrayList<>();
for (int i = 0; i < view.getChildCount(); i++) {
l.add(view.getChildAt(i));
if (view.getChildAt(i) instanceof ViewGroup) {
getViews((ViewGroup) view.getChildAt(i), map, id + i);
}
if (view.getChildAt(i) instanceof TextView) {
Log.e("hehe", "getViews: " + ((TextView) view.getChildAt(i)).getText().toString() + "//" + id + "//" + i);
}
}
map.put(id, l);
}
private void handleIntent() {
if (getIntent() != null) {
filePath = getIntent().getStringExtra(FILE_PATH);
fileName = getIntent().getStringExtra(FILE_NAME);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onCallBackAction(Integer integer, Object long1, Object long2) {
Log.d(TAG, "onCallBackAction " + integer + "," + long1 + "," + long2);
}
private void displayFile(String fileAbsPath) {
Bundle bundle = new Bundle();
bundle.putString("filePath", fileAbsPath);
bundle.putString("tempPath", Environment.getExternalStorageDirectory().getPath());
// preOpen 需要文件后缀名 用以判断是否支持
boolean result = mTbsReaderView.preOpen(parseFormat(fileAbsPath), true);
if (result) {
mTbsReaderView.openFile(bundle);
mTbsReaderView.setVisibility(View.VISIBLE);
errorHandleLayout.setVisibility(View.GONE);
} else {
mTbsReaderView.setVisibility(View.GONE);
errorHandleLayout.setVisibility(View.VISIBLE);
}
}
private String parseFormat(String fileName) {
return fileName.substring(fileName.lastIndexOf(".") + 1);
}
@Override
protected void onDestroy() {
super.onDestroy();
mTbsReaderView.onStop();
}
}