Android WebView专题

WebView 专题

第一个WebView程序:加载远程网址

  1. Layout添加WebView组件;

    复制代码
    <WebView
            android:id="@+id/webView_first"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
  2. 初始化组件,加载Url;

    java 复制代码
    public class FirstWebViewActivity extends AppCompatActivity{
    
        private WebView webView_first;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_first_web_view);
            webView_first=findViewById(R.id.webView_first);
            webView_first.loadUrl("https://www.baidu.com/");
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            webView_first.removeAllViews();
            webView_first.destroy();
        }
    }
  3. Manifest文件添加网络访问权限

    xml 复制代码
    <uses-permission android:name="android.permission.INTERNET" />
  4. 出现错误:

    复制代码
    NetworkSecurityConfig   com.hymy.webviewstarter              D  No Network Security Config specified, using platform default
    Denied starting an intent without a user gesture, URI

    解决方法:

    res/xml下添加 network_security_config.xml 文件:

    xml 复制代码
    <?xml version="1.0" encoding="utf-8"?>
    <network-security-config> //默认配置:允许明文通信
        <base-config cleartextTrafficPermitted="true" />
    </network-security-config>

    在AndroidManifest.xml中引用

    xml 复制代码
    <application
            android:networkSecurityConfig="@xml/network_security_config"
            ...
  5. 出现错误:

    访问bing.com无法加载封面图片:

    复制代码
    The resource xxx was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it has an appropriate `as` value and it is preloaded intentionally.

    解决方法:

    复制代码
    webView_first=findViewById(R.id.webView_first);
    //启用JavaScript
    WebSettings webSettings = webView_first.getSettings();
    webSettings.setJavaScriptEnabled(true);
    webView_first.loadUrl("https://cn.bing.com/?mkt=zh-CN");
  6. 下载:在bing搜索网站首页,好看的壁纸,点击可以下载图片,将图片下载到 SD 卡下的Downloader目录下

    参考:https://cloud.tencent.com/developer/article/1742327

  7. 将 JavaScript代码绑定到Android代码

    js调用Android代码中的方法

    新建 WebAppInterface 类

    java 复制代码
    /**
     * @Author : alex
     * @Date : on 2023/11/14 09:21.
     * @Description :描述
     */
    public class WebAppInterface {
        Context mContext;
    
        /**
         * 初始化接口,并设置context
         * @param c
         */
        WebAppInterface(Context c){
            mContext=c;
        }
    
        /**
         * 在Web页面显示消息提示
         * @param toast
         */
        @JavascriptInterface
        public void showToast(String toast){
            Toast.makeText(mContext,toast,Toast.LENGTH_SHORT).show();
        }
    }

    webview 绑定接口:

    复制代码
     webView_first.addJavascriptInterface(new WebAppInterface(this),"Android");

    html代码示例:

    复制代码
    <!doctype html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Bootstrap demo</title>
        <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.1/css/bootstrap.min.css" rel="stylesheet">
      </head>
      <body>
        <h1>Hello, world!</h1>
        <input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />
        <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.1/js/bootstrap.bundle.min.js"></script>
        <script type="text/javascript">
          function showAndroidToast(toast) {
                Android.showToast(toast);
          }
        </script>
      </body>
    </html>
  8. webview中的跳转连接

    参考:https://www.digitalocean.com/community/tutorials/android-webview-example-tutorial

    当 html 中有跳转链接的时候,可以直接阻止、选择性阻止在我们的App跳转、或者可以打开系统浏览器,加载外部链接。

    java 复制代码
    webView_first.setWebViewClient(new MyWebViewClient());
    
    private class MyWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
            Log.i("FirstWebViewActivity",request.getUrl().getHost());
            if ("192.168.96.108".equals(request.getUrl().getHost())) {
                // This is my website, so do not override; let my WebView load the page
                return false;
            }
            // Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs
            Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());
            startActivity(intent);
            return true;
        }
    }
  9. webview加载本地网页,实现 sqlite 数据库增删改查

    使用的库 gson:

    groovy 复制代码
    implementation 'com.google.code.gson:gson:2.8.5'

    首先新建assets文件夹,android默认工程没有创建。

    编写 DBHelper ,定义数据库名,在 onCreate 中初始化数据库:

    java 复制代码
    public class MyDatabaseHelper extends SQLiteOpenHelper {
        public static final String DATABASE_NAME = "student.db";
        public static final int DATABASE_VERSION =1;
    
        public MyDatabaseHelper(Context context){
            super(context,DATABASE_NAME,null,DATABASE_VERSION);
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            // 数据库首次创建时调用,执行创建表等语句
            // 创建student表
            db.execSQL("CREATE TABLE student (" +
                    "id INTEGER PRIMARY KEY AUTOINCREMENT," +
                    "name TEXT," +
                    "age INTEGER," +
                    "birth TEXT)");
            // 如果需要初始化数据可以在这里插入:
            db.execSQL("INSERT INTO student (name, age, birth) " +
                    "VALUES ('张三', 18, '1980-01-01')");
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            // 数据库版本更新时调用
    
        }
    }

    然后定义 MyApplication,在程序启动的时候初始化数据库:

    java 复制代码
    public class MyApplication extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            //检测数据库是否存在,不存在则创建数据库
            Context context = getApplicationContext();
            MyDatabaseHelper dbHelper = new MyDatabaseHelper(context);
            SQLiteDatabase db = dbHelper.getWritableDatabase();
        }
    }

    定义数据增、查接口类,供H5中调用:

    java 复制代码
    public class JsDBInterface {
        private SQLiteDatabase db;
        public JsDBInterface(Context context){
            // 在构造函数中打开数据库
            MyDatabaseHelper helper = new MyDatabaseHelper(context);
            db = helper.getWritableDatabase();
        }
    
        /**
         * 插入数据
         * @param name
         * @param age
         * @param birth
         */
        @JavascriptInterface
        public void insertData(String name,int age,String birth){
            //检查参数
            if(name==null||name.isEmpty()){
                return;
            }
            //插入数据
            ContentValues values = new ContentValues();
            values.put("name",name);
            values.put("age",age);
            values.put("birth",birth);
            db.insert("student",null,values);
            Log.i("插入数据",name);
        }
    
        /**
         * 查询数据
         * @return
         */
        @JavascriptInterface
        public String getAllStudents(){
            List<Student> students = new ArrayList<>();
            //查询数据库中的所有学生
            Cursor cursor = db.query("student",null,null,null,null,null,null);
            //获取所有列
    //        String[] cols = cursor.getColumnNames();
    //        int nameIndex=-1;
            while (cursor.moveToNext()){
                int columnIndex1=cursor.getColumnIndex("id");
                int columnIndex2=cursor.getColumnIndex("name");
                int columnIndex3=cursor.getColumnIndex("age");
                int columnIndex4=cursor.getColumnIndex("birth");
                int id=0;String name="null";int age=0;String birth="0000-00-00";
                if(columnIndex1>=0){
                    id = cursor.getInt(columnIndex1);
                }
                if(columnIndex2>=0){
                    name=cursor.getString(columnIndex2);
                }
                if(columnIndex3>=0){
                    age = cursor.getInt(columnIndex3);
                }
                if(columnIndex4>=0){
                    birth=cursor.getString(columnIndex4);
                }
    
                Student stu = new Student(id,name,age,birth);
                students.add(stu);
            }
            cursor.close();
            Gson gson = new Gson();
            String json = gson.toJson(students);
            return json;
        }
    }

    数据实体类:

    java 复制代码
    public class Student {
        private int id;
        private String name;
        private int age;
        private String birth;
    
        public int getId() {
            return id;
        }
    
        public Student(int id,String name, int age, String birth) {
            this.id=id;
            this.name = name;
            this.age = age;
            this.birth = birth;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getBirth() {
            return birth;
        }
    
        public void setBirth(String birth) {
            this.birth = birth;
        }
    }

    在 Activity 中注册接口:

    java 复制代码
    webView.addJavascriptInterface(new JsDBInterface(MainActivity.this),"JsDBInterface");
    
    加载本地网页:
    private String localUrl="file:///android_asset/index.html";
    webView_first.loadUrl(localUrl);

    HTML 使用了vue,调用代码如下:

    复制代码
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <script src="./vue.min.js"></script>
    </head>
    <body>
        <div id="app">
            <h2>{{message}}</h2>
            <ul>
                <li v-for="user in users" :key="user.id">{{user.name}}</li>
            </ul>
            <button id="btnInsertStudent" onclick="addStudent()">新增学生</button>
            <button id="btnGetAllStudents" onclick="getAllStudent()">查询所有学生</button>
            <ul id='studentList'></ul>
        </div>
        <script>
            var app = new Vue({
                el:'#app',
                data(){
                    return {
                        message:'Hello Vue!'
                    }
                },
                mounted(){
                }
            })
            function addStudent(){
                JsDBInterface.insertData("小明",18,"2010-01-01");
                alert('添加成功')
            }
            function getAllStudent(){
                var data = JsDBInterface.getAllStudents();
                var students = JSON.parse(data);
                var studentTxt='';
                for(var i = 0; i < students.length; i++) {
                    var stu = students[i];
                    studentTxt += '<li>' + stu.name + ',' + stu.age + ',' + stu.birth + '</li>';
                  }
                  var ul = document.getElementById('studentList');
                  ul.innerHTML = studentTxt;
            }
        </script>
    </body>
    </html>

    结果:

  10. webview h5 alert 不运行

    参考:https://copyprogramming.com/howto/modify-alert-title-javascript-in-android-webview

    最简单的处理方法是:添加下面的代码

    复制代码
    webView_first.setWebChromeClient(new WebChromeClient());

但是上面的标题会显示网址,可以用下面的方法进行修改:

java 复制代码
webView_first.setWebChromeClient(new WebChromeClient(){
    @Override
    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
        AlertDialog dialog = new AlertDialog.Builder(view.getContext())
            .setTitle("提示")
            .setMessage(message)
            .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // do nothing
                }
            }).create();
        dialog.show();
        result.confirm();
        return true;
    }
});

修改后:

此外如果对 alert, prompt, confirm统一进行修改,可以使用下面代码:

java 复制代码
webView_first.setWebChromeClient(new WebChromeClient(){
    @Override
    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
        new AlertDialog.Builder(view.getContext())
            .setTitle("alert")
            .setMessage(message)
            .setPositiveButton("确定", (DialogInterface dialog, int which) -> result.confirm())
            .setOnDismissListener((DialogInterface dialog) -> result.confirm())
            .create()
            .show();
        return true;
    }

    @Override
    public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
        new AlertDialog.Builder(view.getContext())
            .setTitle("confirm")
            .setMessage(message)
            .setPositiveButton("确定", (DialogInterface dialog, int which) -> result.confirm())
            .setNegativeButton("取消", (DialogInterface dialog, int which) -> result.cancel())
            .setOnDismissListener((DialogInterface dialog) -> result.cancel())
            .create()
            .show();
        return true;
    }

    @Override
    public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
        final EditText input = new EditText(view.getContext());
        input.setInputType(InputType.TYPE_CLASS_TEXT);
        input.setText(defaultValue);
        new AlertDialog.Builder(view.getContext())
            .setTitle("prompt")
            .setMessage(message)
            .setView(input)
            .setPositiveButton("确定", (DialogInterface dialog, int which) -> result.confirm(input.getText().toString()))
            .setNegativeButton("取消", (DialogInterface dialog, int which) -> result.cancel())
            .setOnDismissListener((DialogInterface dialog) -> result.cancel())
            .create()
            .show();
        return true;
    }
});
相关推荐
雨白13 小时前
Android 快捷方式实战指南:静态、动态与固定快捷方式详解
android
hqk13 小时前
鸿蒙项目实战:手把手带你实现 WanAndroid 布局与交互
android·前端·harmonyos
LING14 小时前
RN容器启动优化实践
android·react native
恋猫de小郭17 小时前
Flutter 发布官方 Skills ,Flutter 在 AI 领域再添一助力
android·前端·flutter
Kapaseker1 天前
一杯美式搞懂 Any、Unit、Nothing
android·kotlin
黄林晴1 天前
你的 Android App 还没接 AI?Gemini API 接入全攻略
android
恋猫de小郭1 天前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab1 天前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
BoomHe2 天前
Now in Android 架构模式全面分析
android·android jetpack
二流小码农2 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos