1. 程式人生 > >Android 之實現執行時註解控制元件

Android 之實現執行時註解控制元件

一、首先的說一下註解的分類,

      1、執行時註解,程式碼簡單,複雜性低,但是效率稍微低一點

      2、編譯時註解、程式碼多,結構複雜,但是效率高

這裡介紹的是執行時註解。

二、還是先說一下思路

    首先建立對應的註解類,並且通過反射 findViewById 方法去實現控制元件的註解

    方法的註解使用了動態代理模式,去減少程式碼的量,然後通過反射去呼叫對應的方法。

    反正特別重要的就是反射反射反射

三、實現(這裡主要是添加布局、獲取控制元件、設定點選事件)

    1、添加布局註解

    正常情況下是要呼叫 setContentView的方法設定Activity的佈局檔案,如果使用註解的話也是一句話偷笑

偷笑

    建立註解檔案,設定註解策略為執行時,作用地方為type型別

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ContentView {
    int value();
}

得到當前的類,並且得到註解中的id,然後通過反射 setContentView實現呼叫

  Class<?> aClass = mContext.getClass();
        //獲取到註解類
        ContentView contentView = aClass.getAnnotation(ContentView.class);
        if (contentView != null) {
            //獲取到註解的id
            int layoutID = contentView.value();
            try {
                // 通過反射執行註解方法
                Method method = aClass.getMethod("setContentView", int.class);
                method.invoke(mContext, layoutID);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }

2、控制元件註解

還是先建立註解類

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ViewInject {
    int value();
}

首先通過傳入的Context得到Class物件

Class<?> aClass = mContext.getClass();

通過DeclaredFields方法當前物件得到所有的成員變數

Field[] declaredFields = aClass.getDeclaredFields();

在遍歷獲取這個陣列,通過每一個成員變數的

getAnnotation方法得到註解,如果註解不為空,則說明有id,是需要的。

然後通過getMethod方法反射findViewById得到該方法,然後反射執行,得到View物件,

然後在把當前值給反射物件

 Class<?> aClass = mContext.getClass();
        //獲取到類中所有的成員變數
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            //得到成員變數的註解
            ViewInject viewInject = field.getAnnotation(ViewInject.class);
            //如果成員變數不為空,說明有註解,則獲取id
            if (viewInject != null) {
                //獲取到控制元件的id
                int valueID = viewInject.value();
                try {
                    //通過反射呼叫findViewById 方法
                    Method method = aClass.getMethod("findViewById", int.class);
                    View view = (View) method.invoke(mContext, valueID);
                    field.setAccessible(true);
                    field.set(mContext, view);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }

3、事件註解

實現思路 獲取當前類中所有的方法,然後去遍歷,然後得到方法的註解,然後判斷是不是存在 EventBase,如果有的話就通過反射findViewById方法得到當前控制元件,再得到當前物件的事件,通過動態代理去實現呼叫

首先的拿到事件的名字、型別、和方法名,所以建立了輔助註解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface EventBase {
    //對點選事件進行擴充套件

    //設定監聽的方法
    String listenerSetter();

    //事件型別
    Class<?> listenerType();

    //事件被觸發後,執行回撥的方法名稱
    String callBackMethod();
}

然後在建立點選事件的註解(這裡以單擊為例,別的點選方法我也會把程式碼展現的)

單擊事件

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerSetter = "setOnClickListener", listenerType = View.OnClickListener.class, callBackMethod = "onClick")
public @interface OnClick {
    //那些控制元件的id,進行點選事件設定
    int[] value();
}

實現事件註解,先獲取當前類的所有方法且遍歷,

然後獲取當前方法的註解

Annotation[] annotations = method.getAnnotations();

在判斷當前註解是否有我們設定的三要素,如果沒有直接進入下一迴圈

有的話直接得到三要素

                 String listenerSeter = eventBase.listenerSetter();
                //得到 listenerType--》 View.OnClickListener.class,
                Class<?> listenerType = eventBase.listenerType();
                //callMethod--->onClick
                String callBackMethod = eventBase.callBackMethod();

通過反射得到當前的方法,然後得到他們的控制元件陣列,再去遍歷,然後通過反射findViewById得到控制元件物件

如果物件不為空通過getMethod得到Method物件,然後通過動態代理得到點選事件的物件,然後呼叫。

        Class<?> clazz = mContext.getClass();
        //獲取Activity中所有的方法
        Method[] methods = clazz.getDeclaredMethods();
        //遍歷所有方法
        for (Method method : methods) {
        //            獲取方法上的所有註解
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                //獲取註解 anntionType   OnClick  OnLongClck
                Class<?> annotationType = annotation.annotationType();
                //獲取註解的註解   onClick 註解上面的EventBase
                EventBase eventBase = annotationType.getAnnotation(EventBase.class);

                if (eventBase == null) {
                    continue;
                }
                 /*
                開始獲取事件三要素  通過反射注入進去
                1 listenerSetter  返回     setOnClickListener字串
                 */
                String listenerSeter = eventBase.listenerSetter();
                //得到 listenerType--》 View.OnClickListener.class,
                Class<?> listenerType = eventBase.listenerType();
                //callMethod--->onClick
                String callBackMethod = eventBase.callBackMethod();
                //方法名 與方法Method的對應關係
                Map<String, Method> methodMap = new HashMap<>();

                methodMap.put(callBackMethod, method);

                try {
                    Method valueMethod = annotationType.getDeclaredMethod("value");
                    int[] viewIDS = (int[]) valueMethod.invoke(annotation);

                    for (int viewID : viewIDS) {
                        Method findViewById = clazz.getMethod("findViewById", int.class);
                        View view = (View) findViewById.invoke(mContext, viewID);
                        if (view == null) {
                            continue;
                        }
                         /*
                        listenerSetter  setOnClickLitener
                        listenerType   View.OnClickListener.class
                         */
                        Method setOnClickListener = view.getClass().getMethod(listenerSeter, listenerType);
                        //使用動態代理實現listenerType介面,類似程式碼設定控制元件點選事件
                        ListenerInvocationHandler handler = new ListenerInvocationHandler(mContext, methodMap);
                        Object proxy = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, handler);
                        setOnClickListener.invoke(view, proxy);
                    }
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }

        }

listview的item點選事件

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerSetter ="setOnItemClickListener"
        ,listenerType = AdapterView.OnItemClickListener.class,callBackMethod = "onItemClick")
public @interface OnItemClick {
    int[] value();
}

長按事件

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerSetter = "setOnLongClickListener",
        listenerType = View.OnLongClickListener.class,callBackMethod = "onLongClick")

public @interface OnLongClick {
    int[] value() default -1;
}

動態代理方法

public class ListenerInvocationHandler implements InvocationHandler{
    //activity   真實物件
    private Context context;
    private Map<String,Method> methodMap;

    public ListenerInvocationHandler(Context context, Map<String, Method> methodMap) {
        this.context = context;
        this.methodMap = methodMap;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String name=method.getName();
        //決定是否需要進行代理
        Method metf=methodMap.get(name);

        if(metf!=null)
        {
            return  metf.invoke(context,args);
        }else
        {
            return method.invoke(proxy,args);
        }
    }
}

呼叫註解例項

@ContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
    @ViewInject(R.id.test)
    Button buttonTest;

    @ViewInject(R.id.listView)
    ListView listView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        InjectUtils.inject(this);
        listView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
                new String[]{"aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa", "aaa"}));
    }

    @OnItemClick(R.id.listView)
    public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
        Toast.makeText(MainActivity.this, "aaaaaaaaaaa" + position, Toast.LENGTH_SHORT).show();
    }

    @OnClick(R.id.test)
    public void testClick(View view) {
        Toast.makeText(MainActivity.this, "aaaaaaaaaaa", Toast.LENGTH_SHORT).show();
    }

    @OnLongClick({R.id.test2, R.id.test})
    public boolean testLongClick(View view) {
        Toast.makeText(MainActivity.this, "bbbbbbbbbbbbbbbbbbbbbbbbbb", Toast.LENGTH_SHORT).show();


        return true;
    }
}

我會在我的資源裡儲存原始碼

相關推薦

Android 實現執行註解控制元件

一、首先的說一下註解的分類,      1、執行時註解,程式碼簡單,複雜性低,但是效率稍微低一點      2、編譯時註解、程式碼多,結構複雜,但是效率高這裡介紹的是執行時註解。二、還是先說一下思路    首先建立對應的註解類,並且通過反射 findViewById 方法去實

Android中的自定義註解(反射實現-執行註解

預備知識: Java註解基礎 Java反射原理 Java動態代理 一、佈局檔案的註解 我們在Android開發的時候,總是會寫到setContentView方法,為了避免每次都寫重複的程式碼,我們需要使用註解來代替我們做這個事情,只需要在類Activity上

【我的Android進階旅】自定義控制元件使用ViewPager實現可以預覽的畫廊效果,並且自定義畫面切換的動畫效果的切換時間

我們來看下效果 在這裡,我們實現的是,一個ViewPager來顯示圖片列表。這裡一個頁面,ViewPage展示了前後的預覽,我們讓預覽頁進行Y軸的壓縮,並設定透明度為0.5f,所有我們看到gif最後,左右兩邊的圖片有點朦朧感。讓預覽頁和主頁面有主從感。我們用分

Android實現執行操作的幾種方法

在Android開發中我們可能會有延時執行某個操作的需求,例如我們啟動應用的時候,一開始呈現的是一個引導頁面,過了兩三秒後,會自動跳轉到主介面。這就是一個延時操作。  下面是實現延時執行操作的幾種方法: 1.使用執行緒的休眠實現延時操作    new Thread() {

Android自定義View實戰】自定義評價打分控制元件RatingBar,可以自定義星星大小和間距

在Android開發中,我們經常會用到對商家或者商品的評價,運用星星進行打分。然而在Android系統中自帶的打分控制元件,RatingBar特別不好用,間距和大小無法改變。所以,我就自定義了一個特別好用的打分控制元件。在專案中可以直接使用,特別簡

註解學習初始化佈局及控制元件2

/** * @author anqiansong *初始化佈局 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface InitContentView { public int

[Android]ButterKnife-無盡刃-繫結檢視控制元件和事件的快速開發工具

簡單介紹 Butter knife是大神JakeWharton的一款Android利器,多數開發者都應該瞭解和使用過,這把黃油刀最大的吸引人的地方就是簡化了android程式編寫中的view,fin

Android kotlin Fragment怎樣直接獲取控制元件

在onCreateView方法中你可以直接使用當前佈局返回的view.id來使用控制元件。 override fun onCreateView(inflater: LayoutInflater

Android中RecyclerView的item中控制元件的點選事件新增刪除一行、上移下移一行的程式碼實現

Demo展示圖片 佈局程式碼 // (layout)activity_main.xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/an

自定義註解執行註解(RetentionPolicy.RUNTIME)

前面有提到註解按生命週期來劃分可分為3類: 1、RetentionPolicy.SOURCE:註解只保留在原始檔,當Java檔案編譯成class檔案的時候,註解被遺棄; 2、RetentionPolicy.CLASS:註解被保留到class檔案,但jvm載入class檔案

Android實現滑動開關元件

    由於Android並未提供滑動開關之類的元件,所以我們需要自己去實現一個自定義的檢視元件來實現滑動開關效果。     這裡有一個示例程式碼,它包括三個類:開關元件檢視、狀態監聽介面、MainActivity     我們先來看看整個demo的效果圖:    我們先

android RecyclerView介面卡實現item+item內部控制元件點選事件

@先看看效果是不是自己想要的吧@Adapter:自定義一個繼承RecyclerView.Adapter的介面卡,作用:將資料與每一個item繫結LayoutManager:佈局管理器,確定每一個item的擺放形式,展示和隱藏        a: LinearLayoutMan

android 6.0執行許可權應用相簿圖片選取

public class MainActivity extends AppCompatActivity { public static final int REQUEST_CODE=200; public static final int REQUEST_C

Android自定義多TAB懸浮控制元件實現蘑菇街首頁效果

說明: 1.viewpager不能左右滑動; 2.轉載時程式碼略有改動(注:修復tab控制元件不能自動滑動的問題); 3.原始碼下載後不能直接執行,經過調整此問題已經修復。 核心程式碼: package com.Imy.Fuli.Fra

Android簡單互動,點選控制元件短暫的放大後恢復正常

原理:監聽控制元件的事件,在touch事件觸發是播放放大動畫; 說明:在onTouch()事件中,動畫的播放要放在對DOWN事件的處理中,不能簡單的放在onTouch()的方法裡面裡了事,那樣會有一

Android-ListView中巢狀(ListView)控制元件item的點選事件不起作用的問題

解決:1、在主listview佈局檔案中的listview中新增屬性 android:focusable="false"              2、在子listview中最頂上的佈局檔案新增屬性

Android實現畫虛線的控制元件

在Android的UI開發中,有時為了介面美觀而需要虛線。我自己實現一個虛線控制元件。 package com.custom;    import android.content.Context; import android.graphics.Canvas; import

android 在 button、textview等控制元件按下和選中的時候改變顏色或圖片實現方法

首先,在res資料夾下新建一個資料夾drawable,這是無關解析度的: 在下面建立一個xml檔案:select.xml 方法1 <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android

Android——(SQLite資料庫)ListView控制元件商品展示案例

一、實驗目的 開發一個購物車,需要將購物車中的商品以列表的形式展示,並且還需要對購物車中的商品進行增刪改查操作。要實現這些功能就需要使用 ListView 和 SQLite 資料庫。 二、 訓練目標 1) 掌握 SQLite 儲存方式 2) 掌握如何使用 SQLite 完成

Android進階——Material Design新控制元件TabLayout製作可滾動的Tabs頁面(二)

引言 上一篇總結講解了下TabLayout的基本特點、屬性和簡單的應用步驟,相信大家已經對於TabLayout已經不再陌生,這篇就結合Fragment+ViewPager的架構開發一個主流App的主要框架,進一步學習下TabLayout的操作和監聽,同時分享一