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;
    }
});
相关推荐
帅得不敢出门3 小时前
安卓设备adb执行AT指令控制电话卡
android·adb·sim卡·at指令·电话卡
我又来搬代码了5 小时前
【Android】使用productFlavors构建多个变体
android
德育处主任7 小时前
Mac和安卓手机互传文件(ADB)
android·macos
芦半山7 小时前
Android“引用们”的底层原理
android·java
迃-幵7 小时前
力扣:225 用队列实现栈
android·javascript·leetcode
大风起兮云飞扬丶8 小时前
Android——从相机/相册获取图片
android
Rverdoser8 小时前
Android Studio 多工程公用module引用
android·ide·android studio
aaajj8 小时前
[Android]从FLAG_SECURE禁止截屏看surface
android
@OuYang8 小时前
android10 蓝牙(二)配对源码解析
android
Liknana8 小时前
Android 网易游戏面经
android·面试