Weex组件的事件绑定发生在WXComponent的方法applyLayoutAndEvent上:
public void applyLayoutAndEvent(WXComponent component) {
if(!isLazy()) {
if (component == null) {
component = this;
}
setLayout(component.getDomObject());
setPadding(component.getDomObject().getPadding(), component.getDomObject().getBorder());
addEvents();
}
}
提取和component一一对应的mDomObj对象,设置layout和padding。最后设置事件到自己的成员变量set中:
private void addEvents() {
int count = mDomObj.getEvents().size();
for (int i = 0; i < count; ++i) {
addEvent(mDomObj.getEvents().get(i));
}
setActiveTouchListener();
}
获取mDomObj所有的事件,设置到addEvent
public void addEvent(String type) {
if (TextUtils.isEmpty(type)) {
return;
}
mAppendEvents.add(type);
View view = getRealView();
/**
* 处理1.CLICK 2.FOCUS 3.手势 4.滑动
*/
if (type.equals(Constants.Event.CLICK) && view != null) {
addClickListener(mClickEventListener);
}
else if ((type.equals( Constants.Event.FOCUS) || type.equals( Constants.Event.BLUR)) ) {
addFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(boolean hasFocus) {
Map<String, Object> params = new HashMap<>();
params.put("timeStamp", System.currentTimeMillis());
fireEvent(hasFocus ? Constants.Event.FOCUS : Constants.Event.BLUR, params);
}
});
} else if (view != null && needGestureDetector(type)) {
if (view instanceof WXGestureObservable) {
if (wxGesture == null) {
wxGesture = new WXGesture(this, mContext);
}
mGestureType.add(type);
((WXGestureObservable) view).registerGestureListener(wxGesture);
} else {
WXLogUtils.e(view.getClass().getSimpleName() + " don't implement " +
"WXGestureObservable, so no gesture is supported.");
}
}
else {
Scrollable scroller = getParentScroller();
if (type.equals(Constants.Event.APPEAR) && scroller != null) {
scroller.bindAppearEvent(this);
}
if (type.equals(Constants.Event.DISAPPEAR) && scroller != null) {
scroller.bindDisappearEvent(this);
}
}
}
模拟普通的点击事件触发流程:
点击对应view,事件触发
private OnClickListener mClickEventListener = new OnClickListener() {
@Override
public void onHostViewClick() {
Map param= WXDataStructureUtil.newHashMapWithExpectedSize(1);
Map position = WXDataStructureUtil.newHashMapWithExpectedSize(4);
int[] location = new int[2];
mHost.getLocationOnScreen(location);
position.put("x", WXViewUtils.getWebPxByWidth(location[0],mInstance.getViewPortWidth()));
position.put("y", WXViewUtils.getWebPxByWidth(location[1],mInstance.getViewPortWidth()));
position.put("width", WXViewUtils.getWebPxByWidth(mDomObj.getLayoutWidth(),mInstance.getViewPortWidth()));
position.put("height", WXViewUtils.getWebPxByWidth(mDomObj.getLayoutHeight(),mInstance.getViewPortWidth()));
param.put(Constants.Name.POSITION, position);
fireEvent(Constants.Event.CLICK,param);
}
};
将这次点击事件对应的view坐标和宽高打包成param,以fireEvent()发送出去之后。
传递路径:fireEvent()->WXBridgeManager.getInstance().fireEventOnNode():
public void fireEventOnNode(final String instanceId, final String ref,
final String type, final Map data,final Map domChanges) {
/**
* 1.第一步,addJSTask()将刚才的事件再做了一次封装,扔到mNextTickTasks队列去等待执行
* 2.第二步,sendMessage()发送CALL_JS_BATCH消息,从mNextTickTasks取出所有事件进行处理
*/
addJSTask(METHOD_FIRE_EVENT, instanceId, ref, type, data,domChanges);
sendMessage(instanceId, WXJSBridgeMsgType.CALL_JS_BATCH);
}
对应的Handler也是WXBridgeManager自身,handlerMessage()后转到invokeCallJSBatch去处理()
private void invokeCallJSBatch(Message message) {
try {
Object instanceId = message.obj;
Object task = null;
Stack instanceStack = mNextTickTasks.getInstanceStack();
int size = instanceStack.size();
for (int i = size - 1; i >= 0; i--) {
instanceId = instanceStack.get(i);
task = mNextTickTasks.remove(instanceId);
if (task != null && !((ArrayList) task).isEmpty()) {
break;
}
}
task = ((ArrayList) task).toArray();
WXJSObject[] args = {new WXJSObject(WXJSObject.String, instanceId), new WXJSObject(WXJSObject.JSON, WXJsonUtils.fromObjectToJSONString(task))};
/**
* 起主要作用,后面会发起IWXBridge 与JS 引擎进行 jni交互
*/
invokeExecJS(String.valueOf(instanceId), null, METHOD_CALL_JS, args);
} catch (Throwable e) {
}
// If task is not empty, loop until it is empty
if (!mNextTickTasks.isEmpty()) {
mJSHandler.sendEmptyMessage(WXJSBridgeMsgType.CALL_JS_BATCH);
}
}
循环取出所有需要执行的指令,一个个发起调用.
然后等待JS 引擎处理完毕,通过IWXBridge的callNative()返回处理结果,而IWXBridge的通常实例WXBridge最后也会交给WXBridgeManager的callNative()进行处理,而WXBridgeManager会根据instanceId,也就是WxSDKInstance对应的instanceId交给对应的WxDomModule来处理:
WXDomModule dom = getDomModule(instanceId);
dom.callDomMethod(task);
public void callDomMethod(JSONObject task) {
if (task == null) {
return;
}
String method = (String) task.get(WXBridgeManager.METHOD);
JSONArray args = (JSONArray) task.get(WXBridgeManager.ARGS);
callDomMethod(method, args);
}
public Object callDomMethod(String method, JSONArray args) {
if (method == null) {
return null;
}
try {
switch (method) {
case CREATE_BODY:
if (args == null) {
return null;
}
createBody((JSONObject) args.get(0));
break;
case UPDATE_ATTRS:
if (args == null) {
return null;
}
updateAttrs((String) args.get(0), (JSONObject) args.get(1));
break;
//...省略其他cases
}
} catch (IndexOutOfBoundsException e) {
} catch (ClassCastException cce) {
}
return null;
}
具体按照V-Dom的变化发送对应的改变事件,这边简单的通过模拟点击图片,改变一个文本的内容,所以会发指令集UPDATE_FINISH+UPDATE_ATTRS过来,WxDomModule将对应的指令发给WxDomHandler,由handler具体负责后面的渲染重绘工作。后面的处理流程跟渲染过程差不多,不再细谈。