请豆包优化BaseWebView的WebSettings部分后,再修复点击网页链接无反应的Bug。

This commit is contained in:
ZhanGSKen 2025-06-11 03:52:48 +08:00
parent fde4b275f7
commit fa79c3f807
5 changed files with 162 additions and 211 deletions

View File

@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Tue Jun 10 07:12:46 GMT 2025
#Tue Jun 10 19:49:43 GMT 2025
stageCount=0
libraryProject=
baseVersion=15.0
publishVersion=15.0.0
buildCount=11
buildCount=19
baseBetaVersion=15.0.1

View File

@ -2,10 +2,23 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" >
<application>
<application
tools:replace="android:icon"
android:icon="@drawable/ic_winbollbeta">
<!-- Put flavor specific code here -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="cc.winboll.studio.webpagesources.beta.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider"/>
</provider>
</application>
</manifest>

View File

@ -12,6 +12,9 @@
<!-- 修改或删除您共享存储空间中的内容 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- 只能在前台获取精确的位置信息 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<application
android:name=".App"
android:allowBackup="true"
@ -22,6 +25,7 @@
android:resizeableActivity="true"
android:requestLegacyExternalStorage="true"
android:networkSecurityConfig="@xml/network_security_config">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
@ -29,38 +33,65 @@
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="http"/>
<data android:scheme="https"/>
</intent-filter>
<!-- 以下是设置为默认浏览器的关键配置 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:mimeType="text/html" />
<data android:mimeType="application/xhtml+xml" />
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:mimeType="text/html"/>
<data android:mimeType="application/xhtml+xml"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="about" />
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="about"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="javascript" />
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="javascript"/>
</intent-filter>
</activity>
<activity
@ -75,4 +106,4 @@
</application>
</manifest>
</manifest>

View File

@ -1,12 +1,5 @@
package cc.winboll.studio.webpagesources.common;
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2023/06/22 12:57:08
* @Describe 网页客户端视图基础类
* 参考https://blog.csdn.net/Azhuoyanyan/article/details/17531887?app_version=5.15.2&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%2217531887%22%2C%22source%22%3A%22weixin_38986226%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app
*
*/
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
@ -14,8 +7,8 @@ import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.AttributeSet;
import android.view.GestureDetector;
@ -66,71 +59,87 @@ public class BaseWebView extends WebView {
WebChromeClient.FileChooserParams mFileChooserParams;
AuthLoginDialog mAuthLoginDialog;
// AuthenticationBean mLastAuthenticationBean;
/**
* 在java代码里new的时候会用到
* @param context
*/
public BaseWebView(Context context) {
super(context);
}
/**
* 在xml布局文件中使用时自动调用
* @param context
*/
public BaseWebView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
mBaseWebViewHandler = new BaseWebViewHandler();
/*TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.test);
String text = ta.getString(R.styleable.test_text);
setText(text + " ZhanGSKen.CN");
ta.recycle();*/
//setBackgroundResource(R.drawable.custom_edittext_background);
//setMinWidth(100);
//setPadding(10,0,10,0);
//setPaddingRelative(0,0,0,0);
MyWebChromeClient mMyWebChromeClient = new MyWebChromeClient();
setWebChromeClient(mMyWebChromeClient);
mBaseWebViewClient = new BaseWebViewClient();
setWebViewClient(mBaseWebViewClient);
WebSettings mWebSettings = getSettings();
// 设置是否启用安全浏览
// ------------------- 优化后的 WebSettings 配置 -------------------
// 基础功能配置
mWebSettings.setJavaScriptEnabled(true); // 启用JavaScript
mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true); // 允许JS打开新窗口
mWebSettings.setDomStorageEnabled(true); // 启用DOM存储
mWebSettings.setDatabaseEnabled(true); // 启用数据库存储
//mWebSettings.setAppCacheEnabled(true); // 启用应用缓存
String appCachePath = context.getCacheDir().getAbsolutePath();
//mWebSettings.setAppCachePath(appCachePath);
mWebSettings.setCacheMode(WebSettings.LOAD_DEFAULT); // 缓存模式
// 页面渲染与缩放
mWebSettings.setUseWideViewPort(true); // 支持viewport标签
mWebSettings.setLoadWithOverviewMode(true); // 自动加载时适应屏幕
mWebSettings.setSupportZoom(true); // 支持缩放
mWebSettings.setBuiltInZoomControls(true); // 显示缩放控件
mWebSettings.setDisplayZoomControls(false); // 隐藏默认缩放控件
mWebSettings.setDefaultTextEncodingName("utf-8"); // 默认编码
mWebSettings.setMinimumFontSize(12); // 最小字体大小
// 性能优化
mWebSettings.setLoadsImagesAutomatically(true); // 自动加载图片
mWebSettings.setBlockNetworkImage(false); // 不阻止图片加载
mWebSettings.setRenderPriority(WebSettings.RenderPriority.HIGH); // 高渲染优先级
// 混合内容配置Android 5.0+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mWebSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
// 安全配置
mWebSettings.setSafeBrowsingEnabled(true); // 启用安全浏览
mWebSettings.setAllowFileAccess(true); // 允许访问文件URL
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mWebSettings.setAllowFileAccessFromFileURLs(false); // 禁止文件URL访问其他文件
mWebSettings.setAllowUniversalAccessFromFileURLs(false); // 禁止文件URL执行JS
}
// 高级功能可选
mWebSettings.setGeolocationEnabled(true); // 启用地理位置
mWebSettings.setNeedInitialFocus(true); // 获取初始焦点
mWebSettings.setSaveFormData(true); // 保存表单数据
mWebSettings.setEnableSmoothTransition(true); // 平滑过渡动画
// 硬件加速Android 3.0+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
// 字体缩放100%为默认
mWebSettings.setTextZoom(100);
// ------------------- WebSettings 配置结束 -------------------
mWebSettings.setSafeBrowsingEnabled(true);
// 启用JavaScript
mWebSettings.setJavaScriptEnabled(true);
// 设置可以支持缩放
mWebSettings.setSupportZoom(true);
// 设置true,才能让Webivew支持<meta>标签的viewport属性
mWebSettings.setUseWideViewPort(true);
// 设置出现缩放工具
mWebSettings.setBuiltInZoomControls(true);
// 设置隐藏缩放控件
mWebSettings.setDisplayZoomControls(false);
mWebSettings.setDefaultTextEncodingName("utf-8");
// 允许WebView运行JavaScript并开启JavaScript Console以接收日志
mWebSettings.setDomStorageEnabled(true); // 如果有localStorage等需要
mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);
addJavascriptInterface(new JSConsole(mContext), "console");
addJavascriptInterface(new JS(mContext), "local_obj");
// 最小缩放等级
setInitialScale(60);
mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);
//winbollView.loadUrl("https://www.so.com");
mGestureDetector = new GestureDetectorCompat(mContext, new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent e) {
downX = (int) e.getX();
downY = (int) e.getY();
//LogUtils.d(TAG, "downX is " + Integer.toString(downX));
//LogUtils.d(TAG, "downY is " + Integer.toString(downY));
}
});
@ -146,43 +155,19 @@ public class BaseWebView extends WebView {
if (type == WebView.HitTestResult.UNKNOWN_TYPE)
return false;
if (type == WebView.HitTestResult.EDIT_TEXT_TYPE) {
//let TextViewhandles context menu return true;
}
final ItemLongClickedPopWindow itemLongClickedPopWindow = new ItemLongClickedPopWindow(mContext, ItemLongClickedPopWindow.IMAGE_VIEW_POPUPWINDOW, UIUtil.dip2px(mContext, 180), ViewGroup.LayoutParams.WRAP_CONTENT);
// Setup custom handlingdepending on the type
switch (type) {
case WebView.HitTestResult.PHONE_TYPE: // 处理拨号
break;
case WebView.HitTestResult.EMAIL_TYPE: // 处理Email
break;
case WebView.HitTestResult.GEO_TYPE: // TODO
break;
case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
break;
case WebView.HitTestResult.SRC_ANCHOR_TYPE: // 超链接
// Log.d(DEG_TAG, "超链接");
//szUrl = result.getExtra();
//break;
case WebView.HitTestResult.IMAGE_TYPE: // 处理长按图片的菜单项
szUrl = result.getExtra();
//通过GestureDetector获取按下的位置来定位PopWindow显示的位置
itemLongClickedPopWindow.showAtLocation(v, Gravity.TOP | Gravity.LEFT, downX, downY + 10);
break;
default:
break;
}
szUrl = result.getExtra();
itemLongClickedPopWindow.showAtLocation(v, Gravity.TOP | Gravity.LEFT, downX, downY + 10);
itemLongClickedPopWindow.getView(R.id.item_longclicked_copylink)
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 复制链接到剪贴板
// Gets a handle to the clipboard service.
ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
// Creates a new text clip to put on the clipboard
ClipData clip = ClipData.newPlainText("simple text", szUrl);
// Set the clipboard's primary clip.
clipboard.setPrimaryClip(clip);
Toast.makeText(mContext, "Copy to clipboard.", Toast.LENGTH_SHORT).show();
itemLongClickedPopWindow.dismiss();
@ -199,67 +184,12 @@ public class BaseWebView extends WebView {
return true;
}
});
/*setOnCreateContextMenuListener(new View.OnCreateContextMenuListener(){
public void onCreateContextMenu(ContextMenu menu, View arg1,
ContextMenuInfo arg2) {
final HitTestResult hitTestResult = ((WebView) arg1).getHitTestResult();
MenuItem.OnMenuItemClickListener handler = new MenuItem.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case WEB_CopyLink : {
break;
}
case WEB_DownLoadLink : {
// 下载链接地址
new Thread(new LinkDownLoadThread(hitTestResult.getExtra())).start();
break;
}
}
return true;
}
};
// 添加上下文菜单
int resultType = hitTestResult.getType();
if ((resultType == HitTestResult.ANCHOR_TYPE) ||
(resultType == HitTestResult.IMAGE_ANCHOR_TYPE) ||
(resultType == HitTestResult.SRC_ANCHOR_TYPE) ||
(resultType == HitTestResult.SRC_IMAGE_ANCHOR_TYPE)) {
Intent i = new Intent();
//AppLog.d("extrea", result.getExtra());
MenuItem item = menu.add(0, WEB_CopyLink, 0, "Copy Link").setOnMenuItemClickListener(handler);
MenuItem item2 = menu.add(0, WEB_DownLoadLink, 0, "DownLoad Link").setOnMenuItemClickListener(handler);
item.setIntent(i);
//menu.setHeaderTitle(hitTestResult.getExtra());
TextView tv = new TextView(mContext);
tv.setText(hitTestResult.getExtra());
tv.setTextColor(0x8F000000);
tv.setPadding(15, 5, 15, 5);
menu.setHeaderView(tv);
}
}
});
setDownloadListener(new MyDownLoadListener(mContext));*/
}
/**
* 不会自动调用如果有默认style时在第二个构造函数中调用
* @param context
* @param attrs
* @param defStyleAttr
*/
public BaseWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/*public void setLastUrl(String lastUrl) {
this.mszLastUrl = lastUrl;
}*/
public String getLastUrl() {
return _mszLastUrl;
}
@ -269,12 +199,10 @@ public class BaseWebView extends WebView {
Pattern patternHttp = Pattern.compile("(?i)^http[s]{0,1}://", Pattern.CASE_INSENSITIVE);
Matcher matcherHttp = patternHttp.matcher(url);
if (matcherHttp.matches()) {
// 加载普通 Http 协议访问
stopLoading();
loadUrl(url);
return;
} else {
// 加载其他协议类型访问
Pattern pattern = Pattern.compile("^[a-z]+:.*", Pattern.MULTILINE);
Matcher matcher = pattern.matcher(url);
if (matcher.find()) {
@ -303,25 +231,24 @@ public class BaseWebView extends WebView {
private class MyWebChromeClient extends WebChromeClient {
@Override
public void onProgressChanged(WebView view, int newProgress) {
//super.onProgressChanged(view, newProgress);
}
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
setFilePathCallback(filePathCallback);
setFileChooserParams(fileChooserParams);
//ToastUtils.show("onShowFileChooser");
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
((AppCompatActivity)mContext).startActivityForResult(intent, MainActivity.REQUEST_FILE_CHOOSER);
return true;
//return super.onShowFileChooser(webView, filePathCallback, fileChooserParams);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mGestureDetector.onTouchEvent(event);
boolean ret = mGestureDetector.onTouchEvent(event);
LogUtils.d(TAG, String.format("onTouchEvent ret : %s", ret));
return super.onTouchEvent(event);
}
@ -335,7 +262,6 @@ public class BaseWebView extends WebView {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SHOW_DATA:{
//LogUtils.d(TAG, "(String)msg.obj is " + (String)msg.obj);
loadDataWithBaseURL("", (String)msg.obj, "text/html", "UTF-8", "");
break;
}
@ -344,14 +270,9 @@ public class BaseWebView extends WebView {
}
}
/**
* 逻辑处理
* @author linzewu
*/
final class InJavaScriptLocalObj {
@JavascriptInterface
public void getSource(String html) {
//LogUtils.d(TAG, html);
}
}
@ -370,11 +291,6 @@ public class BaseWebView extends WebView {
}
}
/**
* @Author ZhanGSKen@QQ.COM
* @Date 2023/06/22 12:57:08
* @Describe 网页客户端基础类
*/
class BaseWebViewClient extends WebViewClient {
public static final String TAG = "BaseWebViewClient";
@ -387,42 +303,16 @@ public class BaseWebView extends WebView {
mIOnPageFinished.onPageFinished(url);
LogUtils.d(TAG, "Page load finished : " + url);
_mszLastUrl = url;
//ToastUtils.show("onPageFinished");
new Handler(Looper.getMainLooper()).postDelayed(new Runnable(){
@Override
public void run() {
if (mAuthLoginDialog != null) {
mAuthLoginDialog.checkAndSaveLastAuthenticationBean();
mAuthLoginDialog = null;
}
}
}, 1000);
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
// WinBoll Studio 网站证书校验
/*if (SSLUtil.isSSLCertOk(error.getCertificate(), "9F568FCA9E2B3D59FF3BE2F5FF77CB5BFE1D30562D9880D2D354F0DF63A012E1")) {
LogUtils.d(TAG, "WinBoll Studio 网站证书校验通过。");
handler.proceed();
return;
}*/
LogUtils.d(TAG, "onReceivedSslError 0\nerror : " + error.toString());
//LogUtils.d(TAG, "On Received Ssl Error.");
//handler.proceed();
//super.onReceivedSslError(view, handler, error);
}
@Override
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
int statusCode = errorResponse.getStatusCode();
/*if (statusCode == 404) {
// 处理404错误
Log.d("WebView", "Received a 404 error: " + failingUrl);
}*/
LogUtils.d(TAG, "onReceivedHttpError statusCode " + Integer.toString(statusCode));
super.onReceivedHttpError(view, request, errorResponse);
}
@ -431,18 +321,8 @@ public class BaseWebView extends WebView {
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
/*if (errorCode == WebViewClient.ERROR_TIMEOUT) {
LogUtils.d(TAG, "WebViewClient.ERROR_TIMEOUT");
ToastUtils.show("WebViewClient.ERROR_TIMEOUT");
} else if (errorCode == WebViewClient.ERROR_UNKNOWN) {
LogUtils.d(TAG, "WebViewClient.ERROR_UNKNOWN");
ToastUtils.show("WebViewClient.ERROR_UNKNOWN");
}*/
LogUtils.d(TAG, "onReceivedError 1\nerrorCode : " + errorCode
+ "\ndescription :" + description + "\nfailingUrl : " + failingUrl);
//在此处显示加载失败页面
//loadFailure.setVisibility(View.VISIBLE);
}
@Override
@ -462,13 +342,11 @@ public class BaseWebView extends WebView {
@Override
public void onLoadResource(WebView view, String url) {
super.onLoadResource(view, url);
//加载资源
}
@Override
public void onReceivedHttpAuthRequest(WebView view,
HttpAuthHandler handler, String host, String realm) {
//super.onReceivedHttpAuthRequest(view, handler, host, realm);
mAuthLoginDialog = new AuthLoginDialog(view, handler, host, realm);
mAuthLoginDialog.show();
}
@ -476,15 +354,20 @@ public class BaseWebView extends WebView {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
//开始加载页面
LogUtils.d(TAG, "Page load started : " + url);
_mszLastUrl = url;
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//点击链接跳转对网页中超链接按钮的响应
//view.loadUrl(url);
view.loadUrl(url);
return true;
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
// 修复Android 5.0点击无反应的问题
view.loadUrl(request.getUrl().toString());
return true;
}
}
@ -496,4 +379,18 @@ public class BaseWebView extends WebView {
public void setOnPageFinished(IOnPageFinished iOnPageFinished) {
mIOnPageFinished = iOnPageFinished;
}
public class JSConsole {
private Context mContext;
public JSConsole(Context context) {
this.mContext = context;
}
@JavascriptInterface
public void log(String message) {
LogUtils.d(TAG, message);
}
}
}

View File

@ -5,7 +5,17 @@
<application>
<!-- Put flavor specific code here -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="cc.winboll.studio.webpagesources.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider"/>
</provider>
</application>
</manifest>