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