正しい姿勢フラッタでアセンブリに埋め込まれたネイティブ

入門

フラッタープロジェクト期間に混合ネイティブから長いの移行では、移行を円滑にするために、より完璧なネイティブフラッタコントロールの使用は良い選択となります。この記事では、ソリューションが埋め込まれたネイティブコンポーネントを拡張するために、これに基づいてAndroidViewとダブルエンドを使うに導入したいと考えています

1.チュートリアル

1.1. DemoRun

このシナリオでは明らかにフラッターマップSDKライブラリを提供し、マップの独自のセットを開発していない今、現実的ではない、多くのアプリケーションにマップを埋め込むする可能性があると存在し続けますが、。このシナリオでは、混合スタックの形は良い選択です。私たちは、地図描画ネイティブツリーに直接埋め込むことができますが、このプログラムは、フラッタービュー描画ツリーに埋め込まれていない、暴力のない、よりエレガントな方法で、使用も非常に激しいです。

この時、公式コントロールAndroidViewフラッターの使用は、よりエレガントなソリューションです。ここでは、デモの簡単な埋め込み高い道徳的なマップを行うために、私たちは、このシナリオに従う使用AndroidViewと実装の原則を見てみましょう。

正しい姿勢フラッタでアセンブリに埋め込まれたネイティブ

1.2。AndroidView使用

使用比較的簡単類似AndroidView MethodChannelは、3つのステップに分けられる:最初のステップ:対応する位置コードをAndroidViewダーツを使用して、ビュータイプの使用を通過する必要がある、これはと、一意の文字列のウィジェットを識別するために使用されますビューや仲間へのネイティブ。

正しい姿勢フラッタでアセンブリに埋め込まれたネイティブ
ステップ2:PlatformViewFactory主なタスクは、create()メソッドでビューを作成し、フラッターにそれを渡す(これは正確ではありませんが、のはとても理解も聞かせて、フォローアップをすることで、PlatformViewFactoryを書き、コードのネイティブ側を追加説明されます)
正しい姿勢フラッタでアセンブリに埋め込まれたネイティブ

第三段階:ちょうど書き込まPlatformViewFactoryを登録するregisterViewFactory()メソッドを使用して、この方法は、2つの引数、第一パラメータとだけ書かれたの2番目のパラメータに対応するビュータイプフラッタ端を書き込む前に必要性を必要としますPlatformViewFactory。
正しい姿勢フラッタでアセンブリに埋め込まれたネイティブ

配置高德地图的部分这里就省略不说了,官方有比较详细的文档,可以去高德开发者平台进行查阅。
以上便是使用AndroidView的所有操作,总体看起来还是比较简单的,但是真正要用起来,还是有两个无法忽视的问题:
1.View最终的显示尺寸由谁决定?2.触摸事件是如何处理的?
下面就让在下来给各位一一解答。

2. 原理讲解

想要解决上面的两个问题,首先必须得理解所谓"传View"的本质是什么?

2.1. 所谓"传View"的本质是什么?

要解决这个问题,自然避免不了的需要去阅读源码,从更深的层面去看这个传递的整个过程,可以整理出一张这样的流程图:
正しい姿勢フラッタでアセンブリに埋め込まれたネイティブ

我们可以看到,Flutter最终拿到的是native层返回的一个textureId。根据native的知识ky h这个textureId是已经在native侧渲染好了的view的绘图数据对应的ID,通过这个ID可以直接在GPU中找到相应的绘图数据并使用,那么Flutter是如何去利用这个ID的呢?我这里也给大家再简单整理一下

正しい姿勢フラッタでアセンブリに埋め込まれたネイティブ

Flutter的Framework层最后会递交给Engine层一个layerTree,在管线中会遍历layertree的每一个叶子节点,每一个叶子节点最终会调用Skia引擎完成界面元素的绘制,在遍历完成后,在调用glPresentRenderBuffer(IOS)或者glSwapBuffer(Android)按完成上屏操作。

Layer的种类有很多,而AndroidView则使用的是其中的TextureLayer。TextureLayer在被遍历到时,会调用一个engine层的方法SceneBuilder::addTexture() 将textureId作为参数传入。最终在绘制的时候,skia会直接在GPU中根据textureId找到相应的绘制数据,并将其绘制到屏幕上。

那么是不是谁拿到这个ID都可以进行这样的操作呢?答案当然是否定的,Texture数据存储在创建它的EGLContext对应的线程中,所以如果在别的线程进行操作是无法获取到对应的数据的。这里需要引入几个概念:

  • 显示屏对象(Display):提供合理的显示器的像素密度和大小的信息
  • Presentation:它给Android提供了在对应的上下文(Context)和显示屏对象(Display)上绘制的能力,通常用于双屏异显。

这里不展开讲解Presentation,我们只需要明白Flutter是通过Presentation实现了外接纹理,在创建Presentation时,传入FlutterView对应的Context和创建出来的一个虚拟显示屏对象,使得Flutter可以直接通过ID找到并使用Native创建出来的纹理数据。

2.2. View最终的显示尺寸由谁决定?

通过上面的流程大家应该都能想到,显示尺寸看起来像是由两部分决定的:AndroidView的大小,Android端View的大小。那么实际上到底是有谁来决定的呢,让我们来做一个实验?

直接新建一个Flutter工程,并把中间改成一个AndroidView。

//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),
      ),
    );
  }
}

在Android端也要加上对应的代码,为了更好地看出裁切效果,这里使用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() {

        }
    };
}

正しい姿勢フラッタでアセンブリに埋め込まれたネイティブ
首先先看AndroidView,AndroidView对应的RenderObject是RenderAndroidView,而一个RenderObject的最终大小的确定是存在两种可能,一种是由父节点所指定,还有一种是在父节点指定的范围中根据自身情况确定大小。打开对应的源码,可以看到其中有个很重要的属性sizedByParent = true,也就是说AndroidView的大小是由其父节点所决定的,我们可以使用Container、SizedBox等控件控制AndroidView的大小。
AndroidView的绘图数据是Native层所提供的,那么当Native中渲染的View的实际像素大小大于AndroidView的大小时,会发生什么呢?通常情况下,这种情况的处理思路无非就两种选择,一种是裁切,另一种是缩放。Flutter保持了其一贯的做法,所有out of the bounds的Widget统一使用裁切的方式进行展示,上面所描述的情况就被当作是一种out of the bounds。
当这个View的实际像素大小小于AndroidView的时候,会发现View并不会相应地变小(Container的背景色并没有显露出来),没有内容的地方会被白色填充。这其中的原因是SingleViewPresentation::onCreate中,会使用一个FrameLayout作为rootView。

2.3. 触摸事件如何传递

Android的事件流大家应该都很熟悉了,自顶向下传递,自底向上处理或回流。Flutter同样是使用这一规则,但是其中AndroidView通过两个类来去处理手势:
MotionEventsDispatcher:负责将事件封装成Native的事件并向Native传递;
AndroidViewGestureRecognizer:负责识别出相应的手势,其中有两个属性:

正しい姿勢フラッタでアセンブリに埋め込まれたネイティブ
PointerEventポインタがにあっcacheEventsそうでない場合は、分散forwardedPointersにプロパティに行くだけcachedEventsとforwardedPointers、。ここに達成するために、そのようなあなたが公式のノートを参照することができgestureRecognizersによって処理され、スライディングイベントとして、いくつかのイベントの紛争を解決することです。

/// 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() ],
///     ),
///   ),
/// )

だから、実際には非常に簡単であるプロセスのこの部分を要約、総括:AndroidViewがイベントに勝った場合は、この段階を舞うために元々ネイティブのイベントは、この記事の範囲を超えて、フラッターは、イベントを処理するための独自のルールに従って、イベントは次のようになります対応する終了イベントへのネイティブパッケージとネイティブによるチャネルを返し、ネイティブは、そのプロセスのルールに従ってイベントを処理します。

おすすめ

転載: blog.51cto.com/14332859/2412067