红橙Darren视频笔记 IOC注解框架 自己写个注解框架

本节是架构篇开篇 这里先解释一下最终项目的架构
在这里插入图片描述

目标

目标就是不需要进行一大堆的findviewbyid以及各种setListener的杂乱代码,本文以findviewbyid和setOnClickListener为例 介绍如何进行属性绑定和事件绑定

开始实现

1.创建测试module

public class ActivityIocTest extends AppCompatActivity {
    @ViewById(R.id.tv)
    TextView mTextView;
    @ViewById(R.id.btn)
    Button mButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ioc_test);
        ViewUtils.injectActivity(this);
        mTextView.setText("Tv text!!!!");
        mButton.setText("Button text!!!!");
    }

    @OnClick({R.id.tv,R.id.btn})
    public void onItemClick(View view){
        switch (view.getId()){
            case R.id.tv:
                Toast.makeText(this,"text view clicked",Toast.LENGTH_SHORT).show();
                break;
            case R.id.btn:
                Toast.makeText(this,"button clicked",Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Ioc Test" />

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Ioc Test" />

</LinearLayout>

2.创建ioc module 专门生成注解,进行属性和事件绑定
3.测试module依赖ioc模块

dependencies {
    ...
    implementation project(path: ':ioc')//添加ioc模块注解依赖
}

4.编写ioc模块
4.1 注解的声明

/**
 * Created by hjcai on 2021/2/7.
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
    //可以传入多个参数
    int[] value();
}
/**
 * Created by hjcai on 2021/2/5.
 * 注解声明
 */

@Target(ElementType.FIELD)//该注解表示只能用于类的属性
//其他常见属性如 TYPE用在类上  CONSTRUCTOR用在构造函数上 METHOD用于方法
@Retention(RetentionPolicy.RUNTIME)
public//该注解表示在运行时生效
//其他常见属性如 CLASS编译时   RUNTIME运行时  SOURCE源码级别

@interface ViewById {
    //value代表可以该注解可以添加一个参数
    int value();
}

4.2 工具类编写

/**
 * Created by hjcai on 2021/2/5.
 * <p>
 * 主要是调用findViewById
 */
class ViewFinder {
    private Activity mActivity;

    public ViewFinder(Activity activity) {
        this.mActivity = activity;
    }

    public View findViewById(int viewId) {
        return mActivity.findViewById(viewId);
    }
}

4.3 实际注入属性与方法的类

/**
 * Created by hjcai on 2021/2/5.
 */
public class ViewUtils {

    //Activity绑定
    public static void injectActivity(Activity activity) {
        injectActivity(new ViewFinder(activity), activity);
    }

    private static void injectActivity(ViewFinder finder, Object object) {
        injectActivityField(finder, object);
        injectActivityEvent(finder, object);
    }

    //注入属性
    private static void injectActivityField(ViewFinder finder, Object object) {
        //1 反射 获取class里面所有属性
        Class<?> clazz = object.getClass();
        Field[] fields = clazz.getDeclaredFields();//获取Activity的所有属性包括私有和共有
        for (Field field : fields) {
            //2 遍历fields 找到添加了注解ViewById的filed
            ViewById viewById = field.getAnnotation(ViewById.class);
            if (viewById != null) {
                //3 获取注解里面的id值
                int viewId = viewById.value();
                View view = finder.findViewById(viewId);//相当于调用Activity.findViewById
                if (view != null) {
                    field.setAccessible(true);
                    try {
                        //4 动态的注入找到的View
                        field.set(object, view);//利用反射 将object(activity)中的声明了ViewById注解的地方 替换成刚刚find的view
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    private static void injectActivityEvent(ViewFinder finder, Object object) {
        //1 反射 获取class里面所有属性
        Class<?> clazz = object.getClass();
        Method[] methods = clazz.getDeclaredMethods();//获取Activity的所有方法包括私有和共有
        for (Method method : methods) {
            //2 遍历fields 找到添加了注解OnClick的method
            OnClick onClick = method.getAnnotation(OnClick.class);
            if (onClick != null) {
                //3 获取注解里面的id值
                int[] viewIds = onClick.value();
                for (int viewId : viewIds) {
                    View view = finder.findViewById(viewId);//相当于调用Activity.findViewById
                    if (view != null) {
                        // 4. 给每个view设置点击事件
                        view.setOnClickListener(new DeclaredOnClickListener(method, object));
                    }
                }
            }
        }
    }

    private static class DeclaredOnClickListener implements View.OnClickListener {
        private Object mObject;
        private Method mMethod;//实际定义的方法 在这里是 ActivityIocTest.onItemClick

        public DeclaredOnClickListener(Method method, Object object) {
            this.mObject = object;
            this.mMethod = method;
        }

        @Override
        public void onClick(View v) {
            try {
                // 所有方法都可以 包括私有共有
                mMethod.setAccessible(true);
                // 反射执行方法
                // 当View被点击时执行
                mMethod.invoke(mObject, v);//通过反射 调用mObject的声明了onClick注解了的方法 参数为v
            } catch (Exception e) {
                e.printStackTrace();
                try {
                    mMethod.invoke(mObject, null);
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        }
    }
}

小结

这里的原理和xUtil3里面的ioc注解原理一样都是利用annotation+反射实现的

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值