WebView 专题
第一个WebView程序:加载远程网址
- Layout添加WebView组件;
xml
<WebView
android:id="@+id/webView_first"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
-
初始化组件,加载Url;
javapublic 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(); } }
-
Manifest文件添加网络访问权限
xml<uses-permission android:name="android.permission.INTERNET" />
-
出现错误:
sqlNetworkSecurityConfig 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" ...
-
出现错误:
访问bing.com无法加载封面图片:
vbnetThe 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.
解决方法:
iniwebView_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目录下
-
将 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 绑定接口:
arduinowebView_first.addJavascriptInterface(new WebAppInterface(this),"Android");
html代码示例:
xml<!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中的跳转连接
参考:www.digitalocean.com/community/t...
当 html 中有跳转链接的时候,可以直接阻止、选择性阻止在我们的App跳转、或者可以打开系统浏览器,加载外部链接。
javawebView_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; } }
-
webview加载本地网页,实现 sqlite 数据库增删改查
使用的库 gson:
groovyimplementation '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,调用代码如下:
html
<!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 不运行
参考:copyprogramming.com/howto/modif...
最简单的处理方法是:添加下面的代码
java
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;
}
});