package com.v7lin.android.env.webkit; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.util.AttributeSet; import android.view.MotionEvent; import android.webkit.DownloadListener; import android.webkit.WebView; /** * 防止 WebView 调用 destroy 后,继续调用 loadUrl 、loadData 或 loadDataWithBaseURL 方法,引发崩溃 * 原因:destroy 后,WebView 的内核 WebViewCore 将被设置为空 * * WebView 的所有方法都必须在主线程中才能调用 * * Android WebView 依然遵循 dp、sp、px 等单位 * * WebView 内容获取 * view.loadUrl(“javascript:window.handler.show(document.body.innerHTML);”); * * 带图片的 html 不能用 loadData,应用 loadDataWithBaseURL * * @author v7lin E-mail:v7lin@qq.com */ @SuppressWarnings("deprecation") public class CompatWebView extends WebView { private final AtomicBoolean mIsAlive = new AtomicBoolean(true); private WebInitCompat mWebInitCompat; private DownloadListener mDelegateDownloadListener; public CompatWebView(Context context) { super(context); setup(); } public CompatWebView(Context context, AttributeSet attrs) { super(context, attrs); setup(); } public CompatWebView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setup(); } private void setup() { CookieManagerCompat.setAcceptCookie(getContext(), true); CookieManagerCompat.setAcceptFileSchemeCookies(true); mWebInitCompat = WebInitCompat.get(this); mWebInitCompat.setDefaultAttr(); mWebInitCompat.setDefaultSetting(); setDownloadListener(mDefaultDownloadListener); } @Override public void setDownloadListener(DownloadListener listener) { if (listener == mDefaultDownloadListener) { super.setDownloadListener(listener); } else { mDelegateDownloadListener = listener; } } private DownloadListener mDefaultDownloadListener = new DownloadListener() { @Override public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { if (mDelegateDownloadListener != null) { mDelegateDownloadListener.onDownloadStart(url, userAgent, contentDisposition, mimetype, contentLength); } else { Uri uri = Uri.parse(url); Intent intent = new Intent(Intent.ACTION_VIEW, uri); getContext().startActivity(intent); } } }; @TargetApi(Build.VERSION_CODES.FROYO) @Override public void loadUrl(String url, Map<String, String> additionalHttpHeaders) { if (mIsAlive.get()) { super.loadUrl(url, additionalHttpHeaders); } } @Override public void loadUrl(String url) { if (mIsAlive.get()) { super.loadUrl(url); } } @Override public void postUrl(String url, byte[] postData) { if (mIsAlive.get()) { super.postUrl(url, postData); } } @Override public void loadData(String data, String mimeType, String encoding) { if (mIsAlive.get()) { super.loadData(data, mimeType, encoding); } } /** * data 内容 -- %,会报找不到页面错误,页面全是乱码 -- #,会让你的goBack失效,但canGoBack是可以使用的 -- \ 和 ? * 在转换时,会报错,因为它会把\当作转义符来使用,如果用两级转义,也不生效 -- * 遇到中文乱码问题,解决办法:参数传"utf-8",页面的编码格式也必须是utf-8,这样编码统一就不会乱了 */ @Override public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl) { if (mIsAlive.get()) { super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl); } } /** * 是否存在纵向滚动条 * * 当我们做类似上拉加载下一页这样的功能的时候,页面初始的时候需要知道当前WebView是否存在纵向滚动条 * * 如果有,则不加载下一页 * * 如果没有,则加载下一页直到其出现纵向滚动条 */ public boolean existVerticalScrollbar() { return computeVerticalScrollRange() > computeVerticalScrollExtent(); } /** * 是否存在横向滚动条 */ public boolean existHorizontalScrollbar() { return computeHorizontalScrollRange() > computeHorizontalScrollExtent(); } @Override public boolean onTouchEvent(MotionEvent event) { // ViewPager里非首屏WebView点击事件不响应 if (event.getAction() == MotionEvent.ACTION_DOWN) { onScrollChanged(getScrollX(), getScrollY(), getScrollX(), getScrollY()); } return super.onTouchEvent(event); } @Override protected void onScrollChanged(int newX, int newY, int oldX, int oldY) { super.onScrollChanged(newX, newY, oldX, oldY); if (newY != oldY) { // 当前内容高度下从未触发过, 浏览器存在滚动条且滑动到将抵底部位置 if (getContentHeight() * getScale() <= getHeight() + getScrollY()) { } } } @Override public void destroy() { mIsAlive.compareAndSet(true, false); super.destroy(); } }