流行的注解框架是butterknife,使用注解框架主要是為了提高編碼效率,盡可能地少寫findviewbyid這種重復代碼。注解分為編譯期注解和運行時注解,butterknife是編譯期注解,接下來自己實現的該框架是運行時注解。編譯期注解有難度,并且不會對性能有影響。 Android中的注解反射框架包含三個部分: 1、Layout布局注解;2、view注解;3、事件注解。 框架建立分析如下: 該框架的目標是自動化下面3個工作: 1、Layout的注解解決的是activity的this.setContentView(R.layout.mainLayout),所以layout的注解的值只需要傳遞一個int值,同時需要將context對象傳進去,那么其注解類設計如下:
具體的方法:
PRivate static void injectLayout(Context context) throws NoSuchMethodException, InvocationTargetException, IllegalaccessException { Class clazz = context.getClass(); Method mtd_setContentView = clazz.getMethod("setContentView", int.class); Method mtd_findViewById = clazz.getMethod("findViewById", int.class); Annotation layoutAnnotation = clazz.getAnnotation(InjectLayout.class); if (layoutAnnotation != null && layoutAnnotation instanceof InjectLayout) { int layoutId = ((InjectLayout) layoutAnnotation).value(); mtd_setContentView.invoke(context, layoutId); }}在Activity中的用法:
@InjectLayout(value=R.layout.activity_main)public class MainActivity extends BaseActivity {}2、View注解,實現的是this.findViewById(R.id.xxx),所以其注解類只需要傳遞一個整型的id值。注解類設計如下:
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface InjectView { int value();}具體的方法:
private static void injectView(Context context) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Class clazz = context.getClass(); Field[] fields = clazz.getDeclaredFields(); Method mtd_findViewById = clazz.getMethod("findViewById", int.class); for (Field field : fields) { Annotation[] annotations = field.getAnnotations(); for (Annotation annotation : annotations) { if (annotation instanceof InjectView) { int viewId = ((InjectView) annotation).value(); field.setAccessible(true); View view = (View) mtd_findViewById.invoke(context, viewId); field.set(context, view); } } }}在activity中的用法:
@InjectView(value = R.id.button) Button myBtn;3、方法的注解。其實指的是事件監聽的注解,其形式為:
view.setOnClickListener(new View.OnClickListener(){ Public void onClick(View view){ Xxxx; } });或者view.setOnLongClickListener();還有view.setOnTouchListener()等等 因為這個里面有匿名內部類,所以需要使用到動態代理。為了實現這個方法,需要知道是哪個view來綁定監聽,所以注解需要包含R.id.xxx;需要知道監聽的是view 的哪個方法,所以需要一個string來表示具體的方法名(匿名內部類listener實現的方法);在實現動態代理時,還需要知道匿名內部類的全類名,所以也需要一個Class;所以其注解類設計如下
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@EventBus(value = 1)public @interface InjectOnClick { String onClickListener(); String onClickMethod(); String bindListerMethod(); int[] bindViewId(); Class type();}注解實現方法:
private static void injectOnClick(Context context) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, InstantiationException { Class clazz = context.getClass(); Method[] methods = clazz.getDeclaredMethods(); if (null == methods || methods.length == 0) return; HashMap<String, Method> methodMap = new HashMap<>(); for (Method method : methods) { //method:onClickText Annotation[] annotations = method.getAnnotations(); if (null == annotations || annotations.length == 0) continue; for (Annotation annotation : annotations) if (annotation instanceof InjectOnClick) { String onClickName = ((InjectOnClick) annotation).onClickMethod(); methodMap.put(onClickName, method); int[] viewIds = ((InjectOnClick) annotation).bindViewId(); for (int viewId : viewIds) { //Annotation eventBus = (Annotation)annotation.annotationType(); Method mtd_findViewById = clazz.getMethod("findViewById", int.class); //獲取VIEW View view = (View) mtd_findViewById.invoke(context, viewId); Log.d(TAG, "injectOnClick: " + ""); //Class clazz_annoymous = Class.forName("android.view.View.OnClickListener"); Method setOnclickListener = view.getClass().getMethod(((InjectOnClick) annotation).bindListerMethod(), ((InjectOnClick) annotation).type()); Class clz = ((InjectOnClick) annotation).type(); //動態代理 XutilsInvocationHandler handler = new XutilsInvocationHandler(context, methodMap);//該類繼承InvocationHandler. Object proxy = (Proxy) Proxy.newProxyInstance(context.getClassLoader(), new Class[]{clz}, handler); setOnclickListener.invoke(view, proxy); } } } }在activity中的使用方法:
@InjectOnClick(onClickListener = "View.OnClickListener",bindListerMethod ="setOnClickListener", onClickMethod ="onClick",bindViewId = {R.id.button},type = View.OnClickListener.class) public void onButtonClick(View view){//此處的參數view,不能省略。 Log.d("liudong", "onClick: "); Toast.makeText(this,"BUTTON 被點擊了",Toast.LENGTH_SHORT).show(); }總體的使用方式是,新建一個BaseActivity extends Activity,在其onCreate()中,依次實現layout view onClick的注解。 public void onCreate(){ InjectUtils.injectLayout(this); InjectUtils.injectView(this); InjectUtils.injectOnClick(this); } 然后后續的Activity都繼承該BaseActivity.
新聞熱點
疑難解答