Android用注解與反射實(shí)現(xiàn)Butterknife功能
自定義注解
1) 先定義布局文件注入
//注解的作用域在類(lèi)上
@Target(ElementType.TYPE)
//讓保持性策略為運(yùn)行時(shí)態(tài),將注解編碼到class文件中,讓虛擬機(jī)讀取
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
int value();//使用時(shí)直接@ContentView(R.layout.xxx)指定的R.layout.xxx就是布局文件,會(huì)自動(dòng)注入布局文件
}2) 布局中控件注入文件
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
int value();
}3) 控件的點(diǎn)擊響應(yīng)注入文件
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface OnClick {
int[] value();
}
使用自定義注解
@ContentView(R.layout.activity_test)
public class TestActivity extends AppCompatActivity implements View.OnClickListener{
@ViewInject(R.id.edit_text)
EditText mEditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
injectContentView(this);
injectView(this);
injectEvent(this);
}
@OnClick(R.id.button)
@Override
public void onClick(View v) {
Toast.makeText(TestActivity.this,"點(diǎn)擊成功"+mEditText.getText().toString(),Toast.LENGTH_SHORT).show();
}
}通過(guò)反射機(jī)制獲取注解參數(shù)
Method相關(guān)方法的介紹:
method.invoke(Object obj,Object args[])的作用就是調(diào)用method類(lèi)代表的方法,其中obj是對(duì)象名,args是傳入method方法的參數(shù)
- getMethods(): 獲得類(lèi)的public類(lèi)型的方法
- getMethod(String name, Class[] params): 獲得類(lèi)的特定方法,name參數(shù)指定方法的名字,params參數(shù)指定方法的參數(shù)類(lèi)型
- getDeclaredMethods(): 獲取類(lèi)中所有的方法(public、protected、default、private)
- getDeclaredMethod(String name, Class[] params): 獲得類(lèi)的特定方法,name參數(shù)指定方法的名字,params參數(shù)指定方法的參數(shù)類(lèi)型
1. 布局文件獲取
public static void injectContentView(Activity activity){
//獲取activity的類(lèi)實(shí)例
Class<? extends Activity> clazz = activity.getClass();
//獲取到activity的ContentView注解
ContentView contentView = clazz.getAnnotation(ContentView.class);
if(contentView!=null){
//如果activity上面存在這個(gè)注解的話,就取出這個(gè)注解對(duì)應(yīng)的value值,就是前面設(shè)置的布局
int layoutId=contentView.value();
try {
//利用反射調(diào)用setContentView方法,完成注入
Method setViewMethod = clazz.getMethod("setContentView", int.class);
setViewMethod.invoke(activity,layoutId);
} catch (Exception e) {
e.printStackTrace();
}
}
}2. 控件獲取實(shí)現(xiàn)
private static void injectView(Activity activity){
//獲取activity的類(lèi)實(shí)例
Class<? extends Activity> clazz = activity.getClass();
//獲取activity的所有成員變量
Field[] fields = clazz.getDeclaredFields();
//遍歷成員變量,獲取成員變量上的ViewInject注解
for(Field field:fields){
//獲取字段上面的注解對(duì)象,同有則繼續(xù)下一個(gè)字段
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if(viewInject!=null){
//獲取ViewInject注解的View的id
int viewId = viewInject.value();
//獲取控件
View view = activity.findViewById(viewId);
try {
//設(shè)置field為可訪問(wèn),就算私有的也能訪問(wèn)到,能夠提高效率
field.setAccessible(true);
//將該控件設(shè)置給field對(duì)象
field.set(activity,view);//將activity對(duì)象的view控件設(shè)置給屬性對(duì)應(yīng)上面的例子是mEditText=findViewById(viewId)
}catch (IllegalAccessException e){
e.printStackTrace();
}
}
}
}3. 控件點(diǎn)擊響應(yīng)
private static void injectEvent(final Activity activity) {
Class<? extends Activity> clazz = activity.getClass();
//獲取所有方法(私有方法也可以獲取到)
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
//獲取方法上面的OnClick注解
OnClick click = method.getAnnotation(OnClick.class);
//有則繼續(xù)下面代碼
if (click != null) {
//獲取注解中的數(shù)據(jù),因可以給多個(gè)button綁定點(diǎn)擊事件,因此定義的注解類(lèi)時(shí)使用的是int[]數(shù)據(jù)類(lèi)型
int[] viewId = click.value();
//在需要發(fā)射時(shí)將method的setAccessible設(shè)置為true,可以提高反射速度,原因是設(shè)置為true后則跳過(guò)了訪問(wèn)檢查,即使private修飾的也可以訪問(wèn)。
method.setAccessible(true);
//設(shè)置一個(gè)代理對(duì)象,當(dāng)調(diào)用setOnClickListener時(shí),把代理對(duì)象傳進(jìn)去,當(dāng)點(diǎn)擊發(fā)生時(shí),就會(huì)invoke方法,可以調(diào)用帶有onClick注解的method方法
//此時(shí)的listener就是actvivty實(shí)現(xiàn)View.OnClickListener接口,并實(shí)現(xiàn)其接口中onClick方法的對(duì)象,其實(shí)也就是返回的invoke對(duì)象。
Object listener = Proxy.newProxyInstance(View.OnClickListener.class.getClassLoader(),
new Class[]{View.OnClickListener.class}, new InvocationHandler() {
//這個(gè)invoke,其實(shí)就是調(diào)用的意思,第一個(gè)參數(shù)就是被代理的對(duì)象,第二個(gè)參數(shù)就是調(diào)用的方法,第三個(gè)參數(shù)是被調(diào)用方法的參數(shù)數(shù)組。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.d(TAG, "method"+method);
Log.d(TAG, "args "+args);
//執(zhí)行activity對(duì)象,參數(shù)args的方法,activity對(duì)象就是當(dāng)前的Activity,并且已經(jīng)實(shí)現(xiàn)View.OnClickListener
//也就是說(shuō)如果點(diǎn)擊了控件將會(huì)執(zhí)行實(shí)現(xiàn)了View.OnClickListener的Activity的onClick方法
Object invoke = method.invoke(activity, args);
return invoke;
}
});
//listener是一個(gè)OnClickListener類(lèi)的對(duì)象,因?yàn)閙ethod傳的是activity,所以是當(dāng)前類(lèi)的OnClickListener對(duì)象
//method是onClick方法
try {
for (int id : viewId) {
//獲取相應(yīng)的控件
View v = activity.findViewById(id);
//"setOnClickListener"是方法名,View.OnClickListener.class方法參數(shù)的類(lèi)型
Method setClickListener = v.getClass().getMethod("setOnClickListener", View.OnClickListener.class);
Log.d(TAG, "injectEvent: "+listener+"v.getClass()"+v);
//View類(lèi)中的setOnClickListener方法需要一個(gè)OnClickListener類(lèi)型的參數(shù)也就是我們的代理對(duì)象
//我們需要將對(duì)應(yīng)的控件進(jìn)行設(shè)置
//這時(shí)當(dāng)我們點(diǎn)擊v(也就是我們找到的控件)時(shí)就會(huì)調(diào)用代理對(duì)象的onClick方法。而代理對(duì)象的onClick方法就是Activity中實(shí)現(xiàn)接口的onClick方法。相當(dāng)于執(zhí)行方法setOnClickListener(listener)。而這個(gè)listener就是我們上面代理(當(dāng)前activity實(shí)現(xiàn)OnClicker接口的對(duì)象)對(duì)象。
setClickListener.invoke(v, listener);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}上面的實(shí)現(xiàn)都是在運(yùn)行時(shí)通過(guò)反射完成注入,但它會(huì)對(duì)性能有一定損耗,而B(niǎo)utterknife用的是APT(Annotation Processing Tool)編譯時(shí)解析技術(shù)。它在Java代碼編譯成Java字節(jié)碼時(shí)就已經(jīng)處理了@Bind, @OnClick這些注解。它的原理是讀入Java源代碼,解析注解,生成新的Java代碼,新生成的Java代碼最后被編譯成Java字節(jié)碼。
到此這篇關(guān)于Android用注解與反射實(shí)現(xiàn)Butterknife功能的文章就介紹到這了,更多相關(guān)Android Butterknife內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android中butterknife的使用與自動(dòng)化查找組件插件詳解
- 詳解Android Studio安裝ButterKnife插件(手動(dòng)安裝)
- Android Studio使用ButterKnife和Zelezny的方法
- Android Kotlin環(huán)境使用ButterKnife的方法
- Android Studio中ButterKnife插件的安裝與使用詳解
- 解決Android Studio 3.0 butterknife:7.0.1配置的問(wèn)題
- Android注解使用之ButterKnife 8.0詳解
- Android注解ButterKnife的基本使用
- Android?ButterKnife依賴注入框架使用教程
相關(guān)文章
Android實(shí)現(xiàn)listview滑動(dòng)時(shí)漸隱漸現(xiàn)頂部欄實(shí)例代碼
android中實(shí)現(xiàn)listview滑動(dòng)時(shí)漸隱漸現(xiàn)頂部欄只是在獲取listview的滑動(dòng)距離上可能沒(méi)法直接獲取,需要?jiǎng)討B(tài)的去計(jì)算。感興趣的朋友一起看看吧2016-10-10
簡(jiǎn)單實(shí)現(xiàn)android短信發(fā)送器
這篇文章主要為大家詳細(xì)介紹了如何簡(jiǎn)單實(shí)現(xiàn)android短信發(fā)送器 ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01
Android之EditText控制禁止輸入空格和回車(chē)
本文主要介紹了Android中使用EditText控制禁止輸入空格和回車(chē)的實(shí)現(xiàn)代碼。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-04-04
純android代碼實(shí)現(xiàn)九宮格手勢(shì)密碼
這篇文章主要為大家詳細(xì)介紹了純android代碼實(shí)現(xiàn)九宮格手勢(shì)密碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
RadioGroup實(shí)現(xiàn)單選框的多行排列
這篇文章主要為大家詳細(xì)介紹了RadioGroup實(shí)現(xiàn)單選框的多行排列,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11
Android自定義控件實(shí)現(xiàn)簡(jiǎn)單的輪播圖控件
這篇文章主要介紹了Android自定義控件實(shí)現(xiàn)簡(jiǎn)單的輪播圖控件的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-04-04
Android編程監(jiān)聽(tīng)網(wǎng)絡(luò)連接狀態(tài)改變的方法
這篇文章主要介紹了Android編程監(jiān)聽(tīng)網(wǎng)絡(luò)連接狀態(tài)改變的方法,基于BroadcastReceiver實(shí)現(xiàn)針對(duì)網(wǎng)絡(luò)連接狀態(tài)的監(jiān)聽(tīng)功能,需要的朋友可以參考下2017-06-06
Android漲姿勢(shì)知識(shí)點(diǎn)之你沒(méi)用過(guò)的BadgeDrawable
現(xiàn)在Android中有許多的應(yīng)用仿蘋(píng)果的在應(yīng)用圖標(biāo)上顯示小紅點(diǎn),下面這篇文章主要給大家介紹了關(guān)于Android漲姿勢(shì)知識(shí)點(diǎn)之你沒(méi)用過(guò)的BadgeDrawable的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-09-09

