效果图:
上图附件下的fragment内显示的就是pdf
前言:
兼容android 8.0,直接获取服务端返的url(http.....pdf)直接打开。
第一步:
在assets文件下,粘贴过去pdfjs的整个包。(在文章尾部提供)
第二步:
<!-- 指纹及手势密码的权限 -->
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="com.fingerprints.service.ACCESS_FINGERPRINT_MANAGER" />
<uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
<!-- 计歩额外用的权限 -->
<uses-feature android:name="android.hardware.sensor.accelerometer" />
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- Android8.0以后需要加这个权限,不然闪退 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- 计歩使用 -->
<uses-feature
android:name="android.hardware.sensor.stepcounter"
android:required="true" />
<uses-feature
android:name="android.hardware.sensor.stepdetector"
android:required="true" />
<!-- 允许应用程序改变网络状态 -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<!-- 允许应用程序改变WIFI连接状态 -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!-- 允许应用程序访问有关的网络信息 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 允许应用程序访问WIFI网卡的网络信息 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 连接网络权限,用于执行云端语音能力 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 获取手机录音机使用权限,听写、识别、语义理解需要用到此权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- 读取手机信息权限 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 读取联系人权限,上传联系人需要用到此权限 -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<!-- 外存储写权限,构建语法需要用到此权限 -->
<uses-permission android:name="android.permission.VIBRATE" />
<!-- 外存储读权限,构建语法需要用到此权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 定位信息是敏感信息,可通过Setting.setLocationEnable(false)关闭定位请求 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- 拍照权限开始 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 读写权限开始 -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission
android:name="android.permission.CHANGE_CONFIGURATION"
tools:ignore="ProtectedPermissions" />
<!-- 蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission
android:name="android.permission.BLUETOOTH_PRIVILEGED"
tools:ignore="ProtectedPermissions" />
<!-- 允许应用程序完全使用网络 -->
<uses-permission
android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
<!-- 配置权限,用来记录应用配置信息 -->
<uses-permission
android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<!-- 微信/支付宝支付权限 -->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--版本更新安卓8.0需要的权限-->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!-- 从Android 9.0(API级别28)开始,默认情况下禁用明文支持。因此http的url均无法在webView中加载,设置usesCleartextTraffic -->
权限为了简便,我就直接粘贴了常用的。大家自行合理删减
在application的地方设置属性,红线标识的位置。
同时,安卓6.0以后的 动态配置权限也要执行。
第三步:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<paths>
<external-path
name="download"
path=""/>
</paths>
<external-path
name="files_root"
path="Android/data/com.yc.stscf/" />
<external-path
name="external_storage_root"
path="." />
<external-path
name="my_images"
path="Android/data/com.yc.stscf/files/Pictures" />
<root-path
name="root_path"
path="." />
</resources>
大家根据自己的包名去更改。
第四步: (开始写内容)
布局:
<?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="@color/white"> <com.yc.stscf.widget.ExtendedWebView
android:id="@+id/view_web"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
很简单的放个自定义的webView,采用自定义的是因为这个界面在viewpager中滑动冲突
自定义控件:
package com.yc.stscf.widget;
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.webkit.WebView;
/**
* ================================================
*
* @author :Vip
* @version :V 1.0.0
* @date :2019/7/24 16:51
* 描 述:主要解决viewPager嵌套webView横向滚动问题
* 修订历史:
* ================================================
*/
public class ExtendedWebView extends WebView {
private boolean isScrollX = false;
public ExtendedWebView(Context context) {
super(context);
}
public ExtendedWebView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent event) {
if (MotionEventCompat.getPointerCount(event) == 1) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
isScrollX = false;
//事件由webView处理
getParent().getParent()
.requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
//嵌套Viewpager时
getParent().getParent()
.requestDisallowInterceptTouchEvent(!isScrollX);
break;
default:
getParent().getParent()
.requestDisallowInterceptTouchEvent(false);
}
} else {
//使webView可以双指缩放(前提是webView必须开启缩放功能,并且加载的网页也支持缩放)
getParent().getParent().
requestDisallowInterceptTouchEvent(true);
}
return super.onTouchEvent(event);
}
/**
* 当webView滚动到边界时执行
**/
@Override
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
isScrollX = clampedX;
}
}
代码:
package com.yc.stscf.fragment.contract;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.yc.stscf.R;
import com.yc.stscf.base.BaseFragment;
import com.yc.stscf.widget.ExtendedWebView;
import butterknife.BindView;
/**
* ================================================
*
* @author :Vip
* @version :V 1.0.0
* @date :2019/7/9 15:47
* 描 述:pdf展示
* 修订历史:
* ================================================
*/
public class ContractEnclosureFragment extends BaseFragment {
@BindView(R.id.view_web)
ExtendedWebView pdfShowWebView;
/**
* 总布局
**/
private View view = null;
/**
* 标志位,标志已经初始化完成
**/
private boolean isPrepared;
@SuppressLint("InflateParams")
@Override
protected View initLayout(LayoutInflater inflater, ViewGroup container, boolean b) {
view = inflater.inflate(R.layout.fragment_contract_enclosure, null);
isPrepared = true;
return view;
}
@SuppressLint("SetJavaScriptEnabled")
@Override
protected void initView(Bundle savedInstanceState) {
pdfShowWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// 返回值是true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器
view.loadUrl(url);
return true;
}
});
WebSettings settings = pdfShowWebView.getSettings();
settings.setSavePassword(false);
settings.setJavaScriptEnabled(true);
settings.setAllowFileAccessFromFileURLs(true);
settings.setAllowUniversalAccessFromFileURLs(true);
settings.setBuiltInZoomControls(true);
pdfShowWebView.requestDisallowInterceptTouchEvent(true);
String mPdfFilePath = "http://114.115.152.132:80/lessonDoc/1540185667975.pdf";
pdfShowWebView.setWebChromeClient(new WebChromeClient());
// if (!"".equals(mPdfFilePath)) {
// byte[] bytes = null;
// try {// 获取以字符编码为utf-8的字符
// bytes = mPdfFilePath.getBytes("UTF-8");
// } catch (UnsupportedEncodingException e) {
// e.printStackTrace();
// }
// if (bytes != null) {
// // BASE64转码
// mPdfFilePath = new Base64Encoder().encode(bytes);
// }
// }
//TODO 采用无样式的,则引用index.html?不需要file=,并且mPdfFilePath不需要转码。
pdfShowWebView.loadUrl("file:///android_asset/pdfjs/web/index.html?" + mPdfFilePath);
//TODO 采用有样式的,则引用 viewer.html?file=,需要mPdfFilePath转码
// pdfShowWebView.loadUrl("file:///android_asset/pdfjs/web/viewer.html?file=" + mPdfFilePath);
}
@Override
protected void lazyLoad() {
if (!isPrepared || !isVisible) {
return;
}
}
}
baseFragment是我自定义的,可以采用原始的fragment去继承。
采用了2种方式。在pdf的资源内已经继承完毕。分为1:全屏的仅仅显示pdf,2:pdf顶部带导航栏,放大缩小快速跳转定位,打印等。 建议方式一,方式一的样式更加美观。方式二是原生的,样式会丑些,当然也是可以修改的,需要掌握一定前端功底,改css样式。 在代码中已经描述了。方式二级(采用有样式的),将注释掉的放开、Base64Encoder是做转码用的,不适用的话加载不出来
配合在线显示pdf的类
package com.yc.stscf.utils;
import java.io.IOException;
import java.io.OutputStream;
/**
* ================================================
*
* @author:Vip 版 本:V4.1.4
* 创建日期:2018/6/29
* 描 述:配合在线预览pdf
* 修订历史:
* ================================================
*/
public class Base64Encoder extends CharacterEncoder {
@Override
protected int bytesPerAtom() {
return 3;
}
@Override
protected int bytesPerLine() {
return 57;
}
private static final char[] PEM_ARRAY =
{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a',
'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
'1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', '='};
@Override
protected void encodeAtom(OutputStream paramOutputStream,
byte[] paramArrayOfByte, int paramInt1, int paramInt2)
throws IOException {
int i;
int j;
int k;
if (paramInt2 == 1) {
i = paramArrayOfByte[paramInt1];
j = 0;
k = 0;
paramOutputStream.write(PEM_ARRAY[(i >>> 2 & 0x3F)]);
paramOutputStream
.write(PEM_ARRAY[((i << 4 & 0x30) + (j >>> 4 & 0xF))]);
paramOutputStream.write(61);
paramOutputStream.write(61);
} else if (paramInt2 == 2) {
i = paramArrayOfByte[paramInt1];
j = paramArrayOfByte[(paramInt1 + 1)];
k = 0;
paramOutputStream.write(PEM_ARRAY[(i >>> 2 & 0x3F)]);
paramOutputStream
.write(PEM_ARRAY[((i << 4 & 0x30) + (j >>> 4 & 0xF))]);
paramOutputStream
.write(PEM_ARRAY[((j << 2 & 0x3C) + (k >>> 6 & 0x3))]);
paramOutputStream.write(61);
} else {
i = paramArrayOfByte[paramInt1];
j = paramArrayOfByte[(paramInt1 + 1)];
k = paramArrayOfByte[(paramInt1 + 2)];
paramOutputStream.write(PEM_ARRAY[(i >>> 2 & 0x3F)]);
paramOutputStream
.write(PEM_ARRAY[((i << 4 & 0x30) + (j >>> 4 & 0xF))]);
paramOutputStream
.write(PEM_ARRAY[((j << 2 & 0x3C) + (k >>> 6 & 0x3))]);
paramOutputStream.write(PEM_ARRAY[(k & 0x3F)]);
}
}
}
父类
package com.yc.stscf.utils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.ByteBuffer;
/**
* ================================================
*
* @author:Vip 版 本:V4.1.4
* 创建日期:2018/6/29
* 描 述:配合在线预览pdf
* 修订历史:
* ================================================
*/
public abstract class CharacterEncoder {
private PrintStream pStream;
protected abstract int bytesPerAtom();
protected abstract int bytesPerLine();
private void encodeBufferPrefix(OutputStream paramOutputStream)
throws IOException {
this.pStream = new PrintStream(paramOutputStream);
}
private void encodeBufferSuffix(OutputStream paramOutputStream)
throws IOException {
}
private void encodeLinePrefix(OutputStream paramOutputStream, int paramInt)
throws IOException {
}
private void encodeLineSuffix(OutputStream paramOutputStream)
throws IOException {
this.pStream.println();
}
protected abstract void encodeAtom(OutputStream paramOutputStream, byte[] paramArrayOfByte, int paramInt1, int paramInt2)
throws IOException;
private int readFully(InputStream paramInputStream, byte[] paramArrayOfByte)
throws IOException {
for (int i = 0; i < paramArrayOfByte.length; i++) {
int j = paramInputStream.read();
if (j == -1) {
return i;
}
paramArrayOfByte[i] = ((byte) j);
}
return paramArrayOfByte.length;
}
private void encode(InputStream paramInputStream, OutputStream paramOutputStream)
throws IOException {
byte[] arrayOfByte = new byte[bytesPerLine()];
encodeBufferPrefix(paramOutputStream);
for (; ; ) {
int j = readFully(paramInputStream, arrayOfByte);
if (j == 0) {
break;
}
encodeLinePrefix(paramOutputStream, j);
for (int i = 0; i < j; i += bytesPerAtom()) {
if (i + bytesPerAtom() <= j) {
encodeAtom(paramOutputStream, arrayOfByte, i, bytesPerAtom());
} else {
encodeAtom(paramOutputStream, arrayOfByte, i, j - i);
}
}
if (j < bytesPerLine()) {
break;
}
encodeLineSuffix(paramOutputStream);
}
encodeBufferSuffix(paramOutputStream);
}
private void encode(byte[] paramArrayOfByte, OutputStream paramOutputStream)
throws IOException {
ByteArrayInputStream localByteArrayInputStream = new ByteArrayInputStream(paramArrayOfByte);
encode(localByteArrayInputStream, paramOutputStream);
}
public String encode(byte[] paramArrayOfByte) {
ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();
ByteArrayInputStream localByteArrayInputStream = new ByteArrayInputStream(paramArrayOfByte);
String str = null;
try {
encode(localByteArrayInputStream, localByteArrayOutputStream);
str = localByteArrayOutputStream.toString("8859_1");
} catch (Exception localException) {
throw new Error("CharacterEncoder.encode internal error");
}
return str;
}
private byte[] getBytes(ByteBuffer paramByteBuffer) {
byte[] localObject = null;
if (paramByteBuffer.hasArray()) {
byte[] arrayOfByte = paramByteBuffer.array();
if ((arrayOfByte.length == paramByteBuffer.capacity()) && (arrayOfByte.length == paramByteBuffer.remaining())) {
localObject = arrayOfByte;
paramByteBuffer.position(paramByteBuffer.limit());
}
}
if (localObject == null) {
localObject = new byte[paramByteBuffer.remaining()];
paramByteBuffer.get((byte[]) localObject);
}
return localObject;
}
public void encode(ByteBuffer paramByteBuffer, OutputStream paramOutputStream)
throws IOException {
byte[] arrayOfByte = getBytes(paramByteBuffer);
encode(arrayOfByte, paramOutputStream);
}
public String encode(ByteBuffer paramByteBuffer) {
byte[] arrayOfByte = getBytes(paramByteBuffer);
return encode(arrayOfByte);
}
private void encodeBuffer(InputStream paramInputStream, OutputStream paramOutputStream)
throws IOException {
byte[] arrayOfByte = new byte[bytesPerLine()];
encodeBufferPrefix(paramOutputStream);
for (; ; ) {
int j = readFully(paramInputStream, arrayOfByte);
if (j != 0) {
encodeLinePrefix(paramOutputStream, j);
for (int i = 0; i < j; i += bytesPerAtom()) {
if (i + bytesPerAtom() <= j) {
encodeAtom(paramOutputStream, arrayOfByte, i, bytesPerAtom());
} else {
encodeAtom(paramOutputStream, arrayOfByte, i, j - i);
}
}
encodeLineSuffix(paramOutputStream);
if (j < bytesPerLine()) {
break;
}
}
}
encodeBufferSuffix(paramOutputStream);
}
private void encodeBuffer(byte[] paramArrayOfByte, OutputStream paramOutputStream)
throws IOException {
ByteArrayInputStream localByteArrayInputStream = new ByteArrayInputStream(paramArrayOfByte);
encodeBuffer(localByteArrayInputStream, paramOutputStream);
}
private String encodeBuffer(byte[] paramArrayOfByte) {
ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();
ByteArrayInputStream localByteArrayInputStream = new ByteArrayInputStream(paramArrayOfByte);
try {
encodeBuffer(localByteArrayInputStream, localByteArrayOutputStream);
} catch (Exception localException) {
throw new Error("CharacterEncoder.encodeBuffer internal error");
}
return localByteArrayOutputStream.toString();
}
public void encodeBuffer(ByteBuffer paramByteBuffer, OutputStream paramOutputStream)
throws IOException {
byte[] arrayOfByte = getBytes(paramByteBuffer);
encodeBuffer(arrayOfByte, paramOutputStream);
}
public String encodeBuffer(ByteBuffer paramByteBuffer) {
byte[] arrayOfByte = getBytes(paramByteBuffer);
return encodeBuffer(arrayOfByte);
}
}
最后: pdf包的引用文件放在了我的下载内容里。大家去里面下载吧。我想免费提供。