天天看點

Flutter中嵌入Android 原生TextView

​​更多文章請檢視 flutter從入門 到精通​​

本篇文章 中寫到的是 flutter 調用了Android 原生的 TextView 案例

添加原生元件的流程基本上可以描述為:

  • 1 android 端實作原生元件PlatformView提供原生view
  • 2 android 端建立PlatformViewFactory用于生成PlatformView
  • 3 android 端建立FlutterPlugin用于注冊原生元件
  • 4 flutter 平台嵌入 原生view

1 建立原生元件

建立在fLutter工程時會生成幾個檔案夾,lib是放flutter工程代碼,android和ios檔案夾分别是對應的雙平台的原生工程。

在這裡直接打開Android工程目錄,項目預設生成了GeneratedPluginRegistrant和MainActivity兩個檔案,GeneratedPluginRegistrant不要動,GeneratedPluginRegistrant是flutter中配制使用其他插件時,程式在編譯時自動進行插件注冊使用的類。

在MainActivity的包下建立自定義View,Flutter的原生View不能直接繼承自View,需要實作提供的PlatformView接口:

public class TestTextView implements PlatformView r{
  
  private final TextView mTestTextView;
  
  /**
   * 
   * @param context
   * @param messenger
   * @param id
   * @param params  初始化時 flutter 傳遞過來的參數
   */
  TestTextView(Context context, BinaryMessenger messenger, int id, Map<String, Object> params) {
    //建立 TextView
    TextView lTextView = new TextView(context);
    lTextView.setText("Android的原生TextView");
    this.mTestTextView = lTextView;
    
    //flutter 傳遞過來的參數
    if (params!=null&&params.containsKey("content")) {
      String myContent = (String) params.get("content");
      lTextView.setText(myContent);
    }
  }
  
  @Override
  public View getView() {
    return mTestTextView;
  }
  
  @Override
  public void dispose() {
  
  }
  
}      

2 建立PlatformViewFactory

import android.content.Context;
import java.util.Map;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.StandardMessageCodec;
import io.flutter.plugin.platform.PlatformView;
import io.flutter.plugin.platform.PlatformViewFactory;

public class TestViewFactory extends PlatformViewFactory {
  private final BinaryMessenger messenger;
  public TestViewFactory(BinaryMessenger messenger) {
    super(StandardMessageCodec.INSTANCE);
    this.messenger = messenger;
  }
  
  /**
   * 
   * @param context
   * @param id
   * @param args  args是由Flutter傳過來的自定義參數
   * @return
   */
  @SuppressWarnings("unchecked")
  @Override
  public PlatformView create(Context context, int id, Object args) {
    //flutter 傳遞過來的參數
    Map<String, Object> params = (Map<String, Object>) args;
    //建立 TestTextView
    return new TestTextView(context, messenger, id, params);
    
  }      

3 建立Plugin并在ManActivity中注冊插件

/**
 * flutter 調用 android 原生view
 *
 */
public class TestFluttertoAndroidTextViewPlugin {
  public static void registerWith(PluginRegistry registry) {
    //防止多次注冊
    final String key = TestFluttertoAndroidTextViewPlugin.class.getCanonicalName();
    if (registry.hasPlugin(key)) return;
    //初始化 PluginRegistry
    PluginRegistry.Registrar registrar = registry.registrarFor(key);
    //設定辨別
    registrar.platformViewRegistry().registerViewFactory("com.flutter_to_native_test_textview", new TestViewFactory(registrar.messenger()));
  }
}      

MainActivity 中注冊

import android.os.Bundle

import io.flutter.app.FlutterActivity
import io.flutter.plugins.FlutterToAndroidPlugins
import io.flutter.plugins.GeneratedPluginRegistrant

class MainActivity: FlutterActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    //flutter 項目工程中預設生成的 
    GeneratedPluginRegistrant.registerWith(this)
    //這是我們新建立的插件
    TestFluttertoAndroidTextViewPlugin.registerWith(this)
   
  }

  override fun onDestroy() {
    super.onDestroy()

  }
}      

4 flutter頁面中嵌入android 原生Textview

4.1 最簡單的調用
Flutter中嵌入Android 原生TextView
//這裡設定的 viewType值與 android 中插件注冊的辨別 一至
//registrar.platformViewRegistry().registerViewFactory("com.flutter_to_native_test_textview", new TestViewFactory(registrar.messenger()));
mTextWidget = Container(
    height: 200,
    child: AndroidView(
      //設定辨別 
        viewType: "com.flutter_to_native_test_textview",
     ),
  );

@override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: appBar,
      //顯示的頁面
      body: mTextWidget,
    );
  }      
4.2 flutter 調用 原生view并傳參數
Flutter中嵌入Android 原生TextView
mTextWidget = Container(
            height: 200,
            child: AndroidView(
              //辨別
              viewType: "com.flutter_to_native_test_textview",
              creationParams: {
                "content": "flutter 傳入的文本内容",
              },
              //參數的編碼方式
              creationParamsCodec: const StandardMessageCodec(),
            ),
          );      

android 原生中的接收(隻會接收一次)

... ... 

TestTextView(Context context, BinaryMessenger messenger, int id, Map<String, Object> params) {
    ... ..
    //flutter 傳遞過來的參數
    if (params!=null&&!params.isEmpty()&&params.containsKey("content")) {
      String myContent = (String) params.get("content");
      lTextView.setText(myContent);
    }

  ... ...
  }      
4.3 flutter 更新 原生view 中的資料

原生元件初始化的參數并不會随着setState重複指派,可以通過MethodCall來實作更新資料。

首先讓原生view元件實作MethodCallHandler接口:

public class TestTextView implements PlatformView , MethodChannel.MethodCallHandler{
  
  private final TextView mTestTextView;
  
  TestTextView(Context context, BinaryMessenger messenger, int id, Map<String, Object> params) {
    
    ... ...
    
    //com.flutter_to_native_test_view_ 是更新資料的通信辨別
    MethodChannel methodChannel = new MethodChannel(messenger, "com.flutter_to_native_test_textview_" + id);
    methodChannel.setMethodCallHandler(this);
  }
  
  ... ...
  
  @Override
  public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {

       //updateText 是flutter 中調用的方法名稱,可以随意定義
    if ("updateText".equals(methodCall.method)) {
      String text = (String) methodCall.arguments;
      this.mTestTextView .setText(text);
      //對flutter 的回調
      result.success(null);
    }
  }
}      

flutter 中調用 android 原生view

MethodChannel _channel;
  int viewId=0;      
mTextWidget = Container(
            height: 200,
            child: AndroidView(
              //辨別
              viewType: "com.flutter_to_native_test_textview",
              creationParams: {
                "content": "flutter 傳入的文本内容",
              },
              //參數的編碼方式
              creationParamsCodec: const StandardMessageCodec(),
              //view建立完成時的回調
              onPlatformViewCreated: (id) {
                viewId = id;
              },
            ),
          );      

更新資料

//這裡設定的辨別 MethodChannel('com.flutter_to_native_test_textview_$viewId');
// 與android MethodChannel methodChannel = new MethodChannel(messenger, "com.flutter_to_native_test_textview_" + id); 中注冊的一至
void clickUpdtae(){
_channel = new MethodChannel('com.flutter_to_native_test_textview_$viewId');
 updateTextView();
}

//這裡的辨別 updateText
//與android 中接收消息的方法中
//if ("updateText".equals(methodCall.method)) {...} 一至
void updateTextView() async {
    return _channel.invokeMethod('updateText', "更新内容");
 }      

通過onPlatformViewCreated回調,監聽原始元件成功建立,并能夠在回調方法的參數中拿到目前元件的id,這個id是系統随機配置設定的,然後通過這個配置設定的id加上我們的元件名稱最為字首建立一個群組件通訊的MethodChannel,拿到channel對象之後就可以通過invokeMethod方法向原生元件發送消息了,這裡這裡調用的是‘updateText’這個方法,參數是一個String

本公衆号會首發系列專題文章,付費的視訊課程會在公衆号中免費刊登,在你上下班的路上或者是睡覺前的一刻,本公衆号都是你浏覽知識幹貨的一個小選擇,收藏不如行動,在那一刻,公衆号會提示你該學習了。