I have written such an article before- jump to view it . Recently, I was bored and wanted to review it, but I found that I didn’t understand it... This is absolutely impossible. The original intention of writing a blog has not been achieved, and I can’t understand it, let alone share it. I gave it to someone else, so I wrote it again. I hope that when I look back someday, I can easily think of my thoughts at this time.
First of all, through this article, you can learn:
1. Interaction between native and H5
2. How to introduce web pages into APP through WebView
3. The specific practice of one-pixel keep-alive method
In order to fully understand the interaction process between native and H5, here is a switching effect
. Previous and next, the display belongs to the native range. The overall interface and switching background are part of H5. After clicking the display, return to the original page of the mobile phone, and the mobile phone can be normal Use (similar to one-pixel keep-alive usage), note: not compatible with mobile phones, different mobile phones may have different effects
First of all, two html interfaces and two activities are needed, one of which is the operation activity, and the other is the activity in the lower left corner. The one in the lower left corner imitates the one-pixel keep-alive method, except that one pixel is changed to the corresponding size and the layout is added.
First create an assets folder for storing html files
The contents of the two html files are as follows:
live2D
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>live2d模型</title>
<style type="text/css">
.rgba{
background-color: rgba(0,0,0,1);
background-position: center 0;
background-size: cover;
min-height: 100vh;
}
</style>
</head>
<body class="rgba">
<input type="button" value="切换背景" onclick="jsColor() " />
</body>
<!-- 导入模型的脚本文件 -->
<script src="https://eqcn.ajz.miesnfu.com/wp-content/plugins/wp-3d-pony/live2dw/lib/L2Dwidget.min.js"></script>
<script language="javascript">
//人物集合
var arr = ["https://unpkg.com/[email protected]/assets/chitose.model.json",
"https://unpkg.com/[email protected]/assets/haruto.model.json",
"https://unpkg.com/[email protected]/assets/hibiki.model.json",
"https://unpkg.com/[email protected]/assets/hijiki.model.json",
"https://unpkg.com/[email protected]/assets/izumi.model.json",
"https://unpkg.com/[email protected]/assets/koharu.model.json",
"https://unpkg.com/[email protected]/assets/miku.model.json",
"https://unpkg.com/[email protected]/assets/ni-j.model.json",
"https://unpkg.com/[email protected]/assets/shizuku.model.json",
"https://unpkg.com/[email protected]/assets/tororo.model.json",
"https://unpkg.com/[email protected]/assets/tsumiki.model.json",
"https://unpkg.com/[email protected]/assets/unitychan.model.json",
"https://unpkg.com/[email protected]/assets/z16.model.json",
"https://unpkg.com/[email protected]/assets/nico.model.json",
"https://unpkg.com/[email protected]/assets/nipsilon.model.json",
"https://unpkg.com/[email protected]/assets/nito.model.json",
"https://unpkg.com/[email protected]/assets/wanko.model.json"];
//背景集合
var color = ["https://c-ssl.duitang.com/uploads/item/202007/01/20200701073201_dMUFs.thumb.1000_0.gif",
"http://img.bimg.126.net/photo/eqj93LhUGXGIJiJfOhQ4KA==/334110797356199467.jpg",
"http://p3.itc.cn/q_70/images03/20200916/1d4adfc4ac6f4e7ca08f3c563bafdd0b.gif",
"https://c-ssl.duitang.com/uploads/blog/202104/24/20210424120118_378cf.thumb.1000_0.gif",];
var i = 0; //默认人物下标
var j = 0; //默认背景下标
//设置背景
function jsColor(){
if(j >= color.length){
j = 0;
}
document.body.style.backgroundImage="URL("+color[j]+")";
test.getBjIndex(j)
j++;
}
//设置人物
function jsBack(){
if (i >0 ){
i--;
}else{
//调用原生方法
test.initJS();
}
setL2();
}
//设置人物
function jsNext(){
if (i < arr.length ){
i++;
}else{
//调用原生方法
test.initJS();
}
setL2();
}
//点击方法之后打开的
function setL2(){
test.getRwIndex(i)
L2Dwidget.init({
// 引用的模型
"model": {
jsonPath: arr[i], //设置人物
"scale": 1
},
// 模型的样式,可以自行更改
"display": {
"position": "right",
"width": 150,
"height": 350,
"hOffset": 50,
"vOffset": 50
},
"mobile": {
"show": true,
"scale": 1
},
"react": {
"opacityDefault": 1,
"opacityOnHover":2
}
});
}
//进来之后默认打开的人物
L2Dwidget.init({
// 引用的模型
"model": {
jsonPath: arr[i],
"scale": 1
},
// 模型的样式,可以自行更改
"display": {
"position": "right",
"width": 150,
"height": 350,
"hOffset": 50,
"vOffset": 50
},
"mobile": {
"show": true,
"scale": 1
},
"react": {
"opacityDefault": 1,
"opacityOnHover":2
}
});
</script>
</html>
live2D_title
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>live2d模型</title>
<style type="text/css">
.rgba{
background-color: rgba(0,0,0,1);
background-position: center 0;
background-size: cover;
min-height: 100vh;
}
</style>
</head>
<body class="rgba">
</body>
<!-- 导入模型的脚本文件 -->
<script src="https://eqcn.ajz.miesnfu.com/wp-content/plugins/wp-3d-pony/live2dw/lib/L2Dwidget.min.js"></script>
<script language="javascript">
//人物集合
var arr = ["https://unpkg.com/[email protected]/assets/chitose.model.json",
"https://unpkg.com/[email protected]/assets/haruto.model.json",
"https://unpkg.com/[email protected]/assets/hibiki.model.json",
"https://unpkg.com/[email protected]/assets/hijiki.model.json",
"https://unpkg.com/[email protected]/assets/izumi.model.json",
"https://unpkg.com/[email protected]/assets/koharu.model.json",
"https://unpkg.com/[email protected]/assets/miku.model.json",
"https://unpkg.com/[email protected]/assets/ni-j.model.json",
"https://unpkg.com/[email protected]/assets/shizuku.model.json",
"https://unpkg.com/[email protected]/assets/tororo.model.json",
"https://unpkg.com/[email protected]/assets/tsumiki.model.json",
"https://unpkg.com/[email protected]/assets/unitychan.model.json",
"https://unpkg.com/[email protected]/assets/z16.model.json",
"https://unpkg.com/[email protected]/assets/nico.model.json",
"https://unpkg.com/[email protected]/assets/nipsilon.model.json",
"https://unpkg.com/[email protected]/assets/nito.model.json",
"https://unpkg.com/[email protected]/assets/wanko.model.json"];
//背景集合
var color = ["https://c-ssl.duitang.com/uploads/item/202007/01/20200701073201_dMUFs.thumb.1000_0.gif",
"http://img.bimg.126.net/photo/eqj93LhUGXGIJiJfOhQ4KA==/334110797356199467.jpg",
"http://p3.itc.cn/q_70/images03/20200916/1d4adfc4ac6f4e7ca08f3c563bafdd0b.gif",
"https://c-ssl.duitang.com/uploads/blog/202104/24/20210424120118_378cf.thumb.1000_0.gif",];
//点击方法之后打开的
function setL2(i,j){
//设置背景
document.body.style.backgroundImage="URL("+color[i]+")";
//进来之后默认打开的人物
L2Dwidget.init({
// 引用的模型
"model": {
jsonPath: arr[j],
"scale": 1
},
// 模型的样式,可以自行更改
"display": {
"position": "left,bottom",
"width": 23,
"height": 60,
"hOffset": 0,
"vOffset": 0
},
"mobile": {
"show": true,
"scale": 1
},
"react": {
"opacityDefault": 1,
"opacityOnHover":2
}
});
}
</script>
</html>
Secondly, bind the WebView to the interface in the Activity.
How to introduce the webpage into the APP through the WebView
-
Among them
mDataBind.mWebView.loadUrl("file:///android_asset/live2D.html")
is to add html to WebView, pay attention to the path file:///android_asset/ If there are other sub-paths, similarly write down, android_asset is the folder where html was created just now -
mDataBind.mWebView.addJavascriptInterface(this, "test")
This method is to associate the entire Activity with the JS of the interface, and then test in the HTML corresponding to the WebView represents the Activity, which is used for Js to call the native method -
Don't forget to set the WebView to support JS, otherwise the whole method will be invalid
Interact with H5 natively, and introduce it in detail with this code
1. Call H5 natively (no parameters)
native
2.H5 call native in H5 (no parameter)
In native,
don’t forget to add annotations, otherwise you won’t find the corresponding method (Koltin and Java should be noted)
@SuppressLint("JavascriptInterface")
@JavascriptInterface
Does the test here in H5
look familiar? This is the one set by the addJavascriptInterface method, and it can be called directly.
3. Call H5 natively (with parameters)
In the original version,
you can see that the difference with no parameter is that the corresponding parameter is added in the brackets. If there are multiple parameters, don’t forget to add a comma.
In H5,
you can see that JS parameters can be defined directly, and then used directly. Don’t forget the comma for multiple parameters.
4. H5 calls the native method (with parameters)
Native in
H5
One-pixel keep-alive method in practice
Why do I want to talk about this here? I think this part is more interesting. After all, it has a glorious history. It can also be seen from the first screenshot that the overall Activity is a small piece. It is also a small piece in the previous article, but it is In a counterfeit way, the overall APP is inoperable, but this time it is operable
In the core code part
, I wrote a one-pixel demo separately, and found that the APP does have one pixel, but the phone cannot perform other operations.
//全屏不可触摸
window.setFlags(
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
)
//取消全屏不可触摸
//window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
Finally, let me talk about the overall idea :
- In html, put different viewer resources and backgrounds in different collections, and switch by calling the H5 method natively
- After switching, call the native method through H5 to pass the current subscript to the native
- Get the subscript natively, click on the pixel activity that jumps to the display and pass the subscript to it
- Pixel Activity gets the subscript, and passes the subscript to H5 for display by natively calling the H5 method
All code posted:
1. Network permissions
<uses-permission android:name="android.permission.INTERNET" />
2. Use dataBinding
dataBinding {
enabled = true
}
3. Overall directory
MainActivity
class MainActivity : AppCompatActivity() {
lateinit var mDataBind: ActivityMainBinding
var bjIndex = "0"
var rwIndex = "0"
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mDataBind = DataBindingUtil.setContentView(this,R.layout.activity_main)
initBar()
initWeb()
initEvent()
}
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun initBar(){
this.window.statusBarColor = this.resources.getColor(R.color.white)
this.window.decorView.systemUiVisibility = SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
}
@SuppressLint("SetJavaScriptEnabled", "JavascriptInterface")
private fun initWeb(){
mDataBind.mWebView.webChromeClient = WebChromeClient()
mDataBind.mWebView.settings.javaScriptEnabled = true
mDataBind.mWebView.settings.domStorageEnabled = true
mDataBind.mWebView.settings.javaScriptCanOpenWindowsAutomatically = true
//mDataBind.mWebView.setBackgroundColor(0);//设置背景色
//mDataBind.mWebView.background.alpha = 0;//设置填充透明度(布局中一定要设置background,不然getbackground会是null)
mDataBind.mWebView.addJavascriptInterface(this, "test")
mDataBind.mWebView.loadUrl("file:///android_asset/live2D.html")
}
private fun initEvent(){
mDataBind.tvBack.setOnClickListener {
mDataBind.mWebView.loadUrl("javascript:jsBack()");
}
mDataBind.tvNext.setOnClickListener {
mDataBind.mWebView.loadUrl("javascript:jsNext()");
}
mDataBind.tvSelect.setOnClickListener {
val it = Intent(this@MainActivity, ShowActivity::class.java)
it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
it.putExtra("BJ_KEY",bjIndex)
it.putExtra("RW_KEY",rwIndex)
startActivity(it)
finish()
}
}
@SuppressLint("JavascriptInterface")
@JavascriptInterface
fun initWVBack(){
Toast.makeText(this,"设置背景",Toast.LENGTH_SHORT).show()
}
@SuppressLint("JavascriptInterface")
@JavascriptInterface
fun initJS(){
Toast.makeText(this,"没了..",Toast.LENGTH_SHORT).show()
}
@SuppressLint("JavascriptInterface")
@JavascriptInterface
fun getBjIndex(s: String){
this.bjIndex = s
}
@SuppressLint("JavascriptInterface")
@JavascriptInterface
fun getRwIndex(s: String){
this.rwIndex = s
}
}
ShowActivity
class ShowActivity : AppCompatActivity() {
lateinit var mDataBind: ActivityShowBinding
var bjKey = "BJ_KEY"
var rwKey = "RW_KEY"
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//一像素的时候不需要setContentView方法
mDataBind = DataBindingUtil.setContentView(this,R.layout.activity_show);
val window = window
//一像素保护法通用默认是左上
window.setGravity(Gravity.LEFT or Gravity.BOTTOM)
val params = window.attributes
params.x = 0
params.y = 0
//一像素的时候这里都是1
params.height = 400
params.width = 200
window.attributes = params
// @SuppressLint("UseCompatLoadingForDrawables") val drawable =
// this.resources.getDrawable(R.drawable.ic_launcher_background)
// window.setBackgroundDrawable(drawable)
//全屏不可触摸
window.setFlags(
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
)
//取消全屏不可触摸
//window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
//获取数据
val i = intent.getStringExtra(bjKey)
val j = intent.getStringExtra(rwKey)
initWeb(i,j)
}
@SuppressLint("JavascriptInterface")
private fun initWeb(i: String?,j: String?){
mDataBind.mWebView.webChromeClient = WebChromeClient()
mDataBind.mWebView.settings.javaScriptEnabled = true
mDataBind.mWebView.settings.domStorageEnabled = true
mDataBind.mWebView.settings.javaScriptCanOpenWindowsAutomatically = true
//mDataBind.mWebView.setBackgroundColor(0);//设置背景色
//mDataBind.mWebView.background.alpha = 0;//设置填充透明度(布局中一定要设置background,不然getbackground会是null)
mDataBind.mWebView.addJavascriptInterface(this, "test")
mDataBind.mWebView.loadUrl("file:///android_asset/live2D_title.html")
Handler().postDelayed(Runnable {
//2秒之后在这进行操作
mDataBind.mWebView.loadUrl("javascript:setL2($i,$j)");
}, 2000)
}
}
activity_main
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<WebView
android:id="@+id/mWebView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="48dp">
<TextView
android:id="@+id/tvBack"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:gravity="center"
android:textStyle="bold"
android:textColor="@color/black"
android:text="上一个"/>
<TextView
android:id="@+id/tvSelect"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:gravity="center"
android:textStyle="bold"
android:textColor="@color/black"
android:layout_centerInParent="true"
android:text="展示"/>
<TextView
android:id="@+id/tvNext"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:gravity="center"
android:textStyle="bold"
android:textColor="@color/black"
android:layout_alignParentRight="true"
android:text="下一个"/>
</RelativeLayout>
</LinearLayout>
</layout>
activity_show
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<LinearLayout
android:layout_width="200px"
android:layout_height="300px"
android:orientation="vertical"
tools:context=".ShowActivity">
<WebView
android:id="@+id/mWebView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</layout>