目录
- 自定义注解
- 使用自定义注解
- 通过反射机制获取注解参数
- 1. 布局文件获取
- 2. 控件获取实现
- 3. 控件点击响应
自定义注解
1) 先定义布局文件注入
| |
| @Target(ElementType.TYPE) |
| |
| @Retention(RetentionPolicy.RUNTIME) |
| public @interface ContentView { |
| int value(); |
| } |
2) 布局中控件注入文件
| @Target(ElementType.FIELD) |
| @Retention(RetentionPolicy.RUNTIME) |
| public @interface ViewInject { |
| int value(); |
| } |
3) 控件的点击响应注入文件
| @Target(ElementType.METHOD) |
| @Retention(RetentionPolicy.RUNTIME) |
| @interface OnClick { |
| int[] value(); |
| } |
使用自定义注解
| @ContentView(R.layout.activity_test) |
| public class TestActivity extends AppCompatActivity implements View.OnClickListener{ |
| @ViewInject(R.id.edit_text) |
| EditText mEditText; |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| injectContentView(this); |
| injectView(this); |
| injectEvent(this); |
| } |
| @OnClick(R.id.button) |
| @Override |
| public void onClick(View v) { |
| Toast.makeText(TestActivity.this,"点击成功"+mEditText.getText().toString(),Toast.LENGTH_SHORT).show(); |
| } |
| } |
通过反射机制获取注解参数
Method相关方法的介绍:
method.invoke(Object obj,Object args[])的作用就是调用method类代表的方法,其中obj是对象名,args是传入method方法的参数
- getMethods(): 获得类的public类型的方法
- getMethod(String name, Class[] params): 获得类的特定方法,name参数指定方法的名字,params参数指定方法的参数类型
- getDeclaredMethods(): 获取类中所有的方法(public、protected、default、private)
- getDeclaredMethod(String name, Class[] params): 获得类的特定方法,name参数指定方法的名字,params参数指定方法的参数类型
1. 布局文件获取
| public static void injectContentView(Activity activity){ |
| |
| Class<? extends Activity> clazz = activity.getClass(); |
| |
| ContentView contentView = clazz.getAnnotation(ContentView.class); |
| if(contentView!=null){ |
| |
| int layoutId=contentView.value(); |
| try { |
| |
| Method setViewMethod = clazz.getMethod("setContentView", int.class); |
| setViewMethod.invoke(activity,layoutId); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
2. 控件获取实现
| private static void injectView(Activity activity){ |
| |
| Class<? extends Activity> clazz = activity.getClass(); |
| |
| Field[] fields = clazz.getDeclaredFields(); |
| |
| for(Field field:fields){ |
| |
| ViewInject viewInject = field.getAnnotation(ViewInject.class); |
| if(viewInject!=null){ |
| |
| int viewId = viewInject.value(); |
| |
| View view = activity.findViewById(viewId); |
| try { |
| |
| field.setAccessible(true); |
| |
| field.set(activity,view); |
| }catch (IllegalAccessException e){ |
| e.printStackTrace(); |
| } |
| } |
| } |
| } |
3. 控件点击响应
| private static void injectEvent(final Activity activity) { |
| Class<? extends Activity> clazz = activity.getClass(); |
| |
| Method[] methods = clazz.getDeclaredMethods(); |
| for (Method method : methods) { |
| |
| OnClick click = method.getAnnotation(OnClick.class); |
| |
| if (click != null) { |
| |
| int[] viewId = click.value(); |
| |
| method.setAccessible(true); |
| |
| |
| Object listener = Proxy.newProxyInstance(View.OnClickListener.class.getClassLoader(), |
| new Class[]{View.OnClickListener.class}, new InvocationHandler() { |
| |
| @Override |
| public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { |
| Log.d(TAG, "method"+method); |
| Log.d(TAG, "args "+args); |
| |
| |
| Object invoke = method.invoke(activity, args); |
| return invoke; |
| } |
| }); |
| |
| |
| try { |
| for (int id : viewId) { |
| |
| View v = activity.findViewById(id); |
| |
| Method setClickListener = v.getClass().getMethod("setOnClickListener", View.OnClickListener.class); |
| Log.d(TAG, "injectEvent: "+listener+"v.getClass()"+v); |
| |
| |
| |
| setClickListener.invoke(v, listener); |
| } |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| } |
上面的实现都是在运行时通过反射完成注入,但它会对性能有一定损耗,而Butterknife用的是APT(Annotation Processing Tool)编译时解析技术。它在Java代码编译成Java字节码时就已经处理了@Bind, @OnClick这些注解。它的原理是读入Java源代码,解析注解,生成新的Java代码,新生成的Java代码最后被编译成Java字节码。