寫給自己的IoC

IoC是什么呢?
我自己也整不明白,我怎么解釋,自己百度去吧

今天干啥,那就弄個注解加載布局加載view 設(shè)置監(jiān)聽吧
廢話不多說,反正我感覺你也不想聽,那就上代碼吧

首先看界面

@ContentView(layoutRes = R.layout.activity_main)
public class MainActivity extends AppCompatActivity {

    @InjectViews(viewRes = R.id.tv_test)
    TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        InitLayout.inject(this);
        textView.setText("來呀打我呀!");
    }

    @OnClick(viewIds = {R.id.tv_demo})
    public void click(View view){
        Toast.makeText(getBaseContext(),"click",Toast.LENGTH_LONG).show();
    }

    @OnLongClick(viewIds = {R.id.tv_test})
    public boolean longClic(View view) {
        Toast.makeText(getBaseContext(),"longClic",Toast.LENGTH_LONG).show();
        return false;
    }
}

布局不給了,就是兩個文本,自己寫

然后再給你看看注解

注解activity 布局

/*
          TYPE: 用于描述類、接口(包括注解類型) 或enum聲明 Class, interface (including annotation type), or enum declaration
          FIELD: 用于描述域 Field declaration (includes enum constants)
          METHOD: 用于描述方法 Method declaration
          PARAMETER: 用于描述參數(shù) Formal parameter declaration
          CONSTRUCTOR: 用于描述構(gòu)造器 Constructor declaration
          LOCAL_VARIABLE: 用于描述局部變量 Local variable declaration
          ANNOTATION_TYPE: Annotation type declaration
          PACKAGE: 用于描述包 Package declaration
          TYPE_PARAMETER: 用來標(biāo)注類型參數(shù) Type parameter declaration
          TYPE_USE: 能標(biāo)注任何類型名稱 Use of a type

          RetentionPolicy
          SOURCE:注解只保留在源文件,當(dāng)Java文件編譯成class文件的時候,注解被遺棄;
          CLASS:注解被保留到class文件,但jvm加載class文件時候被遺棄,這是默認(rèn)的生命周期;
          RUNTIME:注解不僅被保存到class文件中,jvm加載class文件之后,仍然存在;

 */

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

注解初始化view

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectViews {
    int viewRes() default 0;
}

注解設(shè)置事件

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@ClickBase(listener = "setOnClickListener" ,callback = "onClick",listenerType = View.OnClickListener.class)
public @interface OnClick {
    int[] viewIds();
}

再看這個ClickBase

@Target(ElementType.ANNOTATION_TYPE)//在注解之上
@Retention(RetentionPolicy.RUNTIME)
public @interface ClickBase {

    //設(shè)置事件得方法名稱
    String listener();
    //事件回調(diào)的類行
    Class<?> listenerType();
    //回調(diào)的方法名稱
    String callback();
}

好了前期準(zhǔn)備的差不多了,到核心技術(shù)了,不要眨眼

public class InitLayout {

    public static void inject(Activity activity) {
        injectLayout(activity);
        injectViews(activity);
        injectEvents(activity);
    }

    private static void injectViews(Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();
        //獲取到該方法下的變量(全部,不論訪問權(quán)限)
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            //獲取到注解 InjectViews
            InjectViews injectViews = field.getAnnotation(InjectViews.class);
            if (injectViews != null) {
                //獲取到注解中的布局ID
                int viewid = injectViews.viewRes();

//               View view = activity.findViewById(viewid)  //這個是方式之一,但是不夠檔次
                try {
                    //獲取指定類 (getMethod 第一個是方法名 第二個是參數(shù)的集合 可以用Object[])
                    Method method = clazz.getMethod("findViewById", int.class);
                    //執(zhí)行g(shù)etMethod獲取到的方法
                    Object view = method.invoke(activity, viewid);
                    //給屬性field賦值
                    field.set(activity, view);
                    //設(shè)置訪問權(quán)限
                    field.setAccessible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }
    }

    /**
     * 設(shè)置點擊事件
     *
     * @param activity
     */
    private static void injectEvents(Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();
        //獲取到所有public的方法,包括父類
        Method[] methods = clazz.getMethods();

        //遍歷方法,拿到有自己注解的方法
        for (Method method : methods) {
            //一個方法有多個注解
            Annotation[] annotations = method.getAnnotations();
            /*
                遍歷注解,拿到自己定義的注解
                其實也可以這樣拿到 某個注解 -》ContentView contentView = clazz.getAnnotation(ContentView.class);
                但是這樣會寫死,只支持方法上一種注解
                遍歷所有注解,然后拿到每個注解上帶有ClickBase注解的 注解 真是妙哉妙哉妙哉
                后面只要寫各種注解上使用ClickBase注解就可以
             */
            for (Annotation annotation : annotations) {
                //獲取到注解上面的注解
                Class<? extends Annotation> annotationType = annotation.annotationType();
                if (annotationType != null) {
                    try {
                        //拿到方法注解上的注解
                        ClickBase clickBase = annotationType.getAnnotation(ClickBase.class);

                        if (clickBase != null) {
                            //拿到ClickBase的三個重要的值
                            String callbackStr = clickBase.callback();
                            String listenerStr = clickBase.listener();
                            Class<?> listenerType = clickBase.listenerType();
                            //Aop
                            ClickListenerHandler handler = new ClickListenerHandler(activity);
                            //把方法都添加到ClickListenerHandler中
                            handler.addMethod(callbackStr, method);
                            //實現(xiàn)代理
                            Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, handler);

                            /*
                                  獲取ids的也是可以這樣寫
                                  OnClick onClick = (OnClick) annotation;
                                  int[] ids = onClick.viewIds();
                                  但是這樣又寫死了
                             */

//                            //獲取控件id 的方法
                            Method valueMethod = annotationType.getDeclaredMethod("viewIds");
//                            //執(zhí)行獲取id 的方法,拿到id列表
                            int[] ids = (int[]) valueMethod.invoke(annotation);


                            //遍歷控件id列表
                            for (int id : ids) {
                                View view = activity.findViewById(id);
                                //獲取到設(shè)置監(jiān)聽的方法
                                Method clickListener = view.getClass().getMethod(listenerStr, listenerType);

                                clickListener.invoke(view, listener);
                            }
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

        }


    }

    private static void injectLayout(Activity activity) {
        //拿到activity的反射
        Class<? extends Activity> clazz = activity.getClass();
        //拿到反射類的注解
        ContentView contentView = clazz.getAnnotation(ContentView.class);
        if (contentView != null) {
            //拿到注解的值
            int layout = contentView.layoutRes();
            try {
                //獲取指定類 (getMethod 第一個是方法名 第二個是參數(shù)的集合 可以用Object[])
                Method method = clazz.getMethod("setContentView", int.class);
                //執(zhí)行g(shù)etMethod獲取到的方法
                method.invoke(activity, layout);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
}

好了,這個就是核心代碼了

發(fā)現(xiàn)了沒,我們要設(shè)置事件的時候,就要引入一個東西 Aop
用注解的方法,去替掉回調(diào)的方法,nice 看懂了么,不懂沒關(guān)系,我是不會多說的,耗子尾汁

先看看InvocationHandler實現(xiàn)類

public class ClickListenerHandler implements InvocationHandler {

    //執(zhí)行方法的對象
    private Object target;

    //替換掉回調(diào)的方法
    private HashMap<String, Method> methodHashMap = new HashMap();

    public ClickListenerHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String name = method.getName();
        method = methodHashMap.get(name);
        if (method != null) {
            return method.invoke(target, args);
        }
        return null;
    }

    public void addMethod(String methodName,Method method){
        methodHashMap.put(methodName,method);
    }
}

行了,代碼上了,然后擴展吧

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@ClickBase(listener = "setOnLongClickListener" ,callback = "onLongClick",listenerType = View.OnLongClickListener.class)
public @interface OnLongClick {

    int[] viewIds();
}

剩下的自己去鳥補吧

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容