王学岗移动架构34——IOC注入框架设计

本框架可以在Activity和Dialog中使用,并且已有代码示例。fragment读者自己加上就可以了,没写代码
在这里插入图片描述

package com.example.lsn_34;

import android.app.Activity;
import android.os.Bundle;

public class BaseActivity  extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //多态,继承BaseActivity类的类就不用重写了。
        InjectUtils.inject(this);
    }
}

package com.example.lsn_34;

import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;


/**
 * Created by david on 2017/8/21.
 */

public class BaseDialog   extends Dialog {
    public BaseDialog(@NonNull Context context) {
        super(context);
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        InjectUtils.inject(this);
    }

}

package com.example.lsn_34;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)//运行时候有效,程序运行过程中要访问注解
@Target(ElementType.TYPE)//写到类上 面
public @interface ContentView {
    //调用该方法就可以拿到MainActivity上面注解,即:@ContentView(R.layout.activity_main) 中的括号内的内容
    int value();
}

package com.example.lsn_34;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 * 相当于java父类,
 */
@Retention(RetentionPolicy.RUNTIME)
//该注解在另外一个注解上使用
@Target(ElementType.ANNOTATION_TYPE)
public @interface EventBase {
//  setOnClickListener  订阅
    String  listenerSetter();

//    事件以及他的类型
    /**
     * 事件监听的类型
     * @return
     */
    Class<?> listenerType();

    /**
     * 事件处理
     * @return
     */
    String callbackMethod();

}

package com.example.lsn_34;

import android.view.View;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//注入工具类,相当于我们的女朋友,负责找资源,把找到的资源交给Activity类。
public class InjectUtils {
//这个方法给Activity用
    public  static  void inject(Object context){
        //注入布局
         injectLayout(context);
         //注入组件
        injectView(context);
        //注入事件
        injectClick(context);

    }

    /**
     * 事件注入,事件注入比较复杂,因为android的事件不只一种。既要知道是哪一个控件,也要知道是哪一个事件
     * 已OnCLick事件为例,我们分析下事件的三个要素
     * textView.setOnClickListener(new View.OnClickListener() {
     *           @Override
     *            public void onClick(View v) {
     *            }
     *       });
     *       事件源:TextView
     *       事件:new View.OnClickListener()
     *       事件处理:onClick
     *   每个要素都要单独提取出来
     *  另外有事件的订阅setOnClickListenner();
     */
    private static void injectClick(Object context) {
        //拿到Activity
        Class<?> clazz=context.getClass();
        //拿到该Activity所有的方法,然后遍历所有的方法,看看哪个方法有相关注解
        Method[] methods=clazz.getDeclaredMethods();
        for (Method method:methods)
        {
//            OnClick onClick = method.getAnnotation(OnClick.class);//这就写死了,不具备通用性
            //得到方法上的所有注解
            Annotation[] annotations = method.getAnnotations();

            for (Annotation annotation : annotations) {

//                annotation  ===OnClick  OnClick.class
                //得到注解的种类
                Class<?> annotionClass=annotation.annotationType();
                //是否能得到@EventBase的注解
                EventBase eventBase = annotionClass.getAnnotation(EventBase.class);
                //如果没有eventBase,则表示当前方法不是一个处理事件的方法
                if(eventBase==null)
                {
                    continue;
                }
               //开始获取事件处理的相关信息,即三要素
                // 用于确定是哪种事件(onClick还是onLongClick)以及由谁来处理
                //订阅
                String listenerSetter=eventBase.listenerSetter();
                //事件(事件监听的类型)
                Class<?> listenerType=eventBase.listenerType();
                //事件处理   事件被触发之后,执行的回调方法的名称
                String callBackMethod=eventBase.callbackMethod();
/**
 * 拿到三要素之后,我们就想办法执行这个事件
 *  textView.setOnClickListener(new View.OnClickListener() {
 *                               @Override
 *                               public void onClick(View v) {
 *
 *                               }
 *                           });
 *      listenerSetter和callBackMethod是两个字符串,需要通过反射得到,同时要反射得到textView
 */


//                int[] value1=OnClick.value();//这就写死了

                Method valueMethod= null;
                try {
                    //反射得到ID,再根据ID号得到对应的VIEW
                    valueMethod = annotionClass.getDeclaredMethod("value");
                    //得到在Activity的方法中标示的所有的ID,如@OnLongClick({R.id.app_text,R.id.app_text1})
                    int[] viewId= (int[]) valueMethod.invoke(annotation);
                    //每一个ID都需要事件绑定
                    for (int id : viewId) {
                        Method findViewById=clazz.getMethod("findViewById",int.class);
                        View view= (View) findViewById.invoke(context,id);
                        if(view==null)
                        {
                            continue;
                        }
                        //得到ID对应的VIEW以后
                        //开始在这个VIEW上执行监听,使用动态代理,将代码转移到Activity上的onClick(添加自定义注解的方法)上
                        //
                        //activity==context       click==method
                        ListenerInvocationHandler listenerInvocationHandler = new ListenerInvocationHandler(context, method);
                        //proxy======View.OnClickListener()对象
                        Object proxy=Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, listenerInvocationHandler);

                        //执行方法                                   setOnClickListener,new View.OnClickListener()
                        Method onClickMethod = view.getClass().getMethod(listenerSetter, listenerType);
                        onClickMethod.invoke(view, proxy);

                    }

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

            }

        }


    }

    /**
     * 控件注入
     * @param context
     */
    private static void injectView(Object context) {
        //拿到对应的Activity
        Class<?> aClass=context.getClass();
        //拿到所有的成员变量,然后一一判断,判断哪些字段添加了 @ViewInject(R.id.app_text)注解
        Field[] fields=aClass.getDeclaredFields();
        //遍历成员变量
        for (Field field:fields)
        {
            ViewInject viewInject = field.getAnnotation(ViewInject.class);
            if (viewInject != null) {
                int valueId = viewInject.value();
                try {
                    Method method=aClass.getMethod("findViewById",int.class);
                    View view = (View) method.invoke(context, valueId);
//                    View view= mainActivity.findViewById(valueId);
                    field.setAccessible(true);
                    //Activity身上的字段field等于view即:context.field = view;
                    field.set(context,view);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * 布局注入
     * @param context
     */
    private static void injectLayout(Object context) {
        int layoutId=0;//要注入的布局ID
        //拿到MainActivity上面的注解,即:@ContentView(R.layout.activity_main)
        /**
        *我们知道在Java中一切都是对象,我们一般所使用的对象都直接或间接继承自Object类。
        Object类中包含一个方法名叫  getClass,利用这个方法就可以获得一个实例的类型类。
        类型类指的是代表一个类型的类,因为一切皆是对象,
       类型也不例外,在Java使用类型类来表示一个类型。所有的类型类都是Class类的实例。
       *
        */
        
        Class<?> clazz=context.getClass();
        ContentView contentView = clazz.getAnnotation(ContentView.class);
        if (contentView != null) {
            //得到Activity类的注解@ContentView(R.layout.activity_main)中括号内的信息
            layoutId = contentView.value();

            try {
                //反射执行setContentView.
                //int.class:参数是整数
               // getMethod方法则根据方法名称和相关参数,来定位需要查找的Method对象并返回。
                Method method = context.getClass().getMethod("setContentView", int.class);
             //context 参数:实例化后的对象
                method.invoke(context, layoutId);
            } catch ( Exception e) {
                e.printStackTrace();
            }


        }
    }
}

package com.example.lsn_34;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 代理类
 * 代理下面这段代码
 *     public boolean click(View view) {
 *     }
 *     主体是Activity,要执行的是click动作
 */
public class ListenerInvocationHandler implements InvocationHandler {
    //     OnClickListener  1
//    MainActivity   2
    private Object activity;
    //要执行的方法
    private  Method activityMethod;

    public ListenerInvocationHandler(Object activity, Method activityMethod) {
        this.activity = activity;
        this.activityMethod = activityMethod;
    }

    /**
     *按钮点下去就执行这个方法
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在这里去调用被注解了的click()
        return activityMethod.invoke(activity,args);
    }
}

package com.example.lsn_34;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;


@ContentView(R.layout.activity_main)
public class MainActivity extends BaseActivity {

    @ViewInject(R.id.app_text)
    private Button textView;
    private Button textView1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    @Deprecated
    @OnClick({R.id.app_text,R.id.app_text1})
    public boolean click(View view) {
        Toast.makeText(this,"---->"+textView,Toast.LENGTH_SHORT).show();
        NewsDialog newsDialog = new NewsDialog(this);
        newsDialog.show();
        return false;
    }

    @OnLongClick({R.id.app_text,R.id.app_text1})
    public boolean longClick(View view) {
        Toast.makeText(this, "长按了", Toast.LENGTH_SHORT).show();
        return true;
    }
}

package com.example.lsn_34;

import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.Toast;

/**
 * Created by david on 2017/8/21.
 */
@ContentView(R.layout.dialog_news)
public class NewsDialog extends BaseDialog {
    @ViewInject(R.id.dialogBtn)
    Button dialogBtn;

    public NewsDialog(@NonNull Context context) {
        super(context);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Toast.makeText(getContext(), "dialogBtn "+dialogBtn, Toast.LENGTH_SHORT).show();
    }
    @OnClick(R.id.dialogBtn)
    public void click(View view) {
        Toast.makeText(getContext(), "  dialog点击啦", Toast.LENGTH_SHORT).show();
    }
    @Override
    public void show() {
        super.show();
        /**
         * 设置宽度全屏,要设置在show的后面
         */
        WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
        layoutParams.gravity= Gravity.BOTTOM;
        layoutParams.width= WindowManager.LayoutParams.MATCH_PARENT;
        layoutParams.height= WindowManager.LayoutParams.WRAP_CONTENT;
        getWindow().getDecorView().setPadding(0, 0, 0, 0);
        getWindow().setAttributes(layoutParams);
    }
}

package com.example.lsn_34;


import android.view.View;

import com.example.lsn_34.EventBase;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)//注解写在方法上
@EventBase(listenerSetter = "setOnClickListener"
        , listenerType = View.OnClickListener.class
        , callbackMethod = "onClick")
public @interface OnClick {
    int[] value() default -1;
}
  
package com.example.lsn_34;

import android.view.View;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerSetter = "setOnLongClickListener", listenerType = View.OnLongClickListener.class, callbackMethod = "onLongClick")
public @interface OnLongClick {
    int[] value() default -1;
}

package com.example.lsn_34;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by david on 24/2/2017.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)//写到成员变量上面
public @interface ViewInject {
    int value();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值