上一篇博客我們已經(jīng)帶大家簡(jiǎn)單的吹了一下IoC,實(shí)現(xiàn)了Activity中View的布局以及控件的注入,如果你不了解,請(qǐng)參考:Android 進(jìn)階 教你打造 Android 中的 IOC 框架 【ViewInject】 (上)。
本篇博客將帶大家實(shí)現(xiàn)View的事件的注入。
1、目標(biāo)效果
上篇博客,我們的事件的代碼是這么寫的:
[java] ?view plaincopy
package ?com.zhy.zhy_xutils_test;?? ?? import ?android.app.Activity;?? import ?android.os.Bundle;?? import ?android.view.View;?? import ?android.view.View.OnClickListener;?? import ?android.widget.Button;?? import ?android.widget.Toast;?? ?? import ?com.zhy.ioc.view.ViewInjectUtils;?? import ?com.zhy.ioc.view.annotation.ContentView;?? import ?com.zhy.ioc.view.annotation.ViewInject;?? ?? @ContentView (value?=?R.layout.activity_main)?? public ? class ?MainActivity? extends ?Activity? implements ?OnClickListener?? {?? ????@ViewInject (R.id.id_btn)?? ????private ?Button?mBtn1;?? ????@ViewInject (R.id.id_btn02)?? ????private ?Button?mBtn2;?? ?? ????@Override ?? ????protected ? void ?onCreate(Bundle?savedInstanceState)?? ????{?? ????????super .onCreate(savedInstanceState);?? ?????????? ????????ViewInjectUtils.inject(this );?? ?? ????????mBtn1.setOnClickListener(this );?? ????????mBtn2.setOnClickListener(this );?? ????}?? ?? ????@Override ?? ????public ? void ?onClick(View?v)?? ????{?? ????????switch ?(v.getId())?? ????????{?? ????????case ?R.id.id_btn:?? ????????????Toast.makeText(MainActivity.this ,? "Why?do?you?click?me??" ,?? ????????????????????Toast.LENGTH_SHORT).show();?? ????????????break ;?? ?? ????????case ?R.id.id_btn02:?? ????????????Toast.makeText(MainActivity.this ,? "I?am?sleeping?!!!" ,?? ????????????????????Toast.LENGTH_SHORT).show();?? ????????????break ;?? ????????}?? ????}?? ?? }??
光有View的注入能行么,我們寫View的目的,很多是用來(lái)交互的,得可以點(diǎn)擊神馬的吧。摒棄傳統(tǒng)的神馬,setOnClickListener,然后實(shí)現(xiàn)匿名類或者別的方式神馬的,我們改變?yōu)?#xff1a;
[java] ?view plaincopy
package ?com.zhy.zhy_xutils_test;?? ?? import ?android.view.View;?? import ?android.widget.Button;?? import ?android.widget.Toast;?? ?? import ?com.zhy.ioc.view.annotation.ContentView;?? import ?com.zhy.ioc.view.annotation.OnClick;?? import ?com.zhy.ioc.view.annotation.ViewInject;?? ?? @ContentView (value?=?R.layout.activity_main)?? public ? class ?MainActivity? extends ?BaseActivity?? {?? ????@ViewInject (R.id.id_btn)?? ????private ?Button?mBtn1;?? ????@ViewInject (R.id.id_btn02)?? ????private ?Button?mBtn2;?? ?? ????@OnClick ({?R.id.id_btn,?R.id.id_btn02?})?? ????public ? void ?clickBtnInvoked(View?view)?? ????{?? ????????switch ?(view.getId())?? ????????{?? ????????case ?R.id.id_btn:?? ????????????Toast.makeText(this ,? "Inject?Btn01?!" ,?Toast.LENGTH_SHORT).show();?? ????????????break ;?? ????????case ?R.id.id_btn02:?? ????????????Toast.makeText(this ,? "Inject?Btn02?!" ,?Toast.LENGTH_SHORT).show();?? ????????????break ;?? ????????}?? ????}?? ?? }??
直接通過(guò)在Activity中的任何一個(gè)方法上,添加注解,完成1個(gè)或多個(gè)控件的事件的注入。這里我把onCreate搬到了BaseActivity中,里面調(diào)用了ViewInjectUtils.inject(this);
2、實(shí)現(xiàn)
1、注解文件
[java] ?view plaincopy
package ?com.zhy.ioc.view.annotation;?? ?? import ?java.lang.annotation.ElementType;?? import ?java.lang.annotation.Retention;?? import ?java.lang.annotation.RetentionPolicy;?? import ?java.lang.annotation.Target;?? ?? @Target (ElementType.ANNOTATION_TYPE)?? @Retention (RetentionPolicy.RUNTIME)?? public ? @interface ?EventBase?? {?? ????Class<?>?listenerType();?? ?? ????String?listenerSetter();?? ?? ????String?methodName();?? }??
[java] ?view plaincopy
package ?com.zhy.ioc.view.annotation;?? ?? import ?java.lang.annotation.ElementType;?? import ?java.lang.annotation.Retention;?? import ?java.lang.annotation.RetentionPolicy;?? import ?java.lang.annotation.Target;?? ?? import ?android.view.View;?? ?? @Target (ElementType.METHOD)?? @Retention (RetentionPolicy.RUNTIME)?? @EventBase (listenerType?=?View.OnClickListener. class ,?listenerSetter?=? "setOnClickListener" ,?methodName?=? "onClick" )?? public ? @interface ?OnClick?? {?? ????int []?value();?? }??
EventBase主要用于給OnClick這類注解上添加注解,畢竟事件很多,并且設(shè)置監(jiān)聽(tīng)器的名稱,監(jiān)聽(tīng)器的類型,調(diào)用的方法名都是固定的,對(duì)應(yīng)上面代碼的:
listenerType = View.OnClickListener.class, listenerSetter = "setOnClickListener", methodName = "onClick"
Onclick是用于寫在Activity的某個(gè)方法上的:
[java] ?view plaincopy
@OnClick ({?R.id.id_btn,?R.id.id_btn02?})?? ????public ? void ?clickBtnInvoked(View?view)??
如果你還記得,上篇博客我們的ViewInjectUtils.inject(this);里面已經(jīng)有了兩個(gè)方法,本篇多了一個(gè):
[java] ?view plaincopy
public ? static ? void ?inject(Activity?activity)?? ????{?? ????????injectContentView(activity);?? ????????injectViews(activity);?? ????????injectEvents(activity);?? ????}??
2、injectEvents
[java] ?view plaincopy
? ? ? ? ?? ????private ? static ? void ?injectEvents(Activity?activity)?? ????{?? ?????????? ????????Class<??extends ?Activity>?clazz?=?activity.getClass();?? ????????Method[]?methods?=?clazz.getMethods();?? ?????????? ????????for ?(Method?method?:?methods)?? ????????{?? ????????????Annotation[]?annotations?=?method.getAnnotations();?? ?????????????? ????????????for ?(Annotation?annotation?:?annotations)?? ????????????{?? ????????????????Class<??extends ?Annotation>?annotationType?=?annotation?? ????????????????????????.annotationType();?? ?????????????????? ????????????????EventBase?eventBaseAnnotation?=?annotationType?? ????????????????????????.getAnnotation(EventBase.class );?? ?????????????????? ????????????????if ?(eventBaseAnnotation?!=? null )?? ????????????????{?? ?????????????????????? ????????????????????String?listenerSetter?=?eventBaseAnnotation?? ????????????????????????????.listenerSetter();?? ????????????????????Class<?>?listenerType?=?eventBaseAnnotation.listenerType();?? ????????????????????String?methodName?=?eventBaseAnnotation.methodName();?? ?? ????????????????????try ?? ????????????????????{?? ?????????????????????????? ????????????????????????Method?aMethod?=?annotationType?? ????????????????????????????????.getDeclaredMethod("value" );?? ?????????????????????????? ????????????????????????int []?viewIds?=?( int [])?aMethod?? ????????????????????????????????.invoke(annotation,?null );?? ?????????????????????????? ????????????????????????DynamicHandler?handler?=?new ?DynamicHandler(activity);?? ????????????????????????handler.addMethod(methodName,?method);?? ????????????????????????Object?listener?=?Proxy.newProxyInstance(?? ????????????????????????????????listenerType.getClassLoader(),?? ????????????????????????????????new ?Class<?>[]?{?listenerType?},?handler);?? ?????????????????????????? ????????????????????????for ?( int ?viewId?:?viewIds)?? ????????????????????????{?? ????????????????????????????View?view?=?activity.findViewById(viewId);?? ????????????????????????????Method?setEventListenerMethod?=?view.getClass()?? ????????????????????????????????????.getMethod(listenerSetter,?listenerType);?? ????????????????????????????setEventListenerMethod.invoke(view,?listener);?? ????????????????????????}?? ?? ????????????????????}?catch ?(Exception?e)?? ????????????????????{?? ????????????????????????e.printStackTrace();?? ????????????????????}?? ????????????????}?? ?? ????????????}?? ????????}?? ?? ????}??
嗯,注釋盡可能的詳細(xì)了,主要就是遍歷所有的方法,拿到該方法省的OnClick注解,然后再拿到該注解上的EventBase注解,得到事件監(jiān)聽(tīng)的需要調(diào)用的方法名,類型,和需要調(diào)用的方法的名稱;通過(guò)Proxy和InvocationHandler得到監(jiān)聽(tīng)器的代理對(duì)象,顯示設(shè)置了方法,最后通過(guò)反射設(shè)置監(jiān)聽(tīng)器。
這里有個(gè)難點(diǎn),就是關(guān)于DynamicHandler和Proxy的出現(xiàn),如果不理解沒(méi)事,后面會(huì)詳細(xì)講解。
3、DynamicHandler
這里用到了一個(gè)類DynamicHandler,就是InvocationHandler的實(shí)現(xiàn)類:
[java] ?view plaincopy
package ?com.zhy.ioc.view;?? ?? import ?java.lang.ref.WeakReference;?? import ?java.lang.reflect.InvocationHandler;?? import ?java.lang.reflect.Method;?? import ?java.util.HashMap;?? ?? public ? class ?DynamicHandler? implements ?InvocationHandler?? {?? ????private ?WeakReference<Object>?handlerRef;?? ????private ? final ?HashMap<String,?Method>?methodMap?=? new ?HashMap<String,?Method>(?? ????????????1 );?? ?? ????public ?DynamicHandler(Object?handler)?? ????{?? ????????this .handlerRef?=? new ?WeakReference<Object>(handler);?? ????}?? ?? ????public ? void ?addMethod(String?name,?Method?method)?? ????{?? ????????methodMap.put(name,?method);?? ????}?? ?? ????public ?Object?getHandler()?? ????{?? ????????return ?handlerRef.get();?? ????}?? ?? ????public ? void ?setHandler(Object?handler)?? ????{?? ????????this .handlerRef?=? new ?WeakReference<Object>(handler);?? ????}?? ?? ????@Override ?? ????public ?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?? ????????????throws ?Throwable?? ????{?? ????????Object?handler?=?handlerRef.get();?? ????????if ?(handler?!=? null )?? ????????{?? ????????????String?methodName?=?method.getName();?? ????????????method?=?methodMap.get(methodName);?? ????????????if ?(method?!=? null )?? ????????????{?? ????????????????return ?method.invoke(handler,?args);?? ????????????}?? ????????}?? ????????return ? null ;?? ????}?? }??
好了,代碼就這么多,這樣我們就實(shí)現(xiàn)了,我們事件的注入~~
效果圖:
效果圖其實(shí)沒(méi)撒好貼的,都一樣~~~
3、關(guān)于代理
那么,本文結(jié)束了么,沒(méi)有~~~關(guān)于以下幾行代碼,相信大家肯定有困惑,這幾行干了什么?
[java] ?view plaincopy
?? ????????????????????????DynamicHandler?handler?=?new ?DynamicHandler(activity);?? ????????????????????????handler.addMethod(methodName,?method);?? ????????????????????????Object?listener?=?Proxy.newProxyInstance(?? ????????????????????????????????listenerType.getClassLoader(),?? ????????????????????????????????new ?Class<?>[]?{?listenerType?},?handler);??
InvocationHandler和Proxy成對(duì)出現(xiàn),相信大家如果對(duì)Java比較熟悉,肯定會(huì)想到Java的動(dòng)態(tài)代理~~~
關(guān)于InvocationHandler和Proxy的文章,大家可以參考:http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/?ps:IBM的技術(shù)文章還是相當(dāng)不錯(cuò)的,畢竟有人審核還有獎(jiǎng)金~
但是我們的實(shí)現(xiàn)有一定的區(qū)別,我為什么說(shuō)大家疑惑呢,比如反射實(shí)現(xiàn):
mBtn2.setOnClickListener(this);這樣的代碼,難點(diǎn)在哪呢?
1、mBtn2的獲取?so easy?
2、調(diào)用setOnClickListener ? so easy?
but , 這個(gè) this,這個(gè)this是OnClickListener的實(shí)現(xiàn)類的實(shí)例,OnClickListener是個(gè)接口~~你的實(shí)現(xiàn)類怎么整,聽(tīng)說(shuō)過(guò)反射newInstance對(duì)象的,但是你現(xiàn)在是接口!
是吧~現(xiàn)在應(yīng)該明白上述幾行代碼做了什么了?實(shí)現(xiàn)了接口的一個(gè)代理對(duì)象,然后在代理類的invoke中,對(duì)接口的調(diào)用方法進(jìn)行處理。
4、代碼是最好的老師
光說(shuō)誰(shuí)都理解不了,你在這xx什么呢??下面看代碼,我們模擬實(shí)現(xiàn)這樣一個(gè)情景:
Main類中實(shí)現(xiàn)一個(gè)Button,Button有兩個(gè)方法,一個(gè)setOnClickListener和onClick,當(dāng)調(diào)用Button的onClick時(shí),觸發(fā)的事件是Main類中的click方法
涉及到4個(gè)類:
Button
[java] ?view plaincopy
package ?com.zhy.invocationhandler;?? ?? public ? class ?Button?? {?? ????private ?OnClickListener?listener;?? ?? ????public ? void ?setOnClickLisntener(OnClickListener?listener)?? ????{?? ?? ????????this .listener?=?listener;?? ????}?? ?? ????public ? void ?click()?? ????{?? ????????if ?(listener?!=? null )?? ????????{?? ????????????listener.onClick();?? ????????}?? ????}?? }??
OnClickListener接口
[java] ?view plaincopy
package ?com.zhy.invocationhandler;?? ?? public ? interface ?OnClickListener?? {?? ????void ?onClick();?? }??
OnClickListenerHandler , InvocationHandler的實(shí)現(xiàn)類
[java] ?view plaincopy
package ?com.zhy.invocationhandler;?? ?? import ?java.lang.reflect.InvocationHandler;?? import ?java.lang.reflect.Method;?? import ?java.util.HashMap;?? import ?java.util.Map;?? ?? public ? class ?OnClickListenerHandler? implements ?InvocationHandler?? {?? ????private ?Object?targetObject;?? ?? ????public ?OnClickListenerHandler(Object?object)?? ????{?? ????????this .targetObject?=?object;?? ????}?? ?? ????private ?Map<String,?Method>?methods?=? new ?HashMap<String,?Method>();?? ?? ????public ? void ?addMethod(String?methodName,?Method?method)?? ????{?? ????????methods.put(methodName,?method);?? ????}?? ?? ????@Override ?? ????public ?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?? ????????????throws ?Throwable?? ????{?? ?? ????????String?methodName?=?method.getName();?? ????????Method?realMethod?=?methods.get(methodName);?? ????????return ?realMethod.invoke(targetObject,?args);?? ????}?? ?? }??
我們的Main
[java] ?view plaincopy
package ?com.zhy.invocationhandler;?? ?? import ?java.lang.reflect.InvocationTargetException;?? import ?java.lang.reflect.Method;?? import ?java.lang.reflect.Proxy;?? ?? public ? class ?Main?? {?? ????private ?Button?button?=? new ?Button();?? ?????? ????public ?Main()? throws ?SecurityException,?IllegalArgumentException,?NoSuchMethodException,?IllegalAccessException,?InvocationTargetException?? ????{?? ????????init();?? ????}?? ?? ????public ? void ?click()?? ????{?? ????????System.out.println("Button?clicked!" );?? ????}?? ?? ????public ? void ?init()? throws ?SecurityException,?? ????????????NoSuchMethodException,?IllegalArgumentException,?? ????????????IllegalAccessException,?InvocationTargetException?? ????{?? ????????OnClickListenerHandler?h?=?new ?OnClickListenerHandler( this );?? ????????Method?method?=?Main.class .getMethod( "click" ,? null );?? ????????h.addMethod("onClick" ,?method);?? ????????Object?clickProxy?=?Proxy.newProxyInstance(?? ????????????????OnClickListener.class .getClassLoader(),?? ????????????????new ?Class<?>[]?{?OnClickListener. class ?},?h);?? ????????Method?clickMethod?=?button.getClass().getMethod("setOnClickLisntener" ,?? ????????????????OnClickListener.class );?? ????????clickMethod.invoke(button,?clickProxy);?? ?????????? ????}?? ?? ????public ? static ? void ?main(String[]?args)? throws ?SecurityException,?? ????????????IllegalArgumentException,?NoSuchMethodException,?? ????????????IllegalAccessException,?InvocationTargetException?? ????{?? ?? ????????Main?main?=?new ?Main();?? ?????????? ????????main.button.click();?? ????}?? ?? }??
我們模擬按鈕點(diǎn)擊:調(diào)用main.button.click(),實(shí)際執(zhí)行的卻是Main的click方法。
看init中,我們首先初始化了一個(gè)OnClickListenerHandler,把Main的當(dāng)前實(shí)例傳入,然后拿到Main的click方法,添加到OnClickListenerHandler中的Map中。
然后通過(guò)Proxy.newProxyInstance拿到OnClickListener這個(gè)接口的一個(gè)代理,這樣執(zhí)行這個(gè)接口的所有的方法,都會(huì)去調(diào)用OnClickListenerHandler的invoke方法。
但是呢?OnClickListener畢竟是個(gè)接口,也沒(méi)有方法體~~那咋辦呢?這時(shí)候就到我們OnClickListenerHandler中的Map中大展伸手了:
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName(); Method realMethod = methods.get(methodName); return realMethod.invoke(targetObject, args); }
我們顯示的把要執(zhí)行的方法,通過(guò)鍵值對(duì)存到Map里面了,等調(diào)用到invoke的時(shí)候,其實(shí)是通過(guò)傳入的方法名,得到Map中存儲(chǔ)的方法,然后調(diào)用我們預(yù)設(shè)的方法~。
這樣,大家應(yīng)該明白了,其實(shí)就是通過(guò)Proxy得到接口的一個(gè)代理,然后在InvocationHandler中使用一個(gè)Map預(yù)先設(shè)置方法,從而實(shí)現(xiàn)Button的onClick,和Main的click關(guān)聯(lián)上。
現(xiàn)在看我們InjectEvents中的代碼:
[java] ?view plaincopy
?? ????????????????????????DynamicHandler?handler?=?new ?DynamicHandler(activity);?? ?????????????????????????? ????????????????????????handler.addMethod(methodName,?method);?? ????????????????????????Object?listener?=?Proxy.newProxyInstance(?? ????????????????????????????????listenerType.getClassLoader(),?? ????????????????????????????????new ?Class<?>[]?{?listenerType?},?handler);??
是不是和我們init中的類似~~
好了,關(guān)于如何把接口的回調(diào)和我們Activity里面的方法關(guān)聯(lián)上我們也解釋完了~~~
注:部分代碼參考了xUtils這個(gè)框架,畢竟想很完善的實(shí)現(xiàn)一個(gè)完整的注入不是一兩篇博客就可以搞定,但是核心和骨架已經(jīng)實(shí)現(xiàn)了~~大家有興趣的可以繼續(xù)去完善~
源碼點(diǎn)擊下載
總結(jié)
以上是生活随笔 為你收集整理的Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (下) 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。