WebView 专题
第一个WebView程序:加载远程网址
-
Layout添加WebView组件;
<WebViewandroid:id="@+id/webView_first"android:layout_width="match_parent"android:layout_height="match_parent"/>
-
初始化组件,加载Url;
public class FirstWebViewActivity extends AppCompatActivity{private WebView webView_first;@Overrideprotected 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/");}@Overrideprotected void onDestroy() {super.onDestroy();webView_first.removeAllViews();webView_first.destroy();} }
-
Manifest文件添加网络访问权限
<uses-permission android:name="android.permission.INTERNET" />
-
出现错误:
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 version="1.0" encoding="utf-8"?> <network-security-config> //默认配置:允许明文通信<base-config cleartextTrafficPermitted="true" /> </network-security-config>
在AndroidManifest.xml中引用
<applicationandroid:networkSecurityConfig="@xml/network_security_config"...
-
出现错误:
访问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");
-
下载:在bing搜索网站首页,好看的壁纸,点击可以下载图片,将图片下载到 SD 卡下的Downloader目录下
参考:https://cloud.tencent.com/developer/article/1742327
-
将 JavaScript代码绑定到Android代码
js调用Android代码中的方法
新建 WebAppInterface 类
/*** @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*/@JavascriptInterfacepublic 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>
-
webview中的跳转连接
参考:https://www.digitalocean.com/community/tutorials/android-webview-example-tutorial
当 html 中有跳转链接的时候,可以直接阻止、选择性阻止在我们的App跳转、或者可以打开系统浏览器,加载外部链接。
webView_first.setWebViewClient(new MyWebViewClient());private class MyWebViewClient extends WebViewClient {@Overridepublic 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 pagereturn false;}// Otherwise, the link is not for a page on my site, so launch another Activity that handles URLsIntent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());startActivity(intent);return true;} }
-
webview加载本地网页,实现 sqlite 数据库增删改查
使用的库 gson:
implementation 'com.google.code.gson:gson:2.8.5'
首先新建assets文件夹,android默认工程没有创建。
编写 DBHelper ,定义数据库名,在 onCreate 中初始化数据库:
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);}@Overridepublic 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')");}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// 数据库版本更新时调用} }
然后定义 MyApplication,在程序启动的时候初始化数据库:
public class MyApplication extends Application {@Overridepublic void onCreate() {super.onCreate();//检测数据库是否存在,不存在则创建数据库Context context = getApplicationContext();MyDatabaseHelper dbHelper = new MyDatabaseHelper(context);SQLiteDatabase db = dbHelper.getWritableDatabase();} }
定义数据增、查接口类,供H5中调用:
public class JsDBInterface {private SQLiteDatabase db;public JsDBInterface(Context context){// 在构造函数中打开数据库MyDatabaseHelper helper = new MyDatabaseHelper(context);db = helper.getWritableDatabase();}/*** 插入数据* @param name* @param age* @param birth*/@JavascriptInterfacepublic 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*/@JavascriptInterfacepublic 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;} }
数据实体类:
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 中注册接口:
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>
结果:
-
webview h5 alert 不运行
参考:https://copyprogramming.com/howto/modify-alert-title-javascript-in-android-webview
最简单的处理方法是:添加下面的代码
webView_first.setWebChromeClient(new WebChromeClient());
但是上面的标题会显示网址,可以用下面的方法进行修改:
webView_first.setWebChromeClient(new WebChromeClient(){@Overridepublic boolean onJsAlert(WebView view, String url, String message, JsResult result) {AlertDialog dialog = new AlertDialog.Builder(view.getContext()).setTitle("提示").setMessage(message).setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {// do nothing}}).create();dialog.show();result.confirm();return true;}
});
修改后:
此外如果对 alert, prompt, confirm统一进行修改,可以使用下面代码:
webView_first.setWebChromeClient(new WebChromeClient(){@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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;}
});