Android 中注解view (仿butterknife)
2016-06-09 12:40
441 查看
Android中使用注解来给view绑定事件
自定义注解实现View注入,就不需要再写Button button = (Button) findViewById(R.id.test_btn); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { } });
元注解
@Target@Target表示Annotation可用在什么地方。其中的ElementType取值如下:
TYPE:类,接口或者是enum声明 FIELD:域声明(包括enum实例) METHOD:方法声明 PARAMETER:参数声明 CONSTRUCTOR:构造器声明 LOCAL_VARIABLE:局部变量声明 ANNOTATION_TYPE:注解类型声明 PACKAGE:包声明
@Retention
@Retention表示在什么级别保存该注解信息。其中RetentionPolicy取值如下:
SOURCE:只在源码中保留,该注解将会被编译器丢掉 CLASS:注解在class文件中可用,但是会被VM丢弃 RUNTIME:VM会在运行时保留注解,这时可以通过反射读取注解信息。
@Documented
@Documented表示在Javadocs中包含这个注解。
@Inherited
@Inherited表示允许子类继承父类中的注解。
自定义注解
ContentView,注解Activity中的layout
package com.ljd.msh.inject; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author sv-004 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface ContentView { int value(); }
Inject: 注解实现Activity中View组件的注入.
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author sv-004 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Inject { int value(); }
OnClick:注解实现View的事件注入.
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author sv-004 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface OnClick { int[] value(); }
注解处理器
import android.app.Activity; import android.view.View; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * @author sv-004 */ public class MSHInject { private static Class<?> clazz; public static void inject(Activity activity){ //获取activity的Class类 clazz = activity.getClass(); injectContent(activity); injectView(activity); injectEvent(activity); } public static void unInject(){ clazz = null; } /** * 对ContentView注解惊醒解析 * @param activity */ private static void injectContent(Activity activity){ //取的Activity中的ContentView注解 ContentView contentView = clazz.getAnnotation(ContentView.class); if (contentView != null){ //取出ContentView注解中的值 int id = contentView.value(); try { //获取Activity中setContentView方法,执行setContentView方法为Activity设置ContentView //在这一步中我们也可以直接使用 activity.setContentView(id) 来设置ContentView clazz.getMethod("setContentView",Integer.TYPE).invoke(activity,id); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } /** * 对InjectView注解进行解析 * @param activity */ private static void injectView(Activity activity){ Field[] fields = clazz.getDeclaredFields(); for (Field field : fields){ Inject inject = field.getAnnotation(Inject.class); if (inject != null){ int id = inject.value(); try { //这一步中同样也能够使用 Object view = activity.findViewById(id) 来获取View Object view = clazz.getMethod("findViewById",Integer.TYPE).invoke(activity,id); field.set(activity,view); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } } /** * 对OnClick注解进行解析 * @param activity */ private static void injectEvent(Activity activity){ Method[] methods = clazz.getMethods(); for (Method method : methods) { OnClick onClick = method.getAnnotation(OnClick.class); if (onClick != null){ int[] ids = onClick.value(); MyInvocationHandler handler = new MyInvocationHandler(activity,method); //通过Java中的动态代理来执行View.OnClickListener Object listenerProxy = Proxy.newProxyInstance( View.OnClickListener.class.getClassLoader(), new Class<?>[] { View.OnClickListener.class }, handler); for (int id : ids) { try { Object view = clazz.getMethod("findViewById",Integer.TYPE).invoke(activity,id); Method listenerMethod = view.getClass() .getMethod("setOnClickListener", View.OnClickListener.class); listenerMethod.invoke(view, listenerProxy); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } } static class MyInvocationHandler implements InvocationHandler { private Object target = null; private Method method = null; public MyInvocationHandler(Object target,Method method) { super(); this.target = target; this.method = method; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return this.method.invoke(target,args); } } }
MainActivity
package com.ljd.msh; /** * @author sv-004 * */ import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.ljd.annotation.R; import com.ljd.msh.inject.ContentView; import com.ljd.msh.inject.Inject; import com.ljd.msh.inject.OnClick; import com.ljd.msh.inject.MSHInject; @ContentView(R.layout.activity_main) public class MainActivity extends AppCompatActivity { @Inject(R.id.test_text) TextView textView; @Inject(R.id.test_btn) Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MSHInject.inject(this); textView.setText("hello word"); button.setText("test"); } @OnClick({R.id.test_btn,R.id.test_text}) public void onClick(View view) { switch (view.getId()){ case R.id.test_btn: Toast.makeText(this,"test onClick",Toast.LENGTH_SHORT).show(); break; case R.id.test_text: Toast.makeText(this,"hello word",Toast.LENGTH_SHORT).show(); break; } } @Override protected void onDestroy() { super.onDestroy(); MSHInject.unInject(); } }
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="@dimen/activity_vertical_margin" tools:context="com.ljd.msh.MainActivity"> <Button android:id="@+id/test_btn" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/test_text" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>
参考资料
Java注解Annotation基础
InvocationHandler中invoke()方法的调用问题
butterknife
相关文章推荐
- Android 利用Sharp样式设置文本框EditText圆角形状
- android中双击退出程序的帮助类
- android开源图表库MPAndroidChart文档翻译(上)
- Android 退出程序 关闭所有Activity 的方法
- Android Jni反射Java方法,弹出对话框
- Java Android开发小知识
- Android:Dagger2学习之由浅入深
- Android反编译调试源码
- Android ipc通信机制
- Android VelocityTracker 速度跟踪器
- Android studio 中SlidingMenu的使用
- Android系统横竖屏切换时候Activity的生命周期
- 【Android框架】Android-universal-image-loader源码阅读(一)
- Android Studio 开发常用快捷键
- android框架---->下沉文字Titanic的使用
- Android之GLES2.0显示图片测试代码
- Android之Log封装
- Android实现监听事件的三种方式
- OpenglES2.0 for Android:各种变换来一波
- systrace跟踪 Android性能优化