DaLei 2019-06-28
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 源码中看和试。
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 public void bindData(WXComponent component) { super.bindData(component); // 这里进行 props 的响应渲染 }
super.bindData() 后即可响应 props 进行渲染,因为此时 props 的set方法都已经被调用过。
@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 侧收不到。因此,必须要去使用回调来给返回值。如上所示。
Weex 新内核(WeexCore)将 Dom 层和 Layout 引擎下沉到 C++ 层实现,移除 Java 层的 DomObject,提升渲染性能和内核的可通用性。因此,github 最新版不再可以获取到 WXComponent 中的 DomObject。
如果发现自定义 Component 的逻辑需要用到 Activity,而 WXComponent 只给你提供了 Context 的时候,不要慌,Weex 传入的 Context 其实可以强转 Activity。当然,以防万一,记得用 instance of 保护一下。
同理,如果你想要弹出一个 Fragment,结果发现自己需要一个 FragmentActivity 来getSupportFragmentManager(),不要慌,weex 传入的这个 Activity 也可以强转为
FragmentActivity,同样记得加 instance of 保护,否则业务挂了不算我的,因为这毕竟是文档中的未定义行为。