当在项目中一个页面有着太多的控件的时候,你是否被findViewById快折磨疯了,在listView列表类的findView折磨到不想写的冲动,那么在Java中利用反射和注解原理来就可以解决,当然这样在性能上多少有点欠缺,要丢失的是反应时间上的性能,毕竟要去扫描类中符合要求的字段和方法再去执行你自己定义的注解方法和字段赋值。什么事情都是一把双刃剑,你丢失了几毫秒的性能在代码上得到了开发的速度和代码的优美简洁,我自己看来在于自己怎么去选择。
阐述下用到的知识点:(1)要findViewById自己执行,就需要利用反射原理去执行类中的方法“findViewById(int id)”,那么id的值就靠注解字段来获取。
// 执行该方法,返回一个Object类型的View实例
Object resView = method.invoke(object, id);
field.setAccessible(true);
// 把字段的值设置为该View的实例
field.set(object, resView);
(2)对View设置onClick事件也是类似的,只是我们需要的是对方法进行注解,然后对找到的控件进行一一设置点击事件即可。
Method findViewMethod = (view == null ? objectClass : view.getClass()).getMethod("findViewById", int.class);
final View resView = (View) findViewMethod.invoke(object, id);
if (resView == null) {
Log.e(TAG, "@OnClick annotation value view id (index = "+index+") isn't find any view in "+object.getClass().getSimpleName());
return;
}
resView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
method.invoke(object, resView);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
});
1.定义控件对象注解字段类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject{
int value() default -1;
}
2.定义OnClick事件的方法注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
int [] value();
}
3.定义注解工具类
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import android.util.Log;
import android.view.View;
/**
* 注解工具类
* Annotation Utils
*/
public class ViewUtils {
private static final String TAG = "RRL";
/**
* find view in activity
*
* @param object
*/
public static void inject(Object object) {
long time = System.currentTimeMillis();
reflectFindView(object, null);
injectMethod(object, null);
Log.e(TAG, ViewUtils.class.getSimpleName() + " inject use time:" + (System.currentTimeMillis() - time) + "ms");
}
/**
* find view in fragment or ViewHolder
*
* @param object Fragment or ViewHolder
* @param view there is findViewById method view
*/
public static void inject(Object object, View view) {
long time = System.currentTimeMillis();
reflectFindView(object, view);
injectMethod(object, view);
Log.e(TAG, ViewUtils.class.getSimpleName() + " inject use time:" + (System.currentTimeMillis() - time) + "ms");
}
/**
* 反射找到View Id
* Find view id by reflect
*
* @param object Activity Fragment or ViewHolder
* @param view there is findViewById method view
*/
private static void reflectFindView(Object object, View view) {
//Access to all of the fields
Class<?> fieldClass = object.getClass();
Field[] fields = fieldClass.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
//if every field have you defined annotation class(ViewInject.class) and get view id.
Field field = fields[i];
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if (viewInject != null) {//if you defined annotation class's values is not null
reflectFindView(object, view, field, viewInject.value());
}
}
}
/**
* 反射找控件
* find view by reflect class
*
* @param field annotation field
* @param view findViewById class
* @param id view's Id
*/
private static void reflectFindView(Object object, View view, Field field, int id) {
if (id == -1) {
return;
}
Class<?> fieldCls = object.getClass();
Class<?> findViewClass = (view == null ? object : view).getClass();
try {
// 获取类中的findViewById方法,参数为int
Method method = (findViewClass == null ? fieldCls : findViewClass).getMethod("findViewById", int.class);
// 执行该方法,返回一个Object类型的View实例
Object resView = method.invoke(view == null ? object : view, id);
field.setAccessible(true);
// 把字段的值设置为该View的实例
field.set(object, resView);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/**
* Annotation method
*
* @param object annotation class with declared methods
* @param view annotation view in Fragment or ViewHolder
*/
private static void injectMethod(Object object, View view) {
long time = System.currentTimeMillis();
reflectMethod(object, view);
Log.e(TAG, ViewUtils.class.getSimpleName() + " inject use time:" + (System.currentTimeMillis() - time) + "ms");
}
/**
* reflect method
*
* @param object annotation class with declared methods
* @param view annotation view in Fragment or ViewHolder
*/
private static void reflectMethod(Object object, View view) {
Class<?> objectClass = object.getClass();
Method[] objectMethods = objectClass.getDeclaredMethods();
for (int i = 0; i < objectMethods.length; i++) {
Method method = objectMethods[i];
method.setAccessible(true);//if method is private
//get annotation method
OnClick onClick = method.getAnnotation(OnClick.class);
if (onClick != null) {
int[] values = onClick.value();
for (int index = 0; index < values.length; index++) {
int id = values[index];
reflectMethod(object, view, id,index, method);
}
}
}
}
/**
* reflect method
*
* @param object annotation class with declared methods
* @param view annotation view in Fragment or ViewHolder
* @param id annotation view id
* @param method annotation method
*/
private static void reflectMethod(final Object object, View view, int id,int index, final Method method) {
Class<?> objectClass = object.getClass();
try {
Method findViewMethod = (view == null ? objectClass : view.getClass()).getMethod("findViewById", int.class);
final View resView = (View) findViewMethod.invoke(object, id);
if (resView == null) {
Log.e(TAG, "@OnClick annotation value view id (index = "+index+") isn't find any view in " + object.getClass().getSimpleName());
return;
}
resView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
method.invoke(object, resView);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
});
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
4.使用方法(1)在Activity中使用:
在定义的字段时候如下:
@ViewInject(R.id.tv_content)
private TextView tv_content;
//在onCreate()中setContentView()方法之后调用ViewUtils.inject(this);
(2)在Fragment中使用:
@ViewInject(R.id.tv_content)
private TextView tv_content;
@Override
public View onCreateView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fgt_main, null);
ViewUtils.inject(this, view);
return view;
}
(3)在ViewHolder中使用:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(MainFgt.this.getContext()).inflate(R.layout.list_item_content, null);
ViewUtils.inject(holder, convertView);
convertView.setTag(holder);
} else
holder = (ViewHolder) convertView.getTag();
holder.tv_list_content.setText(position + "测试文字");
return convertView;
}
private class ViewHolder {
@ViewInject(R.id.tv_list_content)
private TextView tv_list_content;
}