Article From:https://www.cnblogs.com/yunqishequ/p/9968123.html

Introduction

In the long period of hybrid engineering from Native to Flutter, if we want to make a smooth transition, it is a good choice to use the more perfect control in Native in Flutter. In this article, I would like to introduce to you how AndroidView is used and how to use AndroidView.On this basis, the extended solution of two-terminal embedded Native component is developed.

1. Using tutorials

1.1. DemoRun

Embedded maps may exist in many Apps, but today’s map SDK does not provide a library of Flutter, and it is obviously unrealistic to develop a set of maps by oneself. In this scenario, using a hybrid stack is a better choice. We can go straight to Native.A Map is embedded in the drawing tree of Flutter, but the view embedded in this scheme is not in the drawing tree of Flutter. It is a more violent and inelegant way, and it is also very difficult to use.

At this point, using the official Flutter control AndroidView is a more elegant solution. Here’s a simple demo embedded in Golden Map. Let’s follow this application scenario and see how AndroidView works.Realization principle.

demo_pic

1.2. AndroidViewUsage mode

AndroidViewSimilar to MethodChannel, it is simple and can be divided into three steps:

Step 1: Use AndroidView at the corresponding location of the dart code, and you need to pass in anviewType,This String will be used to uniquely identify the Widget and to associate it with Native View.

Step 2: Add code to the native side and write a Platform ViewFactory. The main task of the Platform ViewFactory is tocreate()The method creates a View and passes it on to Flutter (that’s not true, but we can understand it that way, which will be explained later).

Step 3: UseregisterViewFactory()Method registers the PlatformViewFactory that has just been written. This method needs to pass in two parameters. The first parameter needs to be written on the Flutter side as well as before.viewTypeCorrespondingly, the second parameter is the Platform ViewFactory just written.

The configuration of Golden Map is omitted here. Official documents are more detailed and can be consulted on the Golden Developer Platform.

These are all the operations using AndroidView. Generally speaking, they seem to be relatively simple, but there are still two problems that can not be ignored if they are really to be used.

  1. ViewWho decides the final display size?
  2. How are touch events handled?

Now let the little idle fish to answer you one by one.

2. Principle explanation

To solve the above two problems, we must first understand what is the essence of the so-called “pass View”?

2.1. What is the essence of the so-called “pass-on view”?

To solve this problem, it is inevitable to read the source code, and to look at the whole process of transmission from a deeper level, we can sort out a flow chart like this:

As we can see, what Flutter eventually gets is a textureId returned by the native layer. According to the knowledge KY h of native, this textureId is the ID corresponding to the drawing data of view rendered on the native side.After this ID can be directly found in the GPU drawing data and use, then how does Flutter use this ID?

In the previous in-depth understanding of Flutter interface development, also introduced to you the drawing process of Flutter. I’ll also give you a brief tidy up here.

FlutterThe Framework layer will finally be submitted to a layerTree in the Engine layer. In the pipeline, every leaf node of the layertree will be traversed. Each leaf node will finally call Skia engine to complete the drawing of interface elements. After the traversal is completed, g will be called.PresentRenderBuffer (IOS) or glSwapBuffer (Android) Click to complete the screen operation.

LayerThere are many kinds, and AndroidView uses TextureLayer. TextureLayer has a more detailed introduction in the previous Flutter External Texture, which is not repeated here. TextureLayerWhen traversed, an engine-level method is calledSceneBuilder::addTexture() TextureId is passed in as a parameter. In the end, Ska will find the corresponding drawing data directly in GPU according to textureId and draw it to the screen.

So who gets this ID can do this? The answer, of course, is No. Texture data is stored in the corresponding thread of the EGLContext that created it, so it is impossible to obtain the corresponding data if it is operated on by other threads. Here we need to introduce a fewConcept:

  • Display: Provide reasonable information about the pixel density and size of the display
  • Presentation:It provides Android with the ability to draw on the corresponding context and display objects, usually for dual-screen rendering.

Instead of explaining Presentation, we just need to understand that Flutter implements external texture through Presentation, and when creating Presentation, we pass in the Context corresponding to FlutterView and create it.A virtual display object is created, which enables Flutter to find texture data directly through ID and use Native to create texture data.

2.2. ViewWho decides the final display size?

As you can imagine from the above process, the display size seems to be determined by two parts: the size of Android View and the size of Android View. So who actually decides, let’s do an experiment?

Create a new Flutter project directly and change the middle to an Android View.

//Flutter
class _MyHomePageState extends State<MyHomePage> {
  double size = 200.0;

  void _changeSize() {
    setState(() {
      size = 100.0;
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: Container(
        color: Color(0xff0000ff),
        child: SizedBox(
          width: size,
          height: size,
          child: AndroidView(
            viewType: 'testView',
          ),
        ),
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: _changeSize,
        child: new Icon(Icons.add),
      ),
    );
  }
}

The Android side also needs to add the corresponding code, in order to better see the cutting effect, here use the ImageView.

//Android
@Override
public PlatformView create(final Context context, int i, Object o) {
    final ImageView imageView = new ImageView(context);
    imageView.setLayoutParams(new ViewGroup.LayoutParams(500,500));
    imageView.setBackground(context.getResources().getDrawable(R.drawable.idle_fish));
    return new PlatformView() {
        @Override
        public View getView() {
            return imageView;
        }

        @Override
        public void dispose() {

        }
    };
}

image-20181114170348189

First, look at AndroidView. The RenderObject corresponding to AndroidView is RenderAndroidView. There are two possibilities for determining the final size of a RenderObject. One is determined by the parent node.Specify, another is to determine the size of the parent node within the scope specified by its own circumstances. Open the corresponding source code and you can see that there is a very important attribute in it.sizedByParent = true,That is to say, the size of AndroidView is determined by its parent node. We can use Container, SizedBox and other controls to control the size of AndroidView.

AndroidViewThe drawing data is provided by the Native layer, so what happens when the actual pixel size of View rendered in Native is larger than that of Android View? Usually, there are only two ways to deal with this situation, one is cutting, the other is cutting.Zoom. Flutter has maintained its consistent practice that all out of the bounds widgets are presented in a tailored fashion, and the situation described above is considered an out of the bounds.

When the actual pixel size of the View is smaller than that of Android View, you will find that the View does not shrink accordingly (the background of Container is not visible), and the places without content will be filled with white. The reason for this is SingleView.Presentation:: In onCreate, a FrameLayout is used as the rootView.

2.3. How to Transfer Touch Events

AndroidEvent streams should be familiar to everyone, top-down transmission, bottom-up processing or reflux. Flutter also uses this rule, but AndroidView handles gestures through two classes:

MotionEventsDispatcher:Responsible for encapsulating events as Native events and transmitting them to Native;

AndroidViewGestureRecognizer:Responsible for identifying the corresponding gestures, which have two attributes:

cachedEventsandforwardedPointers,Only when the pointer attribute of PointerEvent is in forwarded Pointers will it be distributed, otherwise it will exist in cacheEvents. The implementation here is mainly to solve some conflicts of events, such as sliding events.Processing is done through gesture Recognizers, where you can refer to official annotations.

/// For example, with the following setup vertical drags will not be dispatched to the Android view as the vertical drag gesture is claimed by the parent [GestureDetector].
/// 
/// GestureDetector(
///   onVerticalDragStart: (DragStartDetails d) {},
///   child: AndroidView(
///     viewType: 'webview',
///     gestureRecognizers: <OneSequenceGestureRecognizer>[],
///   ),
/// )
/// 
/// To get the [AndroidView] to claim the vertical drag gestures we can pass a vertical drag gesture recognizer in [gestureRecognizers] e.g:
/// 
/// GestureDetector(
///   onVerticalDragStart: (DragStartDetails d) {},
///   child: SizedBox(
///     width: 200.0,
///     height: 100.0,
///     child: AndroidView(
///       viewType: 'webview',
///       gestureRecognizers: <OneSequenceGestureRecognizer>[ new VerticalDragGestureRecognizer() ],
///     ),
///   ),
/// )

So to sum up, this shunt process is actually very simple: the initial stage of events from Native to Flutter is beyond the scope of this article. Flutter handles events according to its own rules. If AndroidView wins events, events will beIt is encapsulated into the corresponding events on the Native side and sent back to Native through the method channel, which is then processed according to its own rules for dealing with events.

3. summary

3.1. Limitations of the scheme

Dali said: This solution is generated by Google in order to solve the contradiction between the growing business needs of developers and the backward ecological environment, which is the main contradiction that a new ecosystem must face. The easiest way to solve this problem is, of course, to allow developers to use it.Controls that are already very mature in the old ecosystem. Of course, this can temporarily solve the problem of the incomplete development of Flutter ecology, but it is inevitable to use this scheme to write two-terminal code (even now iOS has no corresponding control, of course, it will be updated later), can not really achieve.The cross port.

To put it in a nutshell: There are performance flaws in this scheme. In the third commentary of AndroidView, the official mentioned that it is a more expensive scheme to avoid using Flutter control when it can also be implemented. If you’ve seen Flu beforeStudents of the article “External Texture of Flutter” should know that the process cost of data from GPU – & gt; CPU – & gt; GPU in Flutter’s scheme of realizing external texture is relatively high, and it will cause obvious performance defects in a large number of scenarios. We go around it by some means.After the intermediate CPU step, the technology landed in APP for processing image resources.

3.2. practical application

At present, the migration of idle fish from Native to Flutter encounters the problem that Native’s local map resources can not be accessed on the Flutter side. Under the circumstance that Flutter and Native will coexist for a long time, we should copy a resource to Flutte again.The rule of R can be stored, of course, but it inevitably increases the size of the package and is not easy to manage.

Faced with this problem, our solution is to learn from AndroidView’s idea of using Texture and optimize it. Native and Flutter’s image resources are normalized. In addition to loading local images in the Native resource directory,Native’s image library can also be used to load network pictures.

The reason why we do this is that our picture library on the Native side is perfect and has withstood a lot of online tests. At this stage, we don’t want to invest too much energy in the repetition of wheel building, but the idea of dealing with network image resources and local image resources is the same.Therefore, we choose to integrate the image resources in a unified way, communicate with the official team and improve it, and then synchronize with you.

 

Original link
This article is the original content of Yunqi Community, which can not be reproduced without permission.

Leave a Reply

Your email address will not be published. Required fields are marked *