前几篇我们对flutter中的数据的传递层MethodChannel和监听响应层EventChannel进行了全面的介绍和案例展示,本篇
开始讲解flutter中如何显示原生View,如Android 中的AndroidView的显示和iOS中的UiKitView的显示过程
来吧,开始~~~展示!
四.flutter代码中显示原生View
1.显示原生View的原理说明
1.1.AndroidView和UiKitView
顾名思义,flutter为了兼容原生的安卓View在flutter中显示用AndroidView来统一替代所有需要显示在flutter中的安卓view,iOS同理用的是UiKitView,简单来说,这里的AndroidView和UiKitView相当于是原生的一个小仓库,所有的原生view都必须转换为flutter对应的AndroidView或UiKitView,至于具体的比如UiKitView有什么功能,比如UILable,UIButton则依赖于原生自身的特性决定其功能。
1.2.唯一标识key值的设定
同前面我们讲的MethodChannel和EventChannel调用机制,即通过唯一标识符在App启动页或者原生插件页中一开始进行相应的注册操作,然后在flutter中对其绑定同样的key,如此可根据key的一致性,来找到原生和flutter的调用入口,同理这里的原生View展示在flutter的流程也需要保持唯一标识key的统一。
如下所示为flutter代码中对安卓和iOS设置的key值为native_view_show字符标记。
**const** String viewType = **'native_view_show'**;
*// print("tempcreationParams = $tempcreationParams");*
**return** Container(
width: ScreenUtils.*getScreeenWidth*(context),
height: ScreenUtils.*getScreeenHeidth*(context),
alignment: Alignment.*center*,
color: Colors.*white*,
child: defaultTargetPlatform == TargetPlatform.**android** ? AndroidView(
viewType: viewType,
layoutDirection: TextDirection.**ltr**,
creationParams: {
**"key"**: **"~~native-android"**,
},
creationParamsCodec: **const** StandardMessageCodec(),
) : UiKitView(
viewType: viewType,
layoutDirection: TextDirection.**ltr**,
creationParams: {
**"key"**: **"~~native-iOS"**,
},
creationParamsCodec: **const** StandardMessageCodec(),
),
);
如下为以安卓为例的原生类FlutterPluginDemo2Plugin.java中加入需要注册原生界面时标记该key的代码如下所示,在页面启动页的onAttachedToEngine 方法中
*//* 注册原生插件类
flutterPluginBinding
.getPlatformViewRegistry()
.registerViewFactory(**"native_view_show"**, **new** NativeViewFactory());
1.3.原生端的工厂类创建
同上所示,在一开始注册原生插件的时候,需要创建NativeViewFactory.java类,继承自PlatformViewFactory类,在该类里面初始化创建NativeView.java类,继承自PlatformView类,之后在工厂类里面创建NativeView.java类,在NativeView.java类里面对要展示的view进行基本初始化和功能操作。
如此即可实现最终在如安卓的NativeView.java中显示的view功能显示到flutter代码中。
2.同原生代码交互示例
在前面我们对flutter中显示原生的原理进行研究说明后,接下来我们将以最简单的原生组件如何顺利展示在flutter中,进行案例讲解。
那么,首先上场的是安卓端同flutter的交互展示。
2.1.安卓的显示与传值
2.1.1.Flutter端
在example/lib/show_native_page.dart类中在build方法中返回 AndroidView 代码如下所示
AndroidView(
viewType: viewType,
layoutDirection: TextDirection.**ltr**,
creationParams: {
**"key"**: **"~~native-android"**,
},
creationParamsCodec: **const** StandardMessageCodec(),
)
其中这里的viewType是定义同安卓交互的唯一字符串标识如下
**const** String viewType = **'native_view_show'**;
2.1.2.Native(Android)端
1.在原生安卓端的FlutterPluginDemo2Plugin.java,类中的onAttachedToEngine方法中提前注册好原生插件NativeViewFactory工厂类;
*//* 注册原生插件类
flutterPluginBinding
.getPlatformViewRegistry()
.registerViewFactory(**"native_view_show"**, **new** NativeViewFactory());
2.工厂类NativeViewFactory.java中初始化加入NativeView.java类
代码如下所示:
**class** NativeViewFactory **extends** PlatformViewFactory {
NativeViewFactory() {
**super**(StandardMessageCodec.INSTANCE);
}
@NonNull
@Override
**public** PlatformView create(@NonNull Context context, **int** id, @Nullable Object args) {
**final** Map creationParams = (Map) args;
Log.d(**"creationParams"**, creationParams.toString());
*// System.out.print(creationParams);*
**return new** NativeView(context, id, creationParams);
}
}
3.在NativeView.java类中自定义安卓的view(如示例所示为一段textView对象居中显示效果),
如此就可以通过以上方法把安卓的TextView正确显示在flutter的界面上,实现我们的目标。
2.2.iOS的显示与传值
2.2.1.Flutter端
同安卓端所示,在 example/lib/show_native_page.dart类中在build方法中返回 UiKitView 代码所示
UiKitView(
viewType: viewType,
layoutDirection: TextDirection.**ltr**,
creationParams: {
**"key"**: **"~~native-iOS"**,
},
creationParamsCodec: **const** StandardMessageCodec(),
)
2.2.2.Native(iOS)端
同安卓端类似,iOS的原生端流程可以简单概括为,
工厂类的注册——>工厂类的初始化创建——>原生iOSView的展示
1.工厂类的注册
在iOS的 FlutterPluginDemo2Plugin.m类的 registerWithRegistrar方法中提前注册好工厂类FlutterNativeFactory,保证是同flutter的唯一标识字符串相同,如下为native_view_show
FlutterNativeFactory* factory =
[[FlutterNativeFactory alloc] initWithMessenger:registrar.messenger];
[registrar registerViewFactory:factory withId:**@"native_view_show"**];
2.工厂类的初始化创建
在FlutterNativeFactory.h类中遵循FlutterPlatformViewFactory协议,实现其createWithFrame方法,如下所示
- (NSObject*)createWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id **_Nullable**)args {
**return** [[FlutterNativeView alloc] initWithFrame:frame
viewIdentifier:viewId
arguments:args
binaryMessenger:_messenger];
}
在FlutterNativeFactory.m类中设置初始化入口initWithMessenger方法,供FlutterPluginDemo2Plugin.m类使用
3.原生iOS的View的展示
在原生的FlutterNativeView类中的initWithFrame方法中初始化iOS的view,通过以上的流程顺利展示在flutter界面上
- (instancetype)initWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id **_Nullable**)args
binaryMessenger:(NSObject*)messenger {
**if** (self = [super init]) {
_view = [[UIView alloc] init];
*// double kScreenW =* [*UIScreen mainScreen*]*.bounds.size.width;*
*// double kScreenH =* [*UIScreen mainScreen*]*.bounds.size.height;*
_view.frame = CGRectMake(0, 200, 200, 200);
_view.backgroundColor = [UIColor redColor];
NSLog(**@"args = %@"**,args);
**if** ([args isKindOfClass:[NSDictionary **class**]]) {
NSString *key = args[**@"key"**];
NSLog(**@"key = %@"**);
_nameLable.text = [NSString stringWithFormat:**@"%@%@%@"**,**@"我是"**,key,**@"的UILabel"**];
}
[_view addSubview:self.nameLable];
}
**return** self;
}
3.注意事项细节
3.1.唯一标识key的统一
虽然有点啰嗦,但仍需要再次说明,即在example/lib/show_native_page.dart类中设置viewType的值
**const** String viewType = **'native_view_show'**;
要和如下所示(以安卓为例)的FlutterPluginDemo2Plugin.java类中的onAttachedToEngine方法注册的插件view工厂类的key保持一致
*//* 注册原生插件类
flutterPluginBinding
.getPlatformViewRegistry()
.registerViewFactory(**"native_view_show"**, **new** NativeViewFactory());
3.2.传值问题
如下所示为安卓的AndroidView中的creationParams属性可以设置flutter要传递给原生的内容,在经过层层传递后可以实现在安卓的NativeView.java类的构造方法中获取到传递的内容为creationParams对象,从而展示在原生的TextView对象身上。
NativeView(@NonNull Context context, **int** id, @Nullable Map creationParams) {
String value = creationParams.get(**"key"**).toString();
**textView** = **new** TextView(context);
**textView**.setTextSize(30);
**textView**.setBackgroundColor(Color.rgb(255, 255, 255));
**textView**.setGravity(Gravity.CENTER);
**textView**.setText(**"show Android view (原生接收到的值为: "** + value + **")"**);
}
3.3.iOS原生界面展示细节说明
需要注意的是在iOS中的最外层界面展示尺寸方面,仅对位置定义有用,对尺寸设置是没用的,会默认铺满剩余空间,所以如下所示为安卓的最外层界面展示内容:
_view.center = CGPointMake(200, 150);
_view.bounds = CGRectMake(0, 0, 100, 100);
_view.backgroundColor = [UIColor redColor];
而对_view的subview如nameLable并没有影响,可以正常按照iOS的规则进行相关设置操作
**self**.nameLable.frame = CGRectMake(0, 100, 200, 50);
**self**.nameLable.backgroundColor = [UIColor blueColor];
[_view addSubview:**self**.nameLable];
简而言之,即为设置原生最外层尺寸时,仅设置其定位位置,如frame的前2项x和y或者是center对象,而对其尺寸方面的设置是无效的,因为它会默认铺满剩余尺寸,但是对该view的子view设置尺寸按照正常的iOS规则是可以生效的。
这是按照上面设置父View和子View后的效果如下所示,安卓是不是遵循同样的规则,还请小伙伴们自行验证哈。
好了,经过前面的研究和分析,我们对flutter同原生的数据层交互的MethodChannel,EventChannel,以及界面层交互的AndroidView 和 UiKitView的基础使用基本掌握,并且对二者交互的原理得到进一步的了解,这里附上在此系列中写的所有代码的一个小案例,以饷食者!
Flutter同原生交互案例展示:flutter同原生交互Demo示例
下一篇我们将逐步尝试从简单编写一个小插件,小插件的本地,远程发布,以及如何使用的流程进行整体的说明,从而让大家都能编写好自己的小插件,尽可能的解决更多flutter解决不了的原生问题,提供开发速度。
flutter插件基础之调用EventChannel的简单使用(三)
好了,本篇完~~