一手遮天 Android - view(WebView): WebView 拦截 url 跳转,拦截 alert, confirm, prompt 弹出框,拦截文件选择框

项目地址 https://github.com/webabcd/AndroidDemo
作者 webabcd

一手遮天 Android - view(WebView): WebView 拦截 url 跳转,拦截 alert, confirm, prompt 弹出框,拦截文件选择框

示例如下:

/view/webview/WebViewDemo3.java

/**
 * WebView 拦截 url 跳转,拦截 alert, confirm, prompt 弹出框,拦截文件选择框(本例所用 html 请参见 /assets/WebViewDemo3.html)
 *     shouldOverrideUrlLoading() - 拦截任意协议的 url 跳转(通过自定义协议和此特性可以实现 js 调用 android)
 *     onJsAlert() - 拦截 alert 框(WebView 不支持 js 的 alert 框,但是可以通过此特性在 android 中模拟实现 alert 框;也可以通过自定义协议和此特性实现 js 调用 android)
 *     onJsConfirm() - 拦截 confirm 框(WebView 不支持 js 的 confirm 框,但是可以通过此特性在 android 中模拟实现 confirm 框;也可以通过自定义协议和此特性实现 js 调用 android 并返回 bool 值)
 *     onJsPrompt() - 拦截 prompt 框(WebView 不支持 js 的 prompt 框,但是可以通过此特性在 android 中模拟实现 prompt 框;也可以通过自定义协议和此特性实现 js 调用 android 并返回字符串值)
 *     onShowFileChooser() - 拦截文件选择框(WebView 不支持 js 的文件选择框,但是可以通过此特性在 android 中模拟实现文件选择框,然后再将用户选择文件的结果告知给页面)
 */

package com.webabcd.androiddemo.view.webview;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.ViewGroup;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.Toast;

import com.webabcd.androiddemo.R;

import java.util.Set;

public class WebViewDemo3 extends AppCompatActivity {

    private WebView mWebView1;

    private ValueCallback<Uri[]> mFilePathCallback;
    private final static int FILE_CHOOSER_RESULT_CODE = 12345;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_webview_webviewdemo3);

        mWebView1 = findViewById(R.id.webView1);

        sample();
    }

    private void sample() {
        // 启用 javascript 支持
        WebSettings webSettings = mWebView1.getSettings();
        webSettings.setJavaScriptEnabled(true);


        // 加载指定的 url
        mWebView1.loadUrl("file:///android_asset/WebViewDemo3.html");


        mWebView1.setWebViewClient(new WebViewClient() {
            // 通过 shouldOverrideUrlLoading() 拦截 url 跳转
            // 任意协议的 url 跳转都会被拦截,本例演示如何通过此特性来实现 js 调用 android
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                Uri uri = Uri.parse(url);
                // scheme - url 的 scheme,本例需要拦截的 scheme 为 js
                // authority - url 的 authority,本例需要拦截的 authority 为 cn.webabcd.jscallandroid
                if (uri.getScheme().equalsIgnoreCase("js")) {
                    if (uri.getAuthority().equalsIgnoreCase("cn.webabcd.jscallandroid")) {
                        // 通过 url 的 query 来获取 js 传递过来的数据
                        Set<String> parameterNames = uri.getQueryParameterNames();
                        Toast.makeText(WebViewDemo3.this, "android 拦截了 url 跳转 " + uri.getQueryParameter("p1") + uri.getQueryParameter("p2"), Toast.LENGTH_SHORT).show();
                    }

                    // 已处理
                    return true;
                }

                // 这里会返回 false 就是不拦截了
                return super.shouldOverrideUrlLoading(view, url);
            }
        });

        mWebView1.setWebChromeClient(new WebChromeClient() {
            // 通过 onJsAlert() 拦截 alert 框(WebView 不支持 js 的 alert 框,但是可以通过此特性在 android 中模拟实现 alert 框;也可以通过自定义协议和此特性实现 js 调用 android)
            @Override
            public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
                // 通过 AlertDialog 实现一个类似 js 中的 alert 框
                AlertDialog.Builder builder = new AlertDialog.Builder(WebViewDemo3.this);
                builder.setTitle("alert");
                // message 就是 js 中 alert() 传递过来的信息
                builder.setMessage(message);
                builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // 结束 js 中的 alert 框(必须加这句,否则页面就死掉了)
                        result.confirm();
                    }
                });
                builder.setCancelable(false);
                builder.create().show();

                // 已处理
                return true;
            }


            // 通过 onJsConfirm() 拦截 confirm 框(WebView 不支持 js 的 confirm 框,但是可以通过此特性在 android 中模拟实现 confirm 框;也可以通过自定义协议和此特性实现 js 调用 android 并返回 bool 值)
            @Override
            public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
                // 通过 AlertDialog 实现一个类似 js 中的 confirm 框
                AlertDialog.Builder builder = new AlertDialog.Builder(WebViewDemo3.this);
                builder.setTitle("confirm");
                // message 就是 js 中 confirm() 传递过来的信息
                builder.setMessage(message);
                builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // 为 js 中的 confirm 框返回 true
                        result.confirm();
                    }
                });
                builder.setNeutralButton(android.R.string.cancel, new AlertDialog.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // 为 js 中的 confirm 框返回 false
                        result.cancel();
                    }
                });
                builder.setCancelable(false);
                builder.create().show();

                // 已处理
                return true;
            }


            // 通过 onJsPrompt() 拦截 prompt 框(WebView 不支持 js 的 prompt 框,但是可以通过此特性在 android 中模拟实现 prompt 框;也可以通过自定义协议和此特性实现 js 调用 android 并返回字符串值)
            @Override
            public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, final JsPromptResult result) {
                // 通过 AlertDialog 和 EditText 实现一个类似 js 中的 prompt 框
                final EditText editText = new EditText(WebViewDemo3.this);
                AlertDialog.Builder builder = new AlertDialog.Builder(WebViewDemo3.this);
                builder.setTitle("prompt");
                // message 就是 js 中 prompt() 传递过来的信息
                builder.setMessage(message);
                builder.setView(editText);
                builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // 为 js 中的 prompt 框返回数据
                        result.confirm(editText.getText().toString());
                    }
                });
                builder.setCancelable(false);
                builder.create().show();

                // 已处理
                return true;
            }


            // 通过 onShowFileChooser() 拦截文件选择框(WebView 不支持 js 的文件选择框,但是可以通过此特性在 android 中模拟实现文件选择框,然后再将用户选择文件的结果告知给页面)
            // 此方法在 android 5.0 或以上版本支持(android 5.0 以下版本可以重写别的方法)
            @Override
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
                // 保存回调对象,之后获取到用户的选择结果后通过调用此对象的 onReceiveValue() 方法为页面告知结果
                mFilePathCallback = filePathCallback;

                // 弹出文件选择框(选择文件后或退出后会调用 onActivityResult() 方法)
                Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                intent.setType("*/*");
                startActivityForResult(Intent.createChooser(intent, "文件选择"), FILE_CHOOSER_RESULT_CODE);

                return true;
            }
        });
    }

    // 本例重写 onActivityResult() 方法的目的是获取用户在文件选择框中选择文件的结果
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == FILE_CHOOSER_RESULT_CODE) {
            Uri[] result = null;
            // Activity.RESULT_OK - 选择了文件
            // Activity.DONT_FINISH_TASK_WITH_ACTIVITY - 没有选择文件
            if (resultCode == Activity.RESULT_OK) {
                // 将用户的选择结果告知 js 中的文件选择框
                result = new Uri[]{Uri.parse(data.getDataString())};
            }
            mFilePathCallback.onReceiveValue(result);
            mFilePathCallback = null;
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

    // 释放资源
    @Override
    protected void onDestroy() {
        // 从父容器中移除 WebView
        ((ViewGroup) mWebView1.getParent()).removeView(mWebView1);
        // 移除 WebView 内的所有控件
        mWebView1.removeAllViews();
        // destroy() - 销毁
        mWebView1.destroy();
        mWebView1 = null;

        super.onDestroy();
    }
}

/layout/activity_view_webview_webviewdemo3.xml

<?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:orientation="vertical">

    <WebView
        android:id="@+id/webView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

项目地址 https://github.com/webabcd/AndroidDemo
作者 webabcd

posted @ 2021-06-02 08:32  webabcd  阅读(782)  评论(0编辑  收藏  举报