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基础