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 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();
}
// 将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 mOnClickMethodArray;
// OnLongClick事件处理方法数组
private SparseArray mOnLongClickMethodArray;
// OnFocusChange事件处理方法数组
private SparseArray 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();
}
// 将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();
}
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();
}
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();
}
}
}