在DataBinding框架出来之前,Android有很多很多注解型框架,但是随着DataBinding的强势崛起,大多注解型框架都走投无路了。但是为了更好的了解技术,还是写了一个小小小的注解模型
作为一名初学者,已经厌烦了findViewById了,有没有更简单的办法去获取xml文件中的控件实例呢?使用注解就可以了。
注解:我们可以把它理解为是一种标记。通过反射机制+标记就能实现注解型框架了。
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="50dp" />
<TextView
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="50dp" />
</LinearLayout>
代码着实太简单了,就是定义了id分别为username和password的两个TextView控件。
我的目标就是:自动获取控件,并且为id为a的控件增加onClickListener监听,id为b的控件增加onLongClickListener监听。
我们先定义注解吧
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewAnnotation {
int viewId() default 0;
int layoutId() default 0;
int onClick() default 0;
int onLongClick() default 0;
}
这里定义了四个方法,其实要使用的注解。因为写了default,所以可以不同时使用者四个注解,viewId是用来获取控件的,layoutId是为activity设置布局的,onClick是为了设置点击事件监听的,onLongClick是为了设置长按事件监听的。
那我们开始在activity中使用这四个注解
@ViewAnnotation(layoutId = R.layout.activity_main)
public class MainActivity extends Activity {
@ViewAnnotation(viewId = R.id.username)
private TextView username;
@ViewAnnotation(viewId = R.id.password)
private TextView password;
@ViewAnnotation(onClick = R.id.username)
public void clickUsername(View view) {
Toast.makeText(this, "onClickListener", Toast.LENGTH_LONG).show();
}
@ViewAnnotation(onLongClick = R.id.password)
public void onLongClick(View view) {
Toast.makeText(this, "onLongClickListener", Toast.LENGTH_LONG).show();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ViewUtils.injectActivity(this);
username.setText("Aiden");
password.setText("123456");
}
}
可以看出,代码变得非常简单。在onCreate()函数中,写了ViewUtils.injectActivity(this)。这是我自定义的ViewUtils类,然后不同findViewById了,因为我们用注解获取了那个实例了。
public class ViewUtils {
// 注入activity
public static void injectActivity(Activity activity) {
Class<?> activityClass = activity.getClass();
injectContentView(activity, activityClass);
injectView(activity, activityClass);
injectListener(activity, activityClass);
}
// 设置contentView的
private static void injectContentView(Activity activity, Class<?> activityClass) {
if (activityClass.isAnnotationPresent(ViewAnnotation.class)) {
ViewAnnotation inject = activityClass.getAnnotation(ViewAnnotation.class);
int layoutId = inject.layoutId();
if (layoutId > 0) {
activity.setContentView(layoutId);
}
}
}
// 获取控件的
private static void injectView(Activity activity, Class<?> activityClass) {
Field[] fields = activityClass.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(ViewAnnotation.class)) {
field.setAccessible(true);
ViewAnnotation inject = field.getAnnotation(ViewAnnotation.class);
int id = inject.viewId();
if (id > 0) {
try {
field.set(activity, activity.findViewById(id));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
// 分别实现了点击事件和长按事件的监听
private static void injectListener(final Activity activity, Class<?> activityClass) {
Method[] methods = activityClass.getDeclaredMethods();
for (final Method method : methods) {
if (method.isAnnotationPresent(ViewAnnotation.class)) {
ViewAnnotation ViewAnnotation = method.getAnnotation(ViewAnnotation.class);
int onClick = ViewAnnotation.onClick();
if (onClick > 0) {
activity.findViewById(onClick).setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
// 回调activity中的方法
method.invoke(activity, v);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
);
}
int onLongClick = ViewAnnotation.onLongClick();
if (onLongClick > 0) {
activity.findViewById(onLongClick).setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
try {
// 回调activity中的方法
method.invoke(activity, v);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return false;
}
});
}
}
}
}
}
至此,一个简单的注解型框架就形成了。当然了还有更多更多的细节需要处理的。比如说支持Fragment、反射控件的更多方法(如setAdapter() )等等。。。
作为小白,只知道这么多了