Event分类
1、fireEvent
主要是针对某个组件,发出对应的事件(一般在组件内部用),从而触发js中的自定义事件。
<text test-id="button" class="btn" @click="handleOnclick">{{btnTitle}}</text>
如上述代码中的 “@click�” 事件,对应到android 中就是通过View.OnClickListener来实现
具体的实现方案见 WxComponent#OnClickListenerImp
private class OnClickListenerImp implements OnClickListener{
@Override
public void onHostViewClick() {
Map<String, Object> param = WXDataStructureUtil.newHashMapWithExpectedSize(1);
Map<String, Object> position = WXDataStructureUtil.newHashMapWithExpectedSize(4);
int[] location = new int[2];
mHost.getLocationOnScreen(location);
//拼接参数
position.put("x", WXViewUtils.getWebPxByWidth(location[0], mInstance.getInstanceViewPortWidth()));
position.put("y", WXViewUtils.getWebPxByWidth(location[1], mInstance.getInstanceViewPortWidth()));
position.put("width", WXViewUtils.getWebPxByWidth(getLayoutWidth(), mInstance.getInstanceViewPortWidth()));
position.put("height", WXViewUtils.getWebPxByWidth(getLayoutHeight(), mInstance.getInstanceViewPortWidth()));
param.put(Constants.Name.POSITION, position);
//发射事件给js处理
fireEvent(Constants.Event.CLICK, param);
}
};
2、fireModuleEvent
主要是针对某个Module处理事件,当注册了某个组件后,js中就可以像全局事件一样监听,使用
发事件->java
//WeexModule 代表已经注册的组件名字
if (mWXSDKInstance.checkModuleEventRegistered("WeexModule",this)) {
//拼接组件携带的参数
HashMap<String,Object> params=new HashMap<>();
params.put("param1","param1");
params.put("param2","param2");
params.put("param3","param3");
//像WeexModule中发射组件事件
mWXSDKInstance.fireModuleEvent("WeexModule", this, params);
}
收事件->js�
weex.requireModule('WeexModule').addEventListener("WeexModule",function (e) {
//JSON.stringify(e) 可以将传的参数解析成string
输出示例:
{"data":{"param3":"param3","param1":"param1","param2":"param2"},"module":"WeexModule","type":"WeexModule"}
})
1、通过weex.requireModule(‘WeexModule’)先获取到注册的module
2、通过addEventListener添加对某个组件的事件监听
�
3、fireGlobalEventCallback
全局事件,随时随地发送,只要js端监听即可。其实与module event类似,只是预先注册了而已
发事件->java
默认情况下已经在weex初始化的时候注册了名为”globalEvent”的组件
registerModule("globalEvent",WXGlobalEventModule.class);
�
发送:
//参数准备
Map<String,Object> params=new HashMap<String,Object>();
params.put("eventParam","value");
//event表示在js中注解监听的js全局事件名字
mWXSDKInstance.fireGlobalEventCallback(event, params);
收事件->js
//globalEvent添加对showText事件的监听,也就是上述提到的event名字
weex.requireModule("globalEvent").addEventListener("showText", function (e) {
nativeModule.logger("globalEvent --> " + e.eventParam)
});
js处理native回调
当js需要native有callback回调时,需要在native 的方法中传参 JSCallback
@JSMethod(uiThread = true)
public void fireNativeGlobalEvent(String event, JSCallback callback){
Map<String,Object> params=new HashMap<String,Object>();
params.put("eventParam","value");
//发事件
mWXSDKInstance.fireGlobalEventCallback(event, params);
if(null != callback){
//回调事件给js
Map<String, Object> result = new HashMap<>();
result.put("ok", true);
callback.invoke(result);
}
}
�js中定义对应的function处理callback
weex.requireModule('WeexModule').fireNativeGlobalEvent("showText", function (e) {
if (Boolean(e.ok)) {//获取上面传递的参数key=ok
。。。
}
})
Event分发流程
native分发
无论上述三种event的如何分发,最终都会执行到
WxBridgeManager#invokeCallJSBatch
WxBridgeManager#invokeExecJS
WxBridge#execJS
WxBridge#nativeExecJS
通用处理Android到JavaScript的调用,都位于WxBridgeManager中
* <li>{@link #createInstance(String, String, Map, String)}</li>
* <li>{@link #destroyInstance(String)}</li>
* <li>{@link #refreshInstance(String, WXRefreshData)}</li>
* <li>{@link #registerModules(Map)}</li>
* <li>{@link #registerComponents(List)}</li>
* <li>{@link #invokeCallJSBatch(Message)}</li>
�invokeCallJSBatch只是其中一种调用方式,涉及到method为
METHOD_CALLBACK = “callback”;
METHOD_FIRE_EVENT = “fireEvent”;
METHD_FIRE_EVENT_SYNC = “fireEventSync”;
METHD_COMPONENT_HOOK_SYNC = “componentHook”;
//异步处理,按照 instanceId 分组在 一个map(mNextTickTasks)
addJSTask(METHOD_CALLBACK, instanceId, callback, data, keepAlive);
//消息队列里面去处理
sendMessage(instanceId, WXJSBridgeMsgType.CALL_JS_BATCH);
js接收&处理
javeScript端的源码在 receiver.js 中
/**
* Accept calls from native (event or callback).
* 接收来之native的 event或者 callback
* @param {string} id
* @param {array} tasks list with `method` and `args`
*/
export function receiveTasks (id, tasks) {
const document = getDoc(id)
if (!document) {
return new Error(`[JS Framework] Failed to receiveTasks, `
+ `instance (${id}) is not available.`)
}
if (Array.isArray(tasks)) {
return tasks.map(task => {
switch (task.method) {
case 'callback': return callback(document, ...task.args)
case 'fireEventSync':
case 'fireEvent': return fireEvent(document, ...task.args)
case 'componentHook': return componentHook(document, ...task.args)
}
})
}
}
fireEvent事件最终在是 document.fireEvent
处理 ;document是用过instanceId获取的node
function fireEvent (document, nodeId, type, event, domChanges, params) {
const el = document.getRef(nodeId)
if (el) {
//原生js事件发送event..
return document.fireEvent(el, type, event, domChanges, params)
}
return new Error(`invalid element reference "${nodeId}"`)
}
document.fireEvent
就对应到了 js中只要定义了 addEventListener 的监听
document.addEventListener('eventName',function(event){
}}
Event整理&分析
所有的事件常量都定义在 Constants#Event
中
/**
* 绘制view时抛异常导致失败
* 分发事件见{@link com.taobao.weex.ui.view.WXFrameLayout#dispatchDraw(Canvas)}
*/
String LAYEROVERFLOW = "layeroverflow";
/**
* 点击view事件
* 分发处见{@link com.taobao.weex.ui.component.WXComponent#OnClickListenerImp}
*/
String CLICK = "click";
/**
* focus和blur都是focus事件
* 分发处见{@link com.taobao.weex.ui.component.WXComponent#addFocusChangeListener(WXComponent.OnFocusChangeListener)}
*/
String FOCUS = "focus";
String BLUR = "blur";
/**
* 所有的手势事件定义都在 {@link com.taobao.weex.ui.view.gesture.WXGestureType}中,都是
* 通过view的onTouchListener实现 {@link WXComponent#needGestureDetector(String)}
* 手势分为:
* LowLevelGesture: touchstart,touchmove,touchend,touchcancel
* HighLevelGesture:swipe,longpress,panstart,panmove,panend,horizontalpan,verticalpan
* TODO 具体的实现待分析
*/
/**
* 目前的方式只能发生在可滚动的view中,也就是继承 {@link com.taobao.weex.ui.component.Scrollable}
* 然后区分是何种可滚动的view,比如ScrollView,RecyclerView。然后监听滚动事件的发生,并且通过滚动的方向
* 来计算当前的View是否{@linkplain #APPEAR}或者 {@linkplain #DISAPPEAR},具体的例子可见
* {@link com.taobao.weex.ui.component.WXScroller#addEvent(String)} 和
* {@link com.taobao.weex.ui.component.list.template.WXRecyclerTemplateList#addEvent(String)}
* 其中也处理了像{@linkplain #SCROLL},{@linkplain #SCROLL_START},{@linkplain #SCROLL_END}事件
* 个人觉得适配很麻烦。可以参考我之前在视频开发时设计的无侵入滚动监听的方式 - 监听 onLayoutChangeListener
*/
String APPEAR = "appear";
String DISAPPEAR = "disappear";
String SCROLL = "scroll";
String SCROLL_START = "scrollstart";
String SCROLL_END = "scrollend";
/**
* 此两个事件在{@link android.app.Activity} 声明周期回调时发射的事件
* {@link WXSDKInstance#onActivityResume()} -- viewappear
* {@link WXSDKInstance#onActivityPause()} ()} -- viewdisappear
*/
String VIEWAPPEAR = "viewappear";
String VIEWDISAPPEAR = "viewdisappear";
� /**
* 视频播放事件,原生是由{@link com.taobao.weex.ui.view.WXVideoView}实现
* 用{@link com.taobao.weex.ui.component.WXVideo}包裹
*/
String START = "start";
String PAUSE = "pause";
String FINISH = "finish";
String FAIL = "fail";
String ERROR = "error";
/**
* {@link com.taobao.weex.ui.view.WXWebView}使用的事件
* 用{@link com.taobao.weex.ui.component.WXWeb}组件包裹
*/
String RECEIVEDTITLE = "receivedtitle";
String PAGEFINISH = "pagefinish";
String PAGESTART = "pagestart";
String ONMESSAGE = "message";
/**
* 列表中上拉,下拉,加载更多时触发的事件
*/
String LOADMORE = "loadmore"; //加载更多操作
String ONREFRESH = "refresh"; //刷新操作
String ONLOADING = "loading"; //加载操作
String ONPULLING_DOWN = "pullingdown";//下拉操作
String ONPULLING_UP = "pullingup";//上拉操作
/**
* 图片加载成功后的回调,携带的参数
* if (imageView != null && imageView instanceof Measurable) {
* size.put("naturalWidth", ((Measurable) imageView).getNaturalWidth());
* size.put("naturalHeight", ((Measurable) imageView).getNaturalHeight());
* } else {
* size.put("naturalWidth", 0);
* size.put("naturalHeight", 0);
* }
*
* if (containsEvent(Constants.Event.ONLOAD)) {
* params.put("success", result);
* params.put("size", size);
* fireEvent(Constants.Event.ONLOAD, params);
* }
*/
String ONLOAD = "load";
/**
* 这个设计好不合理呀,在四个地方都有使用,居然不区分
* {@link com.taobao.weex.ui.component.WXInput}
* {@link com.taobao.weex.ui.component.AbstractEditComponent}
* {@link com.taobao.weex.ui.component.WXSlider}
* {@link com.taobao.weex.ui.component.WXSwitch}
* 每个事件发生都有对应不同的值
*/
String INPUT = "input";
String CHANGE = "change";
/**
* 点击返回键时的时间,前提是要在自己的页面中的返回逻辑中加上
* {@link WXSDKInstance#onBackPressed()}
*/
String CLICKBACKITEM = "clickbackitem";
/**
* Activity生命周期中执行onResume和onPause的时候的事件回调
*/
String RESUME_EVENT = "WXApplicationDidBecomeActiveEvent";
String PAUSE_EVENT = "WXApplicationWillResignActiveEvent";
/**
* {@link com.taobao.weex.ui.component.AbstractEditComponent}中输入完成后
* 执行的事件 {@link android.view.inputmethod.EditorInfo#IME_ACTION_DONE}
*/
String RETURN = "return";
/**
* 软件盘显示,隐藏的事件
*/
String KEYBOARD = "keyboard";
/**
* RecyclerView sticky item粘黏与否的事件
*/
String UNSTICKY = "unsticky";
String STICKY = "sticky";
/**
* 初始化动画或者 layoutAnimation执行完成后的事件
*/
String ON_TRANSITION_END = "transitionEnd";
interface SLOT_LIFECYCLE {
String CREATE = "create";
String ATTACH = "attach";
String DETACH = "detach";
String DESTORY = "destroy";
}
/**
* 事件冒泡
*/
String STOP_PROPAGATION = "stopPropagation";
String STOP_PROPAGATION_RAX = "stoppropagation";
/**
* 点击返回键的事件 优先级高于 {@link #CLICKBACKITEM}
*/
String NATIVE_BACK = "nativeback";