一深碱、一些感想
其實(shí)在工作的過程中嗤谚,我一直感覺自己的java基礎(chǔ)還是很薄弱的棺蛛,所以不得不重新看看java基礎(chǔ),其實(shí)注解在Android應(yīng)用實(shí)在很廣泛巩步,它讓代碼簡介旁赊,并且解耦,提高了很多開發(fā)效率椅野。為了鞏固對基礎(chǔ)知識的理解终畅,所以干脆使用注解+反射+動態(tài)代理實(shí)現(xiàn)View的點(diǎn)擊事件綁定功能,加深印象鳄橘。在整個實(shí)現(xiàn)過程中要求的知識點(diǎn)還是比較多声离,首先要熟悉Android View點(diǎn)擊事件,當(dāng)然如果對注解不了解瘫怜,那也就沒法理解注解的注解术徊,最后也就是動態(tài)代理了,不過理解了也就運(yùn)用自如了鲸湃。
二赠涮、具體實(shí)現(xiàn)
1,定義View 事件注解類型暗挑,在Android 中View的點(diǎn)擊事件分為點(diǎn)擊和長按兩種笋除,定義事件類型也是為后續(xù)工作做好鋪點(diǎn):
/**
* Description:點(diǎn)擊事件類型定義 事件分為長按和短按(即點(diǎn)擊)
* ElementType.ANNOTATION_TYPE對注解進(jìn)行注解<br>
* Author:Frank<br>
* Date:2020/5/6<br>
* Version:V1.0.0<br>
* Update: <br>
*/
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventType {
Class listenerType();
String listenerSetter();
}
2,定義點(diǎn)擊事件
/**
* Description:短按事件類型(點(diǎn)擊)<br>
* Author:Frank<br>
* Date:2020/5/6<br>
* Version:V1.0.0<br>
* Update: <br>
*/
@EventType(listenerType = View.OnClickListener.class, listenerSetter = "setOnClickListener")
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
@IdRes int[] myValue();
}
3炸裆,定義長按事件
/**
* Description:長按事件類型<br>
* Author:Frank<br>
* Date:2020/5/6<br>
* Version:V1.0.0<br>
* Update: <br>
*/
@EventType(listenerType = View.OnLongClickListener.class, listenerSetter = "setOnLongClickListener")
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnLongClick {
@IdRes int[] myValue();
}
4垃它,獲取注解,使用反射和動態(tài)代理完成事件綁定
/**
* Description:使用反射和動態(tài)代理實(shí)現(xiàn)View點(diǎn)擊事件綁定<br>
* Author:Frank<br>
* Date:2020/5/6<br>
* Version:V1.0.0<br>
* Update: <br>
*/
public class InjectViewClickUtil {
private static final String TAG = InjectViewClickUtil.class.getSimpleName();
public static void inject(Activity activity) {
if (activity == null) {
return;
}
//得到類對象
Class<? extends Activity> clz = activity.getClass();
//獲取類對象中的所有方法
Method[] methods = clz.getDeclaredMethods();
//判斷是否存在成員方法
if (methods == null) {
return;
}
//遍歷方法
for (Method m : methods) {
//獲取方法上的所有注解
Annotation[] annotations = m.getAnnotations();
//判斷是否存在注解
if (annotations == null) {
continue;
}
//遍歷所有注解
for (Annotation a : annotations) {
//得到注解類型對象
Class<? extends Annotation> annotationType = a.annotationType();
if (annotationType.isAnnotationPresent(EventType.class)) {
//得到具體注解對象
EventType eventType = annotationType.getAnnotation(EventType.class);
//取值
Class listenerType = eventType.listenerType();
Log.d(TAG, "inject: " + listenerType);
String listenerSetter = eventType.listenerSetter();
try {
//得到標(biāo)記有OnClick 和 OnLongClick 注解的方法
Method valueMethod = annotationType.getDeclaredMethod("myValue");
//得到所有需要點(diǎn)擊控件的id 也就是注解value
int[] ids = (int[]) valueMethod.invoke(a);
//設(shè)置權(quán)限
m.setAccessible(true);
//InvocationHandler接口是proxy代理實(shí)例的調(diào)用處理程序?qū)崿F(xiàn)的一個接口,
// 每一個proxy代理實(shí)例都有一個關(guān)聯(lián)的調(diào)用處理程序国拇;在代理實(shí)例調(diào)用方法時(shí)洛史,方法調(diào)用被編碼分派到調(diào)用處理程序的invoke方法
ListenerInvocationHandler invocationHandler = new ListenerInvocationHandler(m, activity);
//創(chuàng)建代理對象 最終會回調(diào)我們定義注解的方法
Object proxyInstance = Proxy.newProxyInstance(clz.getClassLoader(), new Class[]{listenerType}, invocationHandler);
for (int id : ids) {
View view = activity.findViewById(id);
Method setter = view.getClass().getMethod(listenerSetter, listenerType);
setter.invoke(view, proxyInstance);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
static class ListenerInvocationHandler<T> implements InvocationHandler {
private Method method;
private T target;
public ListenerInvocationHandler(Method method, T target) {
this.target = target;
this.method = method;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return this.method.invoke(target, args);
}
}
}
三、如何使用
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InjectViewClickUtil.inject(this);
}
@OnClick(myValue = {R.id.tv1, R.id.tv2})
public void onClick(View v) {
if (v.getId() == R.id.tv1) {
Toast.makeText(this, ((TextView) v).getText(), Toast.LENGTH_SHORT).show();
}
if (v.getId() == R.id.tv2) {
Toast.makeText(this, ((TextView) v).getText(), Toast.LENGTH_SHORT).show();
}
}
@OnLongClick(myValue = {R.id.tv1, R.id.tv2})
public boolean onLongClick(View view) {
switch (view.getId()) {
case R.id.tv1:
Log.d(TAG, "onLongClick: 長安了textView1");
break;
case R.id.tv2:
Log.d(TAG, "onLongClick: 長按了textView2");
break;
}
return true;
}
}
注意事項(xiàng):Android View長按事件是需要返回值的即代表當(dāng)前事件是否消費(fèi)酱吝, public boolean
onLongClick(View view) { return false};由于在實(shí)現(xiàn)過程中沒有注意這一問題也殖,最后運(yùn)行報(bào)錯,檢查發(fā)現(xiàn)我定義了一個void類型方法务热,導(dǎo)致執(zhí)行的時(shí)候?yàn)榭疹愋筒粚σ涫龋苯颖罎ⅰ?/p>
四,總結(jié)
java動態(tài)代理機(jī)制中有兩個重要的類和接口InvocationHandler(接口)和Proxy(類)崎岂,這一個類Proxy和接口InvocationHandler是我們實(shí)現(xiàn)動態(tài)代理的核心捆毫;如果對動態(tài)代理不是很了解可以先看看這篇文章(https://blog.csdn.net/yaomingyang/article/details/80981004),由于是在運(yùn)行是通過反射獲取屬性冲甘,所以注解選擇了運(yùn)行時(shí)冻璃,關(guān)于注解的生命周期可以根據(jù)實(shí)際業(yè)務(wù)自行選擇。