註解初始化控件(XUtils方式)

新博客地址

註解初始化控件(XUtils方式)

DEMO鏈接地址

在第一次潭州教育的公開課上接觸了這個框架的講解,我動手研究了一下,結果一出手就停不下來,先後被我碰上了(Glow公司的技術博客——動態Android編程 )、從幾個大牛的博客(學到了github pages + Jekyll 免費製作博客網站)

我發現不寫博客,很多東西就會忘記(代碼如何上傳到jcenter我已經忘記了)

Just Do it!真的會有意想不到的收穫!

實現效果
1. IOC概念介紹
http://www.cnblogs.com/qqlin/archive/2012/10/09/2707075.html我是從這邊文章學習的IOC概念的,寫的淺顯易懂

  • 控件反轉(IOC):創建何種對象的控制權,轉移到第三方
  • 依賴注入(DI):是由IOC容器在運行期間,動態地將某種依賴關係注入到對象之中。
  • IOC與DI之間的關係:DI是一種IOC的具體思想,(編譯運行期,動態注入依賴關係);使用配置文件實現依賴關係的配置也是一種IOC思想(依賴拖拽)。
  • 約定優於配置 這個是什麼鬼??
  • 依賴注入/依賴查找/依賴拖拽
    • 依賴拖拽是通過對注入對象的集中配置實現的
    • 依賴查找是在業務組件代碼中進行的(EJB和Apache Avalon )
      1. XUtils的實現方式XUtils實際上是通過 註解 + 反射 + 動態代理實現的。

layout文件注入:

  • 使用:
@ContentViewInject(R.layout.activity_main)
public class MainActivity extends AppCompatActivity
  • 註解:

@Target(ElementType.TYPE)// 使用對象:類
@Retention(RetentionPolicy.RUNTIME)// 生命範圍:運行期
public @interface ContentViewInject {    
    int value();// 存放佈局id
}
  • 注入代碼:
    private static void injectLayout(Activity context) {
        try {
            // UIClz
            Class<? extends Context> uiClass = context.getClass();
            // 類上的註解
            ContentViewInject annotation = uiClass.getAnnotation(ContentViewInject.class);
            if (annotation != null) {
                // 註解中的layout id值
                int value = annotation.value();
                // 通過反射使用activity中的setContentView方法進行 佈局設置
                // 侷限性:這個方法僅僅適用於activity
                Method setContentView = uiClass.getMethod("setContentView", int.class);
                setContentView.invoke(context, value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

控件注入:

  • 使用:
@ViewInject(R.id.textView)
TextView textView;
......
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TurInject.bind(this);
        textView.setText("徠帝嘎嘎");
    }
  • 註解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
    int value();
}
  • 注入:
    private static void injectViews(Context context) {
        try {
            Class<? extends Context> uiClass = context.getClass();
            // 獲取成員變量
            Field[] fields = uiClass.getDeclaredFields();
            for (Field field : fields) {
                ViewInject annotation = field.getAnnotation(ViewInject.class);
                if (annotation != null) {
                    int value = annotation.value();
                    Method findViewById = uiClass.getMethod("findViewById", int.class);
                    Object invoke = findViewById.invoke(context, value);
                    // 設置允許訪問
                    field.setAccessible(true);
                    field.set(context, invoke);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

事件注入

  • 使用代碼:
    @ClickEvent(value = {R.id.textView}, type = View.OnClickListener.class)
    public void ccClick(View view) {
        Toast.makeText(getBaseContext(), "ccClick", Toast.LENGTH_LONG).show();
    }

    @ClickEvent(value = {R.id.textView}, type = View.OnLongClickListener.class)
    public void longClick(View view) {
        Toast.makeText(getBaseContext(), "longClick", Toast.LENGTH_LONG).show();
    }
  • 註解
 public static void injectMethod(final Context context) {
        try {
            Class<? extends Context> uiClass = context.getClass();
            // 獲取所有的方法
            Method[] methods = uiClass.getMethods();
            for (final Method method : methods) {
                ClickEvent annotation = method.getAnnotation(ClickEvent.class);
                // 塞選含有ClickEvent註解的方法
                if (annotation != null) {
                    int[] values = annotation.value();
                    Class type = annotation.type();
                    // 拿到控件
                    for (int viewId : values) {
                        // 初始化控件
                        Method findViewById = uiClass.getMethod("findViewById", int.class);
                        // view
                        final Object viewObj = findViewById.invoke(context, viewId);

                        Class<?> viewClass = viewObj.getClass();
                        // setOnClickListener/setOnLongClickListener等等
                        String viewSetMethodName = "set" + type.getSimpleName();
                        Method viewMethod = viewClass.getMethod(viewSetMethodName, type);

                        // 動態代理就是針對 任意 一個對象的接口方法的  管理/攔截/AOP
                        ViewEventInvocationHandler viewEventInvocationHandler = new ViewEventInvocationHandler(context, method);
                        //  type.getClassLoader : 類加載器   new Class[]{type} : type爲接口類
                        Object eventInterfaceInstance = Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, viewEventInvocationHandler);
                        // 動態代理  OnClickListener 中的
                        // 執行了setOnClickListener這個方法,那麼在響應這個參數OnClickListener接口中的,onclick方法的時候會響應InvocationHandler中的invoke方法
                        viewMethod.invoke(viewObj, eventInterfaceInstance);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public static class ViewEventInvocationHandler implements InvocationHandler {
        private Context context;
        private Method contentMethod;

        public ViewEventInvocationHandler(Context context, Method contentMethod) {
            this.context = context;
            this.contentMethod = contentMethod;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // method 爲OnClickListener 中的 onClick
            // 系統調用參數接口中的 onClick方法的時候,會響應這個方法
            // 響應這個方法的時候我們需要響應(activity中被ClickEvent標記過的方法)
            // contentMethod 爲activity中被 ClickEvent標記過的方法
            contentMethod.invoke(context, args);
            return true;
        }
    }

知乎——代理、動態代理

  • 主要用來做方法的增強,讓你可以在不修改源碼的情況下,增強一些方法,在方法執行前後做任何你想做的事情(甚至根本不去執行這個方法),因爲在InvocationHandler的invoke方法中,你可以直接獲取正在調用方法對應的Method對象,具體應用的話,比如可以添加調用日誌,做事務控制等。
  • 還有一個有趣的作用是可以用作遠程調用
  • 在某些情況下,一個客戶不想或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。
  • 爲其他對象提供一種代理以控制對這個對象的訪問
  • 動態代理的缺憾:Proxy已經設計得非常優美,但是還是有一點點小小的遺憾之處,那就是它始終無法擺脫僅支持interface代理的桎梏

    到這裏呢,XUtils的佈局注入、控件注入、事件注入就全部介紹完了!

發佈了39 篇原創文章 · 獲贊 9 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章