preface

WebView same-layer rendering is not a new technology. First-line Internet products in China are widely used, such as native components of small programs, and scenes of native players embedded in e-commerce H5.

Scenario analysis Technology blog
Wechat applets Small procedures of the same layer render principle analysis
Baidu applet [Into the small program principle] reveal the components of the same layer rendering
Taobao system player 618 Amoy front-end technology sharing

If you understand how this works, you will find that it is difficult to get started on Android because you need to modify the browser kernel. After reading the article, I called it awesome, but I looked back at the native WebView code and couldn’t find the right API.

Introduction to same layer rendering

On the Android platform, H5 content relies on WebView rendering, which has an independent and equal relationship with the native View. From the perspective of drawing level, WebView and native are bound to cover each other, not to do synchronous scrolling;

In the web DOM tree, some native components are intermingled, and the original layers and styles are retained. This is called same-layer rendering.

What problems can same-layer rendering solve?

The use of Web front-end technology is difficult to achieve, or stability, performance limitations

For example: video player, map, game engine, live push and pull stream, camera preview and other scenes;

See X5 kernel browser same-layer rendering

Preparations:

  • Access Tencent X5 browser x5.tencent.com/ correctly
  • Ensure the X5 kernel loaded x5.tencent.com/docs/questi success…

Prepare a placeholder label

The principle of X5 same-layer rendering is to use native to take over specific tags in H5 pages, so prepare an H5 page and insert a custom tag. The name of the tag can be arbitrarily defined, such as MyTag, and the style is set according to standard CSS

<mytag id = "mytag" src="https://vfx.mtime.cn/Video/2019/02/04/mp4/190204084208765161.mp4" style="position:absolute; width:350px; height:500px;" > placeholder tag </mytag>Copy the code

Force X5WebView same-layer rendering on

X5 same layer rendering capability is off by default, through the cloud switch control, through analysis found that you can force modification of local SP properties, forced open

if (mWebView.getX5WebViewExtension()! Enable SharedPreferences TBs_public_settings = getSharedPreferences(" TBs_public_settings ", Context.MODE_PRIVATE); SharedPreferences.Editor edit = tbs_public_settings.edit(); edit.putInt("MTT_CORE_EMBEDDED_WIDGET_ENABLE",1); edit.apply(); }else {log.d (TAG, "init: non-X5 kernel "); }Copy the code

Native control that registers the target placeholder tag with the browser

Using the registerEmbeddedWidget method, you can register placeholders with the browser kernel that need native tags to take over. The first parameter is the name of the tag to take over. The second parameter is the factory interface that the factory creates for the native tag object

// Register placeholder tags in the DOM tree, Create the corresponding native components Boolean result = mWebView. GetX5WebViewExtension () registerEmbeddedWidget (new String [] {} "mytag", new IEmbeddedWidgetClientFactory() { @Override public IEmbeddedWidgetClient createWidgetClient(String s, Map<String, String> map, IEmbeddedWidget iEmbeddedWidget) { Log.d(TAG, "init: createWidgetClient s"+s); Log.d(TAG, "init: createWidgetClient map"+map.toString()); return new VideoEmbeddedWidgetClient(BrowserActivity.this); }});Copy the code

CreateWidgetClient method parameter meaning

  • sLabel name, uppercase
  • mapAttribute of the tag, specified in HTML
  • iEmbeddedWidgetProvides a native proxy interface for this label

Here is what map prints

init: createWidgetClient map{src=https://vfx.mtime.cn/Video/2019/02/04/mp4/190204084208765161.mp4, style=position:absolute; width:350px; height:500px; , id=mytag}Copy the code

An implementation that handles native placeholder controls, IEmbeddedWidgetClient

How does the native take over replacement placeholder tags, the main implementation class being IEmbeddedWidgetClient

public interface IEmbeddedWidgetClient {
    
    void onSurfaceCreated(Surface var1);

    void onSurfaceDestroyed(Surface var1);

    boolean onTouchEvent(MotionEvent var1);

    void onRectChanged(Rect var1);

    void onVisibilityChanged(boolean var1);

    void onDestroy();

    void onActive();

    void onDeactive();

    void onRequestRedraw();
}
Copy the code

First, the IEmbeddedWidgetClient is not a native View, but rather provides the native drawing entry for the tag area, understood from onSurfaceCreated and onSurfaceDestroyed;

Since it is not a native View drawing, X5 still provides attributes that resemble views, which can be easily seen from the API naming.

  • onSurfaceCreatedThis label can be drawn, requesting native API processing
  • onSurfaceDestroyedThe label view is destroyed, requesting native destruction
  • onTouchEventTouch event distribution
  • onRectChangedThe label is called back to coordinate changes in the WebView (e.g. scrolling, changing width and height)
  • onVisibilityChangedThe label shows hidden
  • onDestroyThe label is removed, or display = None

Demonstrate a Demo

Familiar with X5 API, write a simple Demo to play

The design idea of Demo is to embed a native camera in a vertical scrolling web page. The native camera should be collected and displayed normally, and the front-end code can control the basic operations of camera label display and hiding, synchronous scrolling and so on.

Web page

<! DOCTYPE HTML > < HTML > <head> <title> <meta charset=" utF-8 "> <meta Content ="width=device-width, Initial - scale = 1.0, the maximum - scale = 1.0, user - scalable = no, shrink-to-fit=no" name="viewport"> <script type="text/javascript"> window.onload = touchtouch; function touchtouch(){ var camera = document.getElementById('camera'); console.log("camera:"+camera) camera.addEventListener("touchStart",handlerTouch,false); camera.addEventListener("touchend",handlerTouch,false); camera.addEventListener("touchcancel",handlerTouch,false); camera.addEventListener("touchleave",handlerTouch,false); camera.addEventListener("touchmove",handlerTouch,false); } function handlerTouch(evt){ log(evt); } </script> </head> <body> <p> <div style="background-color: aqua; height: 100px; text-align: center;" >1</div> <div style="background-color: gray; height: 100px; text-align: center;" >2</div> <div style="background-color: rgb(80, 69, 69); height:500px;" > <camera cameraId=0 style="position:absolute; width:350px; height:500px;" </camera> <a href="https://www.baidu.com" style="position:absolute; </a> <div style="background-color: gold; background-color: gold; height: 100px; text-align: center;" >4</div> <div style="background-color: red; height: 100px; text-align: center;" >5</div> <div style="background-color: green; height: 100px; text-align: center;" >6</div> </div> </body> </html>Copy the code

Java implementation

public class CameraEmbeddedWidgetClient implements IEmbeddedWidgetClient { private String TAG = "VideoEmbeddedWidgetClient"; private Rect rect; private CameraHelper cameraHelper; public CameraEmbeddedWidgetClient(Context c) { cameraHelper = new CameraHelper(c); } @Override public void onSurfaceCreated(Surface surface) { Log.d(TAG, "onSurfaceCreated: "); // Canvas canvas = surface.lockCanvas(rect); // canvas.drawColor(Color.parseColor("#7f000000")); // surface.unlockCanvasAndPost(canvas); cameraHelper.preview(surface); } @Override public void onSurfaceDestroyed(Surface surface) { Log.d(TAG, "onSurfaceDestroyed: "); cameraHelper.release(); }}Copy the code

Front-end style changes and Native event firing

  • Specify the style display: NoneNative callbackonVisibilityChanged(false)andonDestroy
  • Specifies the style display:blockCreate a new Client
  • Specify style visibility:visibleNative callbackonVisibilityChanged(true)
  • Specify style visibility:hiddenNative callbackonVisibilityChanged(false)
  • Remove the current DOMEquivalent to display: none

Validation of touch events

You must set up a tag change event listener in JS

camera.addEventListener("touchStart",handlerTouch,false);
camera.addEventListener("touchend",handlerTouch,false);
camera.addEventListener("touchcancel",handlerTouch,false);
camera.addEventListener("touchleave",handlerTouch,false);
camera.addEventListener("touchmove",handlerTouch,false);
Copy the code

Natively accept event handling

IEmbeddedWidgetClient implementation class

@Override public boolean onTouchEvent(MotionEvent motionEvent) { Log.d(TAG, "onTouchEvent: "+motionEvent.toString()); float x = motionEvent.getX(); float y = motionEvent.getY(); int action = motionEvent.getAction(); switch (action){ case MotionEvent.ACTION_DOWN: initX = x; initY = y; intercepted = false; return false; case MotionEvent.ACTION_MOVE: float dx = x - initX; float dy = y - initY; if (! intercepted && Math.abs(dy)>Math.abs(dx) && Math.abs(dy)>16){ intercepted = true; } break; case MotionEvent.ACTION_UP: break; } return intercepted; }Copy the code

conclusion

This article mainly shares the X5 kernel hidden layer rendering ability, used for learning and communication, but there is no official documentation, so please be cautious about introducing projects; If you’re interested in this topic, try it yourself.