Weex 自定义 Component 开发这块,官方文档和网上示例都较少涉及。工作所需有所研究,总结此文以飨读者。
基础定义与注册
如下述代码所示,从 WXComponent 继承出来以后,复写四个构造器方法,就可以完成一个最简单可跑当然也显示不了任何东西的 WXComponet。
需要说明的是 WXComponent 可以指定泛型 T,T extends View,用于指定WXComponent hostView 根布局的类型。这个还是指定的比较好,在某些进阶用法中会需要这个类型。
public class DemoWXComponent extends WXComponent<T> {
public DemoWXComponent(WXSDKInstance instance, WXVContainer parent,
int type,
BasicComponentData basicComponentData) {
super(instance, parent, type, basicComponentData);
}
public DemoWXComponent(WXSDKInstance instance, WXVContainer parent, String instanceId, boolean isLazy,
BasicComponentData basicComponentData) {
super(instance, parent, instanceId, isLazy, basicComponentData);
}
public DemoWXComponent(WXSDKInstance instance, WXVContainer parent, boolean isLazy,
BasicComponentData basicComponentData) {
super(instance, parent, isLazy, basicComponentData);
}
public DemoWXComponent(WXSDKInstance instance, WXVContainer parent,
BasicComponentData basicComponentData) {
super(instance, parent, basicComponentData);
}
}
使用这个 Component 之前,还需要把他注册进 WXSDKEngine。如下所示:
一次注册后,在Android程序销毁之前,可以一直使用这个 Component。无需 unregister,WXSDKEngine 也没有提供 unregister 方法,这是显而易见的,因为当前还未产生任何实例。
WXSDKEngine.registerComponent("democomponent", DemoWXComponent.class);
值得一提的是 componet 名称,尽量不要下划线中划线和大写字母,否则可能会踩坑。
好,接下来是 Weex JS 代码怎么调用。无需 import,直接使用,只要 register 方法已经被执行过了,如下所示:
<democomponent />
如果想要设定 props 怎么办,如 <democomponent source=test/>。
那就在 Componet 中增加如下:
@WXComponentProp(name = "source")
public void setSource(String source) {
mSource = source;
// or do some things
}
weex 会根据外部传入的 props,根据注解调用对应 props 的 set 方法。
生命周期
显然,看了第一节,只能保证链路上 weex 自定义 Component 能跑起来,没有做其他任何事情。那么,为了能实现我们需要的渲染和其他逻辑,就需要了解 Weex Componet 的生命周期。这里的生命周期,实质就是了解可 Override 的几个 WXComponent 方法,和他们的被调用的时机。这一块官方没有任何文档,全靠去 github 源码中看和试。
必需 Override
initComponentHostView()
protected T initComponentHostView(@NonNull Context context) {
// for example
mComponentHostView = new FrameLayout(context);
mComponentHostView.setId(R.id.fragment_content);
return mComponentHostView;
}
用于生成根 View 返回给 Weex 来渲染。注意,不要在这里进行任何响应外界设入的 props 的渲染,因为此时极大可能 props 还没有被传入。
可选 Override
bindData()
@Override
public void bindData(WXComponent component) {
super.bindData(component);
// 这里进行 props 的响应渲染
}
super.bindData() 后即可响应 props 进行渲染,因为此时 props 的set方法都已经被调用过。
destroy()
@JSMethod
@Override
public void destroy() {
super.destroy();
// 进行自定义 Component 的必要销毁逻辑
}
如果有额外需要销毁的逻辑,需要写在 destroy 之中。weex 会在退出 WXActivity 或其他等同的时候调用。值得一提的是,我一般加一个 @JSMethod 注解,以提供前端 Weex 开发一个主动销毁的能力,避免需要的时候不能及时推代码生效,而要等到发版。
暴露方法
上段其实已经提到,怎样暴露一个 Component 方法给前端调用。如下所示:
@JSMethod
public void getDuration(JSCallback callback) {
if (null != getCurrentShortVideoVh() && null != callback) {
Map<String, Object> map = new HashMap<>(1);
map.put("result", "value");
callback.invoke(map);
}
}
需要注意的是,直接把 void 改成返回值比如 boolean 然后试图 return 是没有用的,weex js 侧收不到。因此,必须要去使用回调来给返回值。如上所示。
DOM
Weex 新内核(WeexCore)将 Dom 层和 Layout 引擎下沉到 C++ 层实现,移除 Java 层的 DomObject,提升渲染性能和内核的可通用性。因此,github 最新版不再可以获取到 WXComponent 中的 DomObject。
Tricks:强转
如果发现自定义 Component 的逻辑需要用到 Activity,而 WXComponent 只给你提供了 Context 的时候,不要慌,Weex 传入的 Context 其实可以强转 Activity。当然,以防万一,记得用 instance of 保护一下。
同理,如果你想要弹出一个 Fragment,结果发现自己需要一个 FragmentActivity 来getSupportFragmentManager(),不要慌,weex 传入的这个 Activity 也可以强转为
FragmentActivity,同样记得加 instance of 保护,否则业务挂了不算我的,因为这毕竟是文档中的未定义行为。