自定义界面组件
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使用时的组件信息。
手势事件处理
Hippy手势处理,复用了 Android 系统手势处理机制。扩展组件时,需要在控件的 onTouchEvent
添加部分代码,JS才能正常收到 onTouchDown
、onTouchMove
、onTouchEnd
事件。事件的详细介绍,参考 Hippy 事件机制。
@Override
public NativeGestureDispatcher getGestureDispatcher()
{
return mGestureDispatcher;
}
@Override
public void setGestureDispatcher(NativeGestureDispatcher dispatcher)
{
mGestureDispatcher = dispatcher;
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
boolean result = super.onTouchEvent(event);
if (mGestureDispatcher != null)
{
result |= mGestureDispatcher.handleTouchEvent(event);
}
return result;
}
源码
- 手势事件处理的入口
mGestureDispatcher.handleTouchEvent(event);
public boolean handleTouchEvent(MotionEvent event) {
if (mGestureProcessor == null) {
mGestureProcessor = new NativeGestureProcessor(this);
}
/* 分析1 */
return mGestureProcessor.onTouchEvent(event);
}
- / 分析1 /
NativeGestureProcessor
针对安卓的事件机制,对不同的事件做了不同的处理,有两个重要方法:needHandle
: 判断是否需要处理对应的事件handle
: 对事件进行相应的处理
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
boolean handle = false;
switch (action) {
case MotionEvent.ACTION_DOWN: {
if (mCallback.needHandle(NodeProps.ON_PRESS_IN)) {
mNoPressIn = false;
mLastPressInX = event.getX();
mLastPressInY = event.getY();
getGestureHandler().sendEmptyMessageDelayed(PRESS_IN, TAP_TIMEOUT);
handle = true;
} else {
mNoPressIn = true;
}
if (/* 分析2 a */mCallback.needHandle(NodeProps.ON_TOUCH_DOWN)) {
/* 分析2 b */mCallback.handle(NodeProps.ON_TOUCH_DOWN, event.getX(), event.getY());
handle = true;
}
...
}
return handle;
}
- /* 分析2 */
NativeGestureDispatcher
:needHandle
&handle
/* 分析2 a */
// mGestureTypes 内包含则需要处理
public boolean needHandle(String type) {
...
boolean result = mGestureTypes.contains(type);
...
return result;
...
}
/* 分析2 b */
// 对几种不同的事件进行了默认处理, 我们以 handleTouchDown 为例
public void handle(String type, float x, float y) {
if (mTargetView == null) {
LogUtils.e("NativeGestureDispatcher", "handle!!! but view is null!!!!");
return;
}
if (TextUtils.equals(type, NodeProps.ON_PRESS_IN)) {
handlePressIn(mEngineContext, mTargetView.getId());
} else if (TextUtils.equals(type, NodeProps.ON_PRESS_OUT)) {
handlePressOut(mEngineContext, mTargetView.getId());
} else if (TextUtils.equals(type, NodeProps.ON_TOUCH_DOWN)) {
NativeGestureDispatcher
.handleTouchDown(mEngineContext, mTargetView.getId(), x, y, mTargetView.getId());
} else if (TextUtils.equals(type, NodeProps.ON_TOUCH_MOVE)) {
NativeGestureDispatcher
.handleTouchMove(mEngineContext, mTargetView.getId(), x, y, mTargetView.getId());
} else if (TextUtils.equals(type, NodeProps.ON_TOUCH_END)) {
NativeGestureDispatcher
.handleTouchEnd(mEngineContext, mTargetView.getId(), x, y, mTargetView.getId());
} else if (TextUtils.equals(type, NodeProps.ON_TOUCH_CANCEL)) {
NativeGestureDispatcher
.handleTouchCancel(mEngineContext, mTargetView.getId(), x, y, mTargetView.getId());
}
}
// 将事件名称以及触点坐标发送给前端
public static void handleTouchDown(HippyEngineContext context, int mTagId, float x, float y,
int viewId) {
int[] viewCoords = new int[2];
getLocationInWindow(context, viewId, viewCoords);
HippyMap params = new HippyMap();
params.pushString(KEY_EVENT_NAME, NodeProps.ON_TOUCH_DOWN);
params.pushInt(KEY_TAG_ID, mTagId);
params.pushDouble(KEY_PAGE_X, PixelUtil.px2dp(viewCoords[0] + x));
params.pushDouble(KEY_PAGE_Y, PixelUtil.px2dp(viewCoords[1] + y));
context.getModuleManager().getJavaScriptModule(EventDispatcher.class)
.receiveNativeGesture(params);
}