自定义界面组件
App 开发中有可能使用到大量的UI组件,除了 Hippy SDK 已包括其中常用的部分,如View
、Text
、Image
等,我们仍可能需要对 UI 组件进行扩展封装。
组件扩展
我们将以MyView
为例,从头介绍如何扩展组件。
扩展组件包括:
- 扩展
HippyViewController
。 - 实现
createViewImpl方法
、 - 实现
Props
设置方法。 - 手势事件处理。
- 注册
HippyViewController
。
HippyViewController
是一个视图管理的基类(如果是ViewGroup
的组件,基类为 HippyGroupController
)。 在这个例子中我们需要创建一个 MyViewController
类,它继承 HippyViewController<MyView>
。MyView
是被管理的UI组件类型,它应该是一个 Android View
或者 ViewGroup
。 @HippyController
注解用来定义导出给JS使用时的组件信息。
实现属性 Props 方法
需要接收 JS 设置的属性,需要实现带有 @HippyControllerProps
注解的方法。
@HippyControllerProps
可用参数包括:
name
(必须):导出给JS的属性名称。defaultType
(必须):默认的数据类型。取值包括HippyControllerProps.BOOLEAN
、HippyControllerProps.NUMBER
、HippyControllerProps.STRING
、HippyControllerProps.DEFAULT
、HippyControllerProps.ARRAY
、HippyControllerProps.MAP
。defaultBoolean
:当 defaultType 为 HippyControllerProps.BOOLEAN 时,设置后有效。defaultNumber
:当 defaultType 为 HippyControllerProps.NUMBER 时,设置后有效。defaultString
:当 defaultType 为 HippyControllerProps.STRING 时,设置后有效。
@HippyControllerProps(name = "text", defaultType = HippyControllerProps.STRING, defaultString = "")
public void setText(MyView textView, String text)
{
textView.setText(text);
}
源码
UIManagerModule
hippy更新属性值的入口
@HippyMethod(name = "updateNode")
public void updateNode(int rootID, HippyArray updateArray) {
...
/* 分析1 */
domManager.updateNode(id, props, hippyRootView);
}
- */ 分析1 */
DomManager
**更新具体节点的属性值
public void updateNode(final int id, HippyMap map, HippyRootView hippyRootView) {
DomNode node = mNodeRegistry.getNode(id);
...
/* 分析2 */
mDomStyleUpdateManager.updateStyle(node, hippyMap);
...
}
- */ 分析2 */
DomUpdateManager
**具体更新逻辑
public void updateStyle(T t, HippyMap hippyMap) {
// 若无更新逻辑则结束
if (hippyMap == null) {
return;
}
// 优先从缓存中读取,若无则更新缓存
Class cla = t.getClass();
Map<String, StyleMethod> methods = CLASS_STYLE_METHOD.get(cla);
if (methods == null) {
/* 分析3 */
methods = findStyleMethod(cla);
}
Set<String> styles = hippyMap.keySet();
for (String style : styles) {
StyleMethod styleMethodHolder = methods.get(style);
// 如果 methods 中不存在名为 style 的方法,说明该组件不支持该样式
// 但是如果该样式名为 "style",则说明是样式集合,需要进一步调用 updateStyle 方法进行更新。
if (styleMethodHolder != null) {
{
try {
// 若 hippyMap 中没有 style 的映射就根据 styleMethodHolder 中保存的默认值调用方法,
// 否则根据 hippyMap 中保存的值调用方法
if (hippyMap.get(style) == null) {
switch (styleMethodHolder.mDefaultType) {
case HippyControllerProps.BOOLEAN:
styleMethodHolder.mMethod.invoke(t, styleMethodHolder.mDefaultBoolean);
break;
case HippyControllerProps.NUMBER:
styleMethodHolder.mMethod.invoke(t,
ArgumentUtils.parseArgument(styleMethodHolder.mParamTypes[0],
styleMethodHolder.mDefaultNumber));
break;
case HippyControllerProps.STRING:
styleMethodHolder.mMethod.invoke(t, styleMethodHolder.mDefaultString);
break;
default:
Object o = null;
//noinspection ConstantConditions
styleMethodHolder.mMethod.invoke(t, o);
break;
}
} else {
styleMethodHolder.mMethod.invoke(t,
ArgumentUtils.parseArgument(styleMethodHolder.mParamTypes[0], hippyMap, style));
}
} catch (Throwable e) {
LogUtils.e("ControllerUpdateManager", e.getMessage(), e);
e.printStackTrace();
}
}
} else {
if (hippyMap.get(style) instanceof HippyMap && style.equals(NodeProps.STYLE)) {
updateStyle(t, (HippyMap) hippyMap.get(style));
}
}
}
}
- */ 分析3 */
DomUpdateManager
**查找给定类中所有 @HippyControllerProps 注解的方法
/* 分析3 */
private Map<String, StyleMethod> findStyleMethod(Class cla) {
Map<String, StyleMethod> hashMap = new HashMap<>();
findStyleMethod(cla, hashMap);
return hashMap;
}
private void findStyleMethod(Class cls, Map<String, StyleMethod> hashMap) {
// 首先从顶级父类查找使用 @HippyControllerProps 注解的方法
if (cls != StyleNode.class) {
findStyleMethod(cls.getSuperclass(), hashMap);
}
// 优先使用缓存,若无缓存则通过反射将带有 @HippyControllerProps 注解的方法添加到缓存以及 结果map 中
// 若有缓存则将缓存添加到 结果map 中
Map<String, StyleMethod> methodHolder = CLASS_STYLE_METHOD.get(cls);
if (methodHolder == null) {
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
HippyControllerProps controllerProps = method.getAnnotation(HippyControllerProps.class);
if (controllerProps != null) {
String style = controllerProps.name();
StyleMethod propsMethodHolder = new StyleMethod();
propsMethodHolder.mDefaultNumber = controllerProps.defaultNumber();
propsMethodHolder.mDefaultType = controllerProps.defaultType();
propsMethodHolder.mDefaultString = controllerProps.defaultString();
propsMethodHolder.mDefaultBoolean = controllerProps.defaultBoolean();
propsMethodHolder.mMethod = method;
propsMethodHolder.mParamTypes = method.getGenericParameterTypes();
hashMap.put(style, propsMethodHolder);
}
}
// put to CLASS_PROPS_METHOD
CLASS_STYLE_METHOD.put(cls, new HashMap<>(hashMap));
} else {
hashMap.putAll(methodHolder);
}
}