1. 目标
通过Java注解代替常规的findViewById()初始化View,和setOnClickListener()设置监听器。
最终实现效果MainActivity.java代码如下:
package com.example.myframework; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import com.myframework.activity.BaseActivity; import com.myframework.annotation.ListenerType; import com.myframework.annotation.ViewId; import com.myframework.annotation.ViewListener; public class MainActivity extends BaseActivity { @ViewId(id = R.id.tv_hello) private TextView tvHello; @ViewId(id = R.id.btn_ok) private Button btnOK; @ViewId(id = R.id.et1) private EditText et1; @ViewId(id = R.id.et2) private EditText et2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tvHello.setText("Hello World"); } @ViewListener(viewId = R.id.btn_ok, type = ListenerType.CLICK) private void btnOKOnClick(View v) { tvHello.setText("OnClick"); } @ViewListener(viewId = R.id.btn_ok, type = ListenerType.LONG_CLICK) private boolean btnOKOnLongClick(View v) { tvHello.setText("OnLongClick"); return false; } @ViewListener(viewId = R.id.et1, type = ListenerType.FOCUS_CHANGE) private void et1OnFocusChange(View v, boolean hasFocus) { if (hasFocus) { tvHello.setText("EditText1 has Focus"); et1.setText("EditText1 has Focus"); } else { et1.setText("EditText1 loses Focus"); } } @ViewListener(viewId = R.id.et2, type = ListenerType.FOCUS_CHANGE) private void et2OnFocusChange(View v, boolean hasFocus) { if (hasFocus) { tvHello.setText("EditText2 has Focus"); et2.setText("EditText2 has Focus"); } else { et2.setText("EditText2 loses Focus"); } } }
要实现上述目标,先要学会Java Annotation的使用。
2.1 注解的类型
1.基本Annotation
@Override 限定重写父类方法
@Deprecated 标示已过时
@Suppress Warnings 抑制编译器警告
@SafeVarargs
2.JDK的元Annotation
@Retention
用于修饰Annotation,指定被修饰的Annotation可以保留多长时间。@Retention包含一个RetentionPolicy类型的value成员变量,使用@Retention时必须为该value赋值。
value的值有如下3种:
RetentionPolicy.CLASS 编译器将把Annotation记录在class文件中。当运行Java程序时,JVM不再保留Annotation。默认值。
RetentionPolicy.RUNTIME 编译器将把Annotation记录在class文件中。当运行Java程序时,JVM保留Annotation,程序可以通过反射获取该Annotation的信息。
RetentionPolicy.SOURCE Annotation只保留在源代码中。
@Target
指定修饰类型,如ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE。
@Documented
指定被该元Annotation修饰的Annotation类将被javadoc工具提取成文档。
@Inherited
指定被它修饰的Annotation将具有继承性。如果某个类使用了@A修饰,则其子类将自动被@A修饰。
3.自定义Annotation
以本文实例代码中使用的自定义注解@ViewId、@ViewListener为例。
@ViewId注解类成员变量,所以@Target为FIELD。该注解的成员变量id为被注解View的id。使用@ViewId的程序要在运行时获取id信息,所以@Retention的value=RetentionPolicy.RUNTIME。
ViewId.java代码如下:
package com.myframework.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(value=ElementType.FIELD) public @interface ViewId { int id(); }@ViewListener注解类方法,用于指定事件监听的响应方法。其有两个成员变量viewId和type,分别表示某个view的特定类型监听器。
ViewListener.java代码如下:
package com.myframework.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface ViewListener { int viewId(); ListenerType type(); }ListenerType为enum类型。
ListenerType.java如下:
package com.myframework.annotation; public enum ListenerType { CLICK, LONG_CLICK, FOCUS_CHANGE }
3.1 实现一个继承于Activity的BaseActivity
若某个Activity需要View的依赖注入,则需继承BaseActivity。BaseActivity重载setContentView(int layoutResID)方法, 并添加initView()方法和setListener()方法。
public class BaseActivity extends Activity { public static final String TAG = "BaseActivity"; ... @Override public void setContentView(int layoutResID) { super.setContentView(layoutResID); initView(); setListener(); } private void initView() { ... } private void setListener() { ... } ... }
public class BaseActivity extends Activity { ... private void initView() { Class<?> cls = this.getClass(); // 获取类的所有Fields Field[] fields = cls.getDeclaredFields(); for (Field f : fields) { // 遍历所有的Fields,处理被@ViewId修饰的Field if (f.isAnnotationPresent(ViewId.class)) { f.setAccessible(true); ViewId viewId = f.getAnnotation(ViewId.class); // 得到@ViewId的成员变量id值 int id = viewId.id(); try { // 相当于View v = findViewById(id); f.set(this, findViewById(id)); } catch (Exception e) { e.printStackTrace(); } } } } }
private void setListener() { Class<?> cls = this.getClass(); // 获取类所有的Methods Method[] methods = cls.getDeclaredMethods(); for (Method m : methods) { // 遍历所有的Methods,处理被@ViewListener修饰的Method if (m.isAnnotationPresent(ViewListener.class)) { ViewListener viewListener = m.getAnnotation(ViewListener.class); // 获取@ViewListener的成员变量id值,并获取相关View View v = findViewById(viewListener.viewId()); // 获取@ViewListener的成员变量type值,并根据监听器类型为该View设置监听器。 ListenerType type = viewListener.type(); switch (type) { case CLICK: setOnClickListener(v, m); break; case LONG_CLICK: setOnLongClickListener(v, m); break; case FOCUS_CHANGE: setOnFocusChangeListener(v, m); break; } } } }
3.4 设置监听器方法的实现。
以setOnClickListener(View v, Method m)方法的实现为例。实现原理,将所有的CLICK方法添加进mOnClickMethodArray,该值为SparseArray类型,key为View的id,value为onClick事件处理方法。当执行OnClickListener的onClick(View v)方法时,根据View的id获取相应的执行方法。相关代码如下:
public class BaseActivity extends Activity { public static final String TAG = "BaseActivity"; private OnClickListener mOnClickListener; // OnClick事件处理方法数组 private SparseArray<Method> mOnClickMethodArray; private void setOnClickListener(View v, Method m) { if (mOnClickListener == null) { // 初始化mOnClickListener mOnClickListener = new OnClickListener() { @Override public void onClick(View v) { onClickImp(v); } }; // 初始化OnClick事件处理方法数组 mOnClickMethodArray = new SparseArray<Method>(); } // 将OnClick事件处理方法m添加进数组,key为View的id。 mOnClickMethodArray.append(v.getId(), m); v.setOnClickListener(mOnClickListener); } private void onClickImp(View v) { // 根据id从OnClick事件处理方法数组mOnClickMethodArray中取出要执行的方法 Method m = mOnClickMethodArray.get(v.getId()); // 将该方法设为可访问 m.setAccessible(true); try { // 调用该方法 m.invoke(this, v); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }
4. 总结
BaseActivity.java的完整代码如下:package com.myframework.activity; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import android.app.Activity; import android.util.SparseArray; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; import android.view.View.OnLongClickListener; import com.myframework.annotation.ListenerType; import com.myframework.annotation.ViewId; import com.myframework.annotation.ViewListener; public class BaseActivity extends Activity { public static final String TAG = "BaseActivity"; private OnClickListener mOnClickListener; private OnLongClickListener mOnLongClickListener; private OnFocusChangeListener mOnFocusChangeListener; // OnClick事件处理方法数组 private SparseArray<Method> mOnClickMethodArray; // OnLongClick事件处理方法数组 private SparseArray<Method> mOnLongClickMethodArray; // OnFocusChange事件处理方法数组 private SparseArray<Method> mOnFocusChangeMethodArray; @Override public void setContentView(int layoutResID) { super.setContentView(layoutResID); initView(); setListener(); } private void initView() { Class<?> cls = this.getClass(); // 获取类的所有Fields Field[] fields = cls.getDeclaredFields(); for (Field f : fields) { // 遍历所有的Fields,处理被@ViewId修饰的Field if (f.isAnnotationPresent(ViewId.class)) { f.setAccessible(true); ViewId viewId = f.getAnnotation(ViewId.class); // 得到@ViewId的成员变量id值 int id = viewId.id(); try { // 相当于View v = findViewById(id); f.set(this, findViewById(id)); } catch (Exception e) { e.printStackTrace(); } } } } private void setListener() { Class<?> cls = this.getClass(); // 获取类所有的Methods Method[] methods = cls.getDeclaredMethods(); for (Method m : methods) { // 遍历所有的Methods,处理被@ViewListener修饰的Method if (m.isAnnotationPresent(ViewListener.class)) { ViewListener viewListener = m.getAnnotation(ViewListener.class); // 获取@ViewListener的成员变量id值,并获取相关View View v = findViewById(viewListener.viewId()); // 获取@ViewListener的成员变量type值,并根据监听器类型为该View设置监听器。 ListenerType type = viewListener.type(); switch (type) { case CLICK: setOnClickListener(v, m); break; case LONG_CLICK: setOnLongClickListener(v, m); break; case FOCUS_CHANGE: setOnFocusChangeListener(v, m); break; } } } } private void setOnClickListener(View v, Method m) { if (mOnClickListener == null) { // 初始化mOnClickListener mOnClickListener = new OnClickListener() { @Override public void onClick(View v) { onClickImp(v); } }; // 初始化OnClick事件处理方法数组 mOnClickMethodArray = new SparseArray<Method>(); } // 将OnClick事件处理方法m添加进数组,key为View的id。 mOnClickMethodArray.append(v.getId(), m); v.setOnClickListener(mOnClickListener); } private void onClickImp(View v) { // 根据id从OnClick事件处理方法数组mOnClickMethodArray中取出要执行的方法 Method m = mOnClickMethodArray.get(v.getId()); // 将该方法设为可访问 m.setAccessible(true); try { // 调用该方法 m.invoke(this, v); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } private void setOnLongClickListener(View v, Method m) { if (mOnLongClickListener == null) { mOnLongClickListener = new OnLongClickListener() { @Override public boolean onLongClick(View v) { return onLongClickImp(v); } }; mOnLongClickMethodArray = new SparseArray<Method>(); } mOnLongClickMethodArray.append(v.getId(), m); v.setOnLongClickListener(mOnLongClickListener); } private boolean onLongClickImp(View v) { Method m = mOnLongClickMethodArray.get(v.getId()); m.setAccessible(true); try { return (boolean) m.invoke(this, v); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return false; } private void setOnFocusChangeListener(View v, Method m) { if (mOnFocusChangeListener == null) { mOnFocusChangeListener = new OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { onFocusChangeImp(v, hasFocus); } }; mOnFocusChangeMethodArray = new SparseArray<Method>(); } mOnFocusChangeMethodArray.append(v.getId(), m); v.setOnFocusChangeListener(mOnFocusChangeListener); } private void onFocusChangeImp(View v, boolean hasFocus) { Method m = mOnFocusChangeMethodArray.get(v.getId()); m.setAccessible(true); try { m.invoke(this, v, hasFocus); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }