使用PlatformView將?Android?控件view制作成Flutter插件
引言
小編最近在項目中實現相機識別人臉的功能,將 Android 封裝的控件 view 進行中轉,制作成 FlutterPlugin 提供給 flutter 項目使用。為了方便后期的知識整理,下面,用簡單的 demo 記錄 Android 控件如何封裝成 flutter 插件以及如何實現交互的過程。
1. FlutterPlugin 創(chuàng)建
第一步,創(chuàng)建一個 FlutterPlugin 項目。
2. 創(chuàng)建 Android 控件
拋磚引玉,創(chuàng)建一個簡單的自定義控件,控件內包含三個元素
layout_custom_view.xml (布局文件)
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/androidViewButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:padding="20dp"
android:text="發(fā)送數據給 flutter" />
<!--用于展示從flutter層接收的數據-->
<TextView
android:id="@+id/androidViewText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/androidViewButton"
android:layout_centerHorizontal="true"
android:padding="20dp"
android:text="" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:paddingBottom="10dp"
android:text="Android-View"
android:textSize="20dp"
android:textStyle="bold" />
</RelativeLayout>
CustomView.kt
/**
* android 渲染的自定義view 提供 flutter 使用
*/
class CustomView(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
private var textView: TextView? = null
private var onKeyEventCallback: OnKeyEventCallback? = null
init {
val rootView = LayoutInflater.from(context).inflate(R.layout.layout_custom_view, this, true)
initView(rootView)
}
private fun initView(rootView: View) {
textView = rootView.findViewById(R.id.androidViewText)
rootView.findViewById<Button>(R.id.androidViewButton).setOnClickListener {
//模擬生成一個隨機數傳遞到 flutter
val randomNum = (0..10).random()
onKeyEventCallback?.onKeyEventCallback(randomNum.toString())
}
}
fun setOnKeyEventCallback(callback: OnKeyEventCallback?) {
onKeyEventCallback = callback
}
@SuppressLint("SetTextI18n")
fun getMessageFromFlutter(message: String) {
textView?.text = "自來flutter的數據:$message"
}
}
interface OnKeyEventCallback {
fun onKeyEventCallback(message: String)
}
自定義控件進行UI繪制,顯示文本 Android-View。為了模擬雙向交互流程,控件內放置了一個按鈕用于生成隨機數模擬 android 層向 flutter 層的數據傳輸;放置了一塊文本區(qū)域用于展示從 flutter 層接收到的數據。
3. 注冊 Android 控件
在 plugin 的 onAttachToEngine 方法中對自定義控件進行注冊
class CustomAndroidViewPlugin: FlutterPlugin, ActivityAware {
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
/// 將 Android 控件進行注冊,提供 flutter 層使用
flutterPluginBinding.platformViewRegistry
.registerViewFactory(
VIEW_TYPE_ID,
CustomViewFactory(flutterPluginBinding.binaryMessenger)
)
}
...省略部分非關鍵代碼
companion object {
// 通過唯一值id進行控件注冊
private const val VIEW_TYPE_ID = "com.rex.custom.android/customView"
}
}
實際注冊的對象 CustomViewFactory 代碼如下:
class CustomViewFactory(
private val messenger: BinaryMessenger
) : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
override fun create(
context: Context?,
viewId: Int,
args: Any?
): PlatformView {
@Suppress("UNCHECKED_CAST")
val params = args as HashMap<String, Any>
return CustomViewController(
context = requireNotNull(context),
id = viewId,
messenger = messenger,
params = params
)
}
}
4. 封裝 Android 層通信交互 ‘CustomViewController’
/**
* 提供 AndroidView 與 flutter 間的交互能力
*/
class CustomViewController(
private val context: Context,
messenger: BinaryMessenger,
val id: Int,
val params: HashMap<String, Any>
) : PlatformView {
private var customView: CustomView? = null
private val channel: MethodChannel = MethodChannel(
messenger, "com.rex.custom.android/customView$id"
)
init {
// 如果需要在自定義view交互中申請監(jiān)聽權限可以加上下面這句話
// CustomShared.binding?.addRequestPermissionsResultListener(this)
channel.setMethodCallHandler(this)
params.entries.forEach {
Log.i("rex", "CustomView初始化接收入參:${it.key} - ${it.value}")
}
}
override fun getView(): View = initCustomView()
private fun initCustomView(): View {
if (customView == null) {
customView = CustomView(context, null)
customView!!.setOnKeyEventCallback(object : OnKeyEventCallback {
override fun onKeyEventCallback(message: String) {
// 將 Android 層的數據傳遞到 flutter 層
channel.invokeMethod(
"getMessageFromAndroidView",
"native - $message"
)
}
})
}
return customView!!
}
override fun dispose() {
// flutterView dispose 生命周期 在此響應
Log.i("rex", "flutterView on Dispose")
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"getMessageFromFlutterView" -> {
customView?.getMessageFromFlutter(call.arguments.toString())
result.success(true)
}
else -> result.notImplemented()
}
}
}
代碼說明
- CustomViewController 需實現 PlatformView 實現 getView 方法返回 自定義UI控件。
- 通過 MethodChannel 實現 Android - Flutter 間的交互通信能力。
- Android代碼中,自定義控件如何接收 flutter 端的方法調用? 在 onMethodCall 方法中接收來自 flutter 端的方法調用,通過方法名區(qū)分,調用指定功能。如:示例中的
getMessageFromFlutterView接收 flutter 端傳遞的數據call.arguments,然后在自定義 Android-UI 控件中展示出來customView.getMessageFromFlutter。 - Android代碼中,自定義控件如何調用 flutter 端方法? 使用方法
channel.invokeMethod(param1, param2),param1 為約定的方法名稱,如示例中的getMessageFromAndroidView, 生成一個隨機數傳遞給 flutter 端;param2 為 想要傳遞給 flutter 端的數據,數據類型可以是任意類型,示例中使用的是字符串類型。
5. 在 flutter 中如何使用已注冊的 Android 控件(view)
創(chuàng)建 custom_android_view.dart 用于包裹 Android 控件
關鍵點:通過原生層中注冊的 id 路徑獲取 AndroidView 要求:AndroidView 中 viewType 參數就是原生層中注冊的自定義控件的映射路徑,如示例中 CustomAndroidViewPlugin 內的 viewTypeId
AndroidView(
viewType: 'com.rex.custom.android/customView', //要與注冊的路徑保持一致
onPlatformViewCreated: _onPlatformViewCreated,
creationParams: const <String, dynamic>{'initParams': 'hello world'},
creationParamsCodec: const StandardMessageCodec(),
)
將 AndroidView 進行封裝,控件名稱為 CustomAndroidView ,完整代碼如下:
typedef OnViewCreated = Function(CustomViewController);
///自定義AndroidView
class CustomAndroidView extends StatefulWidget {
final OnViewCreated onViewCreated;
const CustomAndroidView(this.onViewCreated, {Key? key}) : super(key: key);
@override
State<CustomAndroidView> createState() => _CustomAndroidViewState();
}
class _CustomAndroidViewState extends State<CustomAndroidView> {
late MethodChannel _channel;
@override
Widget build(BuildContext context) {
return _getPlatformFaceView();
}
Widget _getPlatformFaceView() {
return AndroidView(
viewType: 'com.rex.custom.android/customView',
onPlatformViewCreated: _onPlatformViewCreated,
creationParams: const <String, dynamic>{'initParams': 'hello world'},
creationParamsCodec: const StandardMessageCodec(),
);
}
void _onPlatformViewCreated(int id) {
_channel = MethodChannel('com.rex.custom.android/customView$id');
final controller = CustomViewController._(
_channel,
);
widget.onViewCreated(controller);
}
}
class CustomViewController {
final MethodChannel _channel;
final StreamController<String> _controller = StreamController<String>();
CustomViewController._(
this._channel,
) {
_channel.setMethodCallHandler(
(call) async {
switch (call.method) {
case 'getMessageFromAndroidView':
// 從native端獲取數據
final result = call.arguments as String;
_controller.sink.add(result);
break;
}
},
);
}
Stream<String> get customDataStream => _controller.stream;
// 發(fā)送數據給native
Future<void> sendMessageToAndroidView(String message) async {
await _channel.invokeMethod(
'getMessageFromFlutterView',
message,
);
}
}
代碼說明
AndroidView在加載完成時會回調我們的_onPlatformViewCreated方法,小編在 _onPlatformViewCreated 方法內將methodChannel初始化,用于監(jiān)聽 Android 端的方法調用,以及后續(xù)用其調用 Android控件內封裝的方法。- 小編給 CustomAndroidView 封裝了一個 controller 控制類,在 CustomAndroidView 的構造方法中回傳給調用者,調用者可通過 controller 進行監(jiān)聽 Android 端傳送過來的數據,以及通過 controller 調用控件提供的能力方法。
如何使用這個View
展示 CustomAndroidView :
Widget _buildAndroidView() {
return CustomAndroidView(_onCustomAndroidViewCreated)
}
接收來自 Android 層的傳輸數據
void _onCustomAndroidViewCreated(CustomViewController controller) {
_controller = controller;
_controller?.customDataStream.listen((data) {
//接收到來自Android端的數據
setState(() {
receivedData = '來自Android的數據:$data';
});
});
}
通過控件發(fā)送數據給 Android 層
final randomNum = Random().nextInt(10);
_controller?.sendMessageToAndroidView('flutter - $randomNum ');
// _controller 在CustomAndroidView 的構造方法回調中獲取,如標簽2
6. 附上 example 完整代碼
example/main.dart
void main() {
runApp(const MaterialApp(home: MyHome()));
}
class MyHome extends StatelessWidget {
const MyHome({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Scaffold(
body: CustomExample(),
);
}
}
class CustomExample extends StatefulWidget {
const CustomExample({Key? key}) : super(key: key);
@override
State<CustomExample> createState() => _CustomExampleState();
}
class _CustomExampleState extends State<CustomExample> {
String receivedData = '';
CustomViewController? _controller;
void _onCustomAndroidViewCreated(CustomViewController controller) {
_controller = controller;
_controller?.customDataStream.listen((data) {
//接收到來自Android端的數據
setState(() {
receivedData = '來自Android的數據:$data';
});
});
}
Widget _buildAndroidView() {
return Expanded(
child: Container(
color: Colors.blueAccent.withAlpha(60),
child: CustomAndroidView(_onCustomAndroidViewCreated),
),
flex: 1,
);
}
Widget _buildFlutterView() {
return Expanded(
child: Stack(
alignment: AlignmentDirectional.bottomCenter,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
TextButton(
onPressed: () {
final randomNum = Random().nextInt(10);
_controller
?.sendMessageToAndroidView('flutter - $randomNum ');
},
child: const Text('發(fā)送數據給Android'),
),
const SizedBox(height: 10),
Text(receivedData),
],
),
const Padding(
padding: EdgeInsets.only(bottom: 15),
child: Text(
'Flutter - View',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
],
),
flex: 1,
);
}
@override
Widget build(BuildContext context) {
return Column(
children: [
_buildAndroidView(),
_buildFlutterView(),
],
);
}
}
如上,demo 將一個頁面均分為上下兩塊,上半部分使用 Android 控件,下半部分使用 Flutter 控件,兩組控件間進行通信交互。

demo 已上傳:github.com/liyufengrex…
以上就是使用PlatformView將 Android 控件view制成Flutter插件的詳細內容,更多關于Android view制成Flutter的資料請關注腳本之家其它相關文章!
相關文章
Android?nonTransitiveRClass資源沖突問題淺析
這篇文章主要介紹了Android?nonTransitiveRClass資源沖突問題,別搞錯了,nonTransitiveRClass不能解決資源沖突,需要的朋友們下面隨著小編來一起學習吧2022-12-12
淺析AndroidStudio3.0最新 Android Profiler分析器(cpu memory network
Android Profiler分為三大模塊: cpu、內存 、網絡。本文給大家介紹AndroidStudio3.0最新 Android Profiler分析器(cpu memory network 分析器)的相關知識,他們的基本使用方法,在文中都給大家提到,具體內容詳情大家通過本文一起學習吧2017-12-12
淺談Android開發(fā)中ListView控件性能的一些優(yōu)化方法
這篇文章主要介紹了Android開發(fā)中ListView控件性能的一些優(yōu)化方法,需要的朋友可以參考下2016-01-01
Android 2d游戲開發(fā)之貪吃蛇基于surfaceview
這篇文章主要介紹了Android 2d游戲開發(fā)基于surfaceview的貪吃蛇,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09
如何判斷軟件程序是否聯(lián)網 聯(lián)網狀態(tài)提示信息Android實現
這篇文章主要為大家詳細介紹了如何判斷軟件程序是否聯(lián)網的實現代碼,Android實現聯(lián)網狀態(tài)信息提示,感興趣的小伙伴們可以參考一下2016-05-05

