ButterKnife注入框架我相信大家都用过至少听过吧,它的出现简直是android开发人员的一大福利,彻底解放了无止境的findViewById,而是通过优雅的注解来实现view的赋值,如果你真没听过那我在这里只能贴上一张github的截图,自己好好反思吧~
1w5的star你还有什么不学的理由!!!
好的,今天我们不说butterfnife怎么用,而是仿照框架中的实现原理,用最简单的例子自己来实现view的注入!
1、编写自定义注解类
既然是注入的形式对view赋值,那么当然少不了注解,我们最常用的莫过于setContentView和findViewById,我们先来定义这两个注解(注释中顺便帮同学们补一些注解的知识):
setContentView
/**
* @Target描述注解的使用范围
* 1.CONSTRUCTOR:用于描述构造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述局部变量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述参数
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
*/
@Target(ElementType.TYPE)
/**
* @Retention 该Annotation被保留的时间长短
* 1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在运行时有效(即运行时保留)
*/
@Retention(RetentionPolicy.CLASS)
public @interface SetContent {
int value() default -1;
}
findViewById
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface BindView {
int value();
}
注解定义好之后,我们的目标是在activity中这样使用对不对
@SetContent(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv_text)
private TextView textView;
}
这是我们需要一个解析器。
2、编写注解解析器,为这些view赋值
/**
* Created by admin on 2017/4/2.
* 注入解析器
* 单例
*/
public class MyAnnotationInject {
private static MyAnnotationInject INSTANCE;
private List<Object> mInjectObjs = new ArrayList<Object>();
public static MyAnnotationInject init() {
if (INSTANCE == null) {
INSTANCE = new MyAnnotationInject();
}
return INSTANCE;
}
/**
* 注册需要注入的类对象
* @param injectObj
*/
public void regist(@NonNull Object injectObj) {
if(mInjectObjs.contains(injectObj)){
return;
}
if (injectObj instanceof Activity) {
Activity activity = (Activity) injectObj;
//拿到注入的class对象
Class<Activity> clz = (Class<Activity>) activity.getClass();
try {
injectContentView(activity, clz);
injectView(activity, clz);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
mInjectObjs.add(injectObj);
}
/**
* 注入view
*/
private void injectView(Activity activity, Class<Activity> clz) throws IllegalAccessException {
//获得所有的属性,包括public、protected、private
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
//遍历所有属性,找到标有Bindview注解的属性
if (field.isAnnotationPresent(BindView.class)) {
//拿到BindView注解的值
BindView annotation = field.getAnnotation(BindView.class);
int viewId = annotation.value();
//如果是私有属性,修改属性赋值之前必须加可修改权限
field.setAccessible(true);
//给属性重新赋值,参数1:要修改的属性的对象 , 参数2 : 新值
field.set(activity, activity.findViewById(viewId));
}
}
}
/**
* 注入activity布局
*/
private void injectContentView(Activity activity, Class<Activity> clz) {
if (clz.isAnnotationPresent(SetContent.class)) {
//拿到SetContent注解的值
SetContent annotation = clz.getAnnotation(SetContent.class);
int resId = annotation.value();
if (resId != -1) {
activity.setContentView(resId);
}
}
}
/**
* 注销需要注入的类对象
* @param injectObj
*/
public void unRegist(Object injectObj) {
if (mInjectObjs.contains(injectObj))
mInjectObjs.remove(injectObj);
}
}
3、Activity中使用我们的注解来代替findViewById
@SetContent(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv_text)
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyAnnotationInject.init().regist(this);
textView.setText("通过注解注入view成功!");
}
@Override
protected void onDestroy() {
super.onDestroy();
MyAnnotationInject.init().unRegist(this);
}
}
我们看一下效果:
当然我这只是butterknife中冰山一角,如果想要具体学习整个框架可以去学习butterknife的源码~
butterKnife地址,点我