首先看一下用法和效果圖:
點(diǎn)擊按鈕 然后處理注入的事件
image.png
具體使用方法類似ButterKnife:
@ContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
@ViewInject(R.id.testTv)
private TextView testTv;
@ViewInject(R.id.btn)
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 注入
InjectUtils.inject(this);
testTv.setText("這是一段很有意思的文字~~~");
btn.setText("點(diǎn)擊有驚喜");
}
@OnClick({R.id.btn})
private void btnClickListener(View v){
Toast.makeText(this, "哎呀扮叨,差一點(diǎn)就拿到100w獎(jiǎng)金了台夺,再試一次吧~", Toast.LENGTH_SHORT).show();
}
}
具體實(shí)現(xiàn)
1群发、首先先定義幾個(gè)想要注入事件的注解文件
- 定義一個(gè)ContentView注解
@Target(ElementType.TYPE) // (Target表示注解用在什么地方) ElementType.TYPE 表示是用在類上的注解
@Retention(RetentionPolicy.RUNTIME) // 存在于運(yùn)行期
public @interface ContentView {
int value();
}
- 定義一個(gè)view的初始化注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
int value();
}
- 然后準(zhǔn)備定義一個(gè)onClick事件的注解辕羽,但是不能像上面那樣寫了妥色,因?yàn)榭赡苓€有別的行為事件租冠,如果按上面寫法鹏倘,那么在處理注解的時(shí)候就需要手動(dòng)判斷然后再對(duì)應(yīng)處理是點(diǎn)擊事件還是長(zhǎng)按事件等等了,耦合度較高顽爹,所以這里需要定義一個(gè)行為事件的父級(jí)注解纤泵,仔細(xì)看會(huì)發(fā)現(xiàn)一般行為事件分為三部分:
簡(jiǎn)單行為事件的結(jié)構(gòu)
所以父級(jí)注解需要有三個(gè)字段,以方便后續(xù)處理注解的事件:
@Target(ElementType.ANNOTATION_TYPE) // 用于注解上的注解
@Retention(RetentionPolicy.RUNTIME) // 存在于運(yùn)行期
public @interface EventBase {
/**
* 設(shè)置監(jiān)聽的方法
* @return
*/
String listenerSetter();
/**
* 設(shè)置監(jiān)聽的類型
* @return
*/
Class<?> listenerType();
/**
* 事件觸發(fā)之后的回調(diào)方法
* @return
*/
String callbackMethod();
}
- 然后再定義自己想要處理的行為事件镜粤,這里定義一個(gè)onClick事件
@Target(ElementType.METHOD) // 用于方法上的注解
@Retention(RetentionPolicy.RUNTIME) // 存在于運(yùn)行期
@EventBase(listenerSetter = "setOnClickListener", listenerType = View.OnClickListener.class, callbackMethod = "onClick")
public @interface OnClick {
int[] value();
}
2捏题、接下來實(shí)現(xiàn)一個(gè)InvocationHandler接口
為什么處理注入事件不直接用InvocationHandler呢,這里做一個(gè)優(yōu)化:
public class EventInvocationHandler implements InvocationHandler {
private Activity activity;
private final Map<String ,Method> methodMap;
public EventInvocationHandler(Activity activity,Map<String ,Method> methodMap){
this.activity=activity;
this.methodMap=methodMap;
}
@Override
public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
Method mtd = methodMap.get(method.getName());
if(mtd!=null){
// 如果這個(gè)方法不是被public修飾的 就強(qiáng)制改成可被使用的(忽略檢查)
if (!Modifier.isPublic(mtd.getModifiers())) {
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
mtd.setAccessible(true);
return null;
});
}
return mtd.invoke(activity,objects);
}
return method.invoke(proxy,objects);
}
}
3肉渴、最后就是核心實(shí)現(xiàn)了公荧,處理注解的類、變量同规、函數(shù)
public class InjectUtils {
public static void inject(Activity activity) {
// 注入布局
injectLayout(activity);
// 注入視圖
injectViews(activity);
// 注入事件
injectEvents(activity);
}
/**
* 注入布局
*
* @param activity
*/
private static void injectLayout(Activity activity) {
Class<? extends Activity> clazz = activity.getClass();
ContentView content = clazz.getAnnotation(ContentView.class);
activity.setContentView(content.value());
}
/**
* 注入布局
*
* @param activity
*/
private static void injectViews(Activity activity) {
Class<? extends Activity> clazz = activity.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field item : fields) {
ViewInject viewInject = item.getAnnotation(ViewInject.class);
if (viewInject == null) {
continue;
}
int id = viewInject.value();
View view = activity.findViewById(id);
item.setAccessible(true); // 設(shè)置訪問權(quán)限
try {
item.set(activity, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
private static void injectEvents(Activity activity) {
Class<? extends Activity> clazz = activity.getClass();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
// 獲取方法上的所有注解
Annotation[] annotation = method.getAnnotations();
for (Annotation ann : annotation) {
Class<? extends Annotation> annotationType = ann.annotationType();
EventBase eventBase = annotationType.getAnnotation(EventBase.class);
if (eventBase == null) {
continue;
}
// 事件要素
Class<?> listenerType = eventBase.listenerType();
String listenerSetter = eventBase.listenerSetter();
String callbackMethod = eventBase.callbackMethod();
// 方法攔截的對(duì)應(yīng)關(guān)系
Map<String,Method> methodMap = new HashMap<>();
methodMap.put(callbackMethod,method);
// 事件源
// 通過方法拿到事件源
try {
Method valueMethod = annotationType.getDeclaredMethod("value");
int[] viewIds = (int[]) valueMethod.invoke(ann);
if (viewIds == null) {
continue;
}
for (int id : viewIds) {
View view = activity.findViewById(id);
if (view == null) {
continue;
}
Method setListenerMethod = view.getClass().getMethod(listenerSetter, listenerType);
// 如何得到View.OnClickListener監(jiān)聽器的代理對(duì)象
EventInvocationHandler invocationHandler = new EventInvocationHandler(activity,methodMap);
Object proxy = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class<?>[]{listenerType}, invocationHandler);
setListenerMethod.invoke(view,proxy);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}