From 204e2c6f0f222f0d5fea413853840790b2d5afd7 Mon Sep 17 00:00:00 2001 From: ZhanGSKen Date: Thu, 27 Nov 2025 11:30:27 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E6=B5=8F=E8=A7=88=E5=99=A8?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- winboll/build.properties | 4 +- winboll/src/main/AndroidManifest.xml | 4 +- .../winboll/studio/winboll/MainActivity.java | 19 +- .../winboll/fragments/BrowserFragment.java | 228 +++++++++++++++++ .../studio/winboll/views/WinBoLLView.java | 232 ++++++++++++++++++ .../src/main/res/drawable/bg_browser_top.xml | 7 + winboll/src/main/res/drawable/bg_edittext.xml | 7 + .../main/res/drawable/progress_bar_style.xml | 8 + .../src/main/res/layout/fragment_browser.xml | 96 ++++++++ .../main/res/xml/network_security_config.xml | 17 +- 10 files changed, 603 insertions(+), 19 deletions(-) create mode 100644 winboll/src/main/java/cc/winboll/studio/winboll/fragments/BrowserFragment.java create mode 100644 winboll/src/main/java/cc/winboll/studio/winboll/views/WinBoLLView.java create mode 100644 winboll/src/main/res/drawable/bg_browser_top.xml create mode 100644 winboll/src/main/res/drawable/bg_edittext.xml create mode 100644 winboll/src/main/res/drawable/progress_bar_style.xml create mode 100644 winboll/src/main/res/layout/fragment_browser.xml diff --git a/winboll/build.properties b/winboll/build.properties index 8e7a6db9..072d7a36 100644 --- a/winboll/build.properties +++ b/winboll/build.properties @@ -1,8 +1,8 @@ #Created by .winboll/winboll_app_build.gradle -#Thu Nov 27 10:54:58 HKT 2025 +#Thu Nov 27 03:28:47 GMT 2025 stageCount=2 libraryProject= baseVersion=15.11 publishVersion=15.11.1 -buildCount=0 +buildCount=8 baseBetaVersion=15.11.2 diff --git a/winboll/src/main/AndroidManifest.xml b/winboll/src/main/AndroidManifest.xml index 163eb03b..43c11cef 100644 --- a/winboll/src/main/AndroidManifest.xml +++ b/winboll/src/main/AndroidManifest.xml @@ -19,7 +19,9 @@ android:label="@string/app_name" android:theme="@style/MyAppTheme" android:resizeableActivity="true" - android:name=".App"> + android:name=".App" + android:usesCleartextTraffic="true" + android:networkSecurityConfig="@xml/network_security_config"> + * @Date 2025/11/27 11:09 + * @Describe 浏览器Fragment(Java 7 语法完整版) + * 适配Java 7特性,移除Lambda/方法引用,兼容低版本Android系统 + */ +public class BrowserFragment extends Fragment implements View.OnClickListener, WinBoLLView.OnPageStatusListener { + + // 控件声明(Java 7 成员变量显式声明) + private EditText mEtUrl; + private Button mBtnLoad; + private Button mBtnRefresh; + private Button mBtnStop; + private Button mBtnForward; + private Button mBtnBack; + private ProgressBar mProgressBar; + private WinBoLLView mWinBoLLView; + + // 单例创建方法(Java 7 静态工厂模式) + public static BrowserFragment newInstance() { + return new BrowserFragment(); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + // 加载布局(Java 7 显式强转,无菱形语法) + View view = inflater.inflate(R.layout.fragment_browser, container, false); + // 初始化控件 + initViews(view); + // 绑定事件 + initEvents(); + // 初始化WinBoLLView + initWinBoLLView(); + return view; + } + + /** + * 初始化控件(Java 7 显式绑定,无Stream简化) + */ + private void initViews(View view) { + mEtUrl = (EditText) view.findViewById(R.id.et_url); + mBtnLoad = (Button) view.findViewById(R.id.btn_load); + mBtnRefresh = (Button) view.findViewById(R.id.btn_refresh); + mBtnStop = (Button) view.findViewById(R.id.btn_stop); + mBtnForward = (Button) view.findViewById(R.id.btn_forward); + mBtnBack = (Button) view.findViewById(R.id.btn_back); + mProgressBar = (ProgressBar) view.findViewById(R.id.progress_bar); + mWinBoLLView = (WinBoLLView) view.findViewById(R.id.winboll_webview); + } + + /** + * 绑定点击事件(Java 7 匿名内部类,无Lambda) + */ + private void initEvents() { + // 功能按钮点击事件 + mBtnLoad.setOnClickListener(this); + mBtnRefresh.setOnClickListener(this); + mBtnStop.setOnClickListener(this); + mBtnForward.setOnClickListener(this); + mBtnBack.setOnClickListener(this); + + // 输入框软键盘“前往”按钮事件(Java 7 匿名内部类实现) + mEtUrl.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, android.view.KeyEvent event) { + // 处理软键盘“前往”点击 + loadUrlFromInput(); + return true; + } + }); + } + + /** + * 初始化WinBoLLView(Java 7 显式调用,无方法引用) + */ + private void initWinBoLLView() { + // 绑定进度条 + mWinBoLLView.setProgressBar(mProgressBar); + // 设置页面状态监听(this 实现 OnPageStatusListener) + mWinBoLLView.setOnPageStatusListener(this); + // 预加载默认页面(兼容Java 7 字符串拼接) + //String defaultUrl = "https://www.baidu.com"; + String defaultUrl = "https://www.winboll.cc"; + mWinBoLLView.loadUrlSafe(defaultUrl); + mEtUrl.setText(defaultUrl); + } + + /** + * 点击事件处理(Java 7 switch-case 语句,无增强switch) + */ + @Override + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.btn_load) { + // 加载输入框中的URL + loadUrlFromInput(); + } else if (id == R.id.btn_refresh) { + // 刷新当前页面 + mWinBoLLView.refreshPage(); + } else if (id == R.id.btn_stop) { + // 停止页面加载 + mWinBoLLView.stopPageLoad(); + } else if (id == R.id.btn_forward) { + // 前进(无历史则提示) + if (!mWinBoLLView.goForwardSafe()) { + showToast("无前进历史"); + } + } else if (id == R.id.btn_back) { + // 后退(无历史则提示) + if (!mWinBoLLView.goBackSafe()) { + showToast("无后退历史"); + } + } + } + + /** + * 从输入框获取URL并加载(Java 7 显式空值校验) + */ + private void loadUrlFromInput() { + // 空值校验(Java 7 显式判断,无Objects.requireNonNull) + if (mEtUrl == null) { + showToast("控件初始化失败"); + return; + } + String url = mEtUrl.getText().toString().trim(); + // 调用WinBoLLView安全加载方法 + mWinBoLLView.loadUrlSafe(url); + // 隐藏软键盘 + hideSoftKeyboard(); + } + + /** + * 隐藏软键盘(Java 7 显式获取系统服务,无Lambda简化) + */ + private void hideSoftKeyboard() { + if (getActivity() == null) { + return; + } + // 获取InputMethodManager(Java 7 显式强转) + InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(android.content.Context.INPUT_METHOD_SERVICE); + if (imm != null && getActivity().getCurrentFocus() != null) { + imm.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(), 0); + } + } + + /** + * 显示Toast提示(Java 7 简化封装,避免重复代码) + */ + private void showToast(String msg) { + if (getActivity() == null || msg == null) { + return; + } + // Java 7 显式创建Toast,无Toast.makeText简化链式调用 + Toast toast = Toast.makeText(getActivity(), msg, Toast.LENGTH_SHORT); + toast.show(); + } + + // ------------------- WinBoLLView.OnPageStatusListener 实现(Java 7 显式重写) ------------------- + @Override + public void onPageStarted(String url) { + // 页面开始加载:更新输入框URL(Java 7 显式非空判断) + if (mEtUrl != null && url != null) { + mEtUrl.setText(url); + } + } + + @Override + public void onPageFinished(String url) { + // 页面加载完成:更新输入框URL + if (mEtUrl != null && url != null) { + mEtUrl.setText(url); + } + } + + @Override + public void onPageError(String errorMsg) { + // 页面加载错误:显示错误提示 + showToast("加载失败:" + errorMsg); + } + + // ------------------- 生命周期管理(防止内存泄漏,Java 7 显式重写) ------------------- + @Override + public void onDestroyView() { + super.onDestroyView(); + // 销毁WinBoLLView(释放资源,避免内存泄漏) + if (mWinBoLLView != null) { + mWinBoLLView.destroyWebView(); + mWinBoLLView = null; + } + // 置空控件(帮助GC回收) + mEtUrl = null; + mBtnLoad = null; + mBtnRefresh = null; + mBtnStop = null; + mBtnForward = null; + mBtnBack = null; + mProgressBar = null; + } + + @Override + public void onDestroy() { + super.onDestroy(); + // 彻底释放资源 + if (mWinBoLLView != null) { + mWinBoLLView.destroyWebView(); + mWinBoLLView = null; + } + } +} + diff --git a/winboll/src/main/java/cc/winboll/studio/winboll/views/WinBoLLView.java b/winboll/src/main/java/cc/winboll/studio/winboll/views/WinBoLLView.java new file mode 100644 index 00000000..d4dc6f53 --- /dev/null +++ b/winboll/src/main/java/cc/winboll/studio/winboll/views/WinBoLLView.java @@ -0,0 +1,232 @@ +package cc.winboll.studio.winboll.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.webkit.WebChromeClient; +import android.webkit.WebResourceError; +import android.webkit.WebResourceRequest; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.ProgressBar; + +/** + * @Author ZhanGSKen&豆包大模型 + * @Date 2025/11/27 11:05 + * @Describe 自定义WebView控件(WinBoLLView) + * 集成进度监听、页面加载控制、安全配置等核心能力 + */ +public class WinBoLLView extends WebView { + public static final String TAG = "WinBoLLView"; + + private ProgressBar mProgressBar; // 页面加载进度条(可选,增强用户体验) + private OnPageStatusListener mStatusListener; // 页面状态监听回调 + private boolean mIsLoading = false; // 自定义加载状态标记(替代系统isLoading()) + + // 构造方法(兼容代码创建和XML布局引用) + public WinBoLLView(Context context) { + super(context); + initWebViewSettings(); + initWebViewClient(); + } + + public WinBoLLView(Context context, AttributeSet attrs) { + super(context, attrs); + initWebViewSettings(); + initWebViewClient(); + } + + public WinBoLLView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initWebViewSettings(); + initWebViewClient(); + } + + /** + * 初始化WebView基础配置(安全+性能+兼容性) + */ + private void initWebViewSettings() { + WebSettings settings = getSettings(); + + // 基础功能配置 + settings.setJavaScriptEnabled(true); // 启用JS(根据需求决定是否开启) + settings.setSupportZoom(true); // 支持缩放 + settings.setBuiltInZoomControls(true); // 显示缩放控件 + settings.setDisplayZoomControls(false); // 隐藏系统缩放控件(优化UI) + settings.setLoadWithOverviewMode(true); // 自适应屏幕 + settings.setUseWideViewPort(true); // 支持宽视角 + + // 缓存配置(提升加载速度) + settings.setCacheMode(WebSettings.LOAD_DEFAULT); + settings.setDomStorageEnabled(true); // 启用DOM存储 + settings.setDatabaseEnabled(true); // 启用数据库存储 + + // 安全配置(防止XSS和恶意跳转) + settings.setJavaScriptCanOpenWindowsAutomatically(false); // 禁止JS自动打开窗口 + setBackgroundColor(0x00000000); // 透明背景(避免白屏闪烁) + + // 适配HTTPS和HTTP混合内容(Android 5.0+) + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + settings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); + } + } + + /** + * 初始化WebViewClient和ChromeClient(控制页面加载和进度) + */ + private void initWebViewClient() { + // 控制页面跳转(不打开系统浏览器) + setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + // 拦截URL加载,使用当前WebView打开 + view.loadUrl(request.getUrl().toString()); + return true; + } + + @Override + public void onPageStarted(WebView view, String url, android.graphics.Bitmap favicon) { + super.onPageStarted(view, url, favicon); + // 页面开始加载:更新自定义加载状态标记 + mIsLoading = true; + // 更新进度条+回调状态 + if (mProgressBar != null) mProgressBar.setVisibility(VISIBLE); + if (mStatusListener != null) mStatusListener.onPageStarted(url); + } + + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + // 页面加载完成:更新自定义加载状态标记 + mIsLoading = false; + // 隐藏进度条+回调状态 + if (mProgressBar != null) mProgressBar.setVisibility(GONE); + if (mStatusListener != null) mStatusListener.onPageFinished(url); + } + + @Override + public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { + super.onReceivedError(view, request, error); + // 页面加载错误:更新自定义加载状态标记 + mIsLoading = false; + // 回调错误信息 + if (mStatusListener != null) { + String errorMsg = error.getDescription().toString(); + mStatusListener.onPageError(errorMsg); + } + } + }); + + // 监听页面加载进度 + setWebChromeClient(new WebChromeClient() { + @Override + public void onProgressChanged(WebView view, int newProgress) { + super.onProgressChanged(view, newProgress); + // 更新进度条进度 + if (mProgressBar != null) mProgressBar.setProgress(newProgress); + } + }); + } + + // ------------------- 对外暴露的控制方法(供Fragment调用) ------------------- + /** + * 加载URL(增加空值校验+优先HTTPS协议) + */ + public void loadUrlSafe(String url) { + if (url == null || url.trim().isEmpty()) { + if (mStatusListener != null) mStatusListener.onPageError("URL不能为空"); + return; + } + // 协议补全:优先HTTPS,兼容HTTP(解决ERR_CLEARTEXT_NOT_PERMITTED) + if (!url.startsWith("http://") && !url.startsWith("https://")) { + url = "https://" + url; // 改为优先HTTPS,而非HTTP + } + super.loadUrl(url); + } + + /** + * 刷新当前页面(使用自定义mIsLoading判断加载状态) + */ + public void refreshPage() { + if (mIsLoading) { // 替换原isLoading(),使用自定义标记 + stopLoading(); // 若正在加载,先停止再刷新 + } + reload(); + } + + /** + * 停止页面加载(使用自定义mIsLoading判断加载状态) + */ + public void stopPageLoad() { + if (mIsLoading) { // 替换原isLoading(),使用自定义标记 + stopLoading(); + mIsLoading = false; // 停止后更新状态标记 + } + } + + /** + * 前进(判断是否有前进历史) + */ + public boolean goForwardSafe() { + if (canGoForward()) { + goForward(); + return true; + } + return false; + } + + /** + * 后退(判断是否有后退历史) + */ + public boolean goBackSafe() { + if (canGoBack()) { + goBack(); + return true; + } + return false; + } + + // ------------------- 辅助功能:进度条和状态监听 ------------------- + /** + * 设置进度条(绑定Fragment中的进度条控件) + */ + public void setProgressBar(ProgressBar progressBar) { + this.mProgressBar = progressBar; + if (mProgressBar != null) { + mProgressBar.setMax(100); + mProgressBar.setVisibility(GONE); + } + } + + /** + * 设置页面状态监听(供Fragment接收加载状态) + */ + public void setOnPageStatusListener(OnPageStatusListener listener) { + this.mStatusListener = listener; + } + + /** + * 页面状态监听接口 + */ + public interface OnPageStatusListener { + void onPageStarted(String url); // 页面开始加载 + void onPageFinished(String url); // 页面加载完成 + void onPageError(String errorMsg); // 页面加载错误 + } + + // ------------------- 资源释放(防止内存泄漏) ------------------- + /** + * 销毁WebView(必须在Fragment销毁时调用) + */ + public void destroyWebView() { + // 停止加载并清空历史 + stopLoading(); + mIsLoading = false; // 销毁时重置加载状态 + clearHistory(); + // 移除所有WebViewClient(避免内存泄漏) + setWebViewClient(null); + setWebChromeClient(null); + // 销毁WebView + destroy(); + } +} diff --git a/winboll/src/main/res/drawable/bg_browser_top.xml b/winboll/src/main/res/drawable/bg_browser_top.xml new file mode 100644 index 00000000..47623988 --- /dev/null +++ b/winboll/src/main/res/drawable/bg_browser_top.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/winboll/src/main/res/drawable/bg_edittext.xml b/winboll/src/main/res/drawable/bg_edittext.xml new file mode 100644 index 00000000..5bf8014c --- /dev/null +++ b/winboll/src/main/res/drawable/bg_edittext.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/winboll/src/main/res/drawable/progress_bar_style.xml b/winboll/src/main/res/drawable/progress_bar_style.xml new file mode 100644 index 00000000..7e8f8a32 --- /dev/null +++ b/winboll/src/main/res/drawable/progress_bar_style.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/winboll/src/main/res/layout/fragment_browser.xml b/winboll/src/main/res/layout/fragment_browser.xml new file mode 100644 index 00000000..04f0e271 --- /dev/null +++ b/winboll/src/main/res/layout/fragment_browser.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + +