Android小白,你还在使用findViewById吗?还在setOnClickListener等等。如果你用过butterknife的话,是不是觉得非常方便,有木有?那么问题来了,为啥别人可以写,我们就不能写?so,今天给大家带来自己实现butterknife的功能。
1.首先定义注解
/**
* Created by yzz on 2017/3/22 0022.
*/
//作用域
@Target({ElementType.FIELD,ElementType.METHOD})
//生命周期
@Retention(RetentionPolicy.RUNTIME)
public @interface YzzAnnotation {
public int id()default -1;
public boolean click()default false;
}
该注解作用范围是字段和方法,生命周期在运行时有效(要想注解+反射就要保证注解的Retention为RetentionPolicy.RUNTIME)。有两个字段,分别为id和click分别来表示View的id和是否设置点击的监听。
2.通过反射技术初始化有该注解的字段
(1)初始化
//采用了泛型,灵活性更强
public void bind(T t) {
//软引用防止Context的内存泄漏
softReference = new SoftReference<>(t);
if (t == null) return;
if (t instanceof ViewGroup || t instanceof Activity || t instanceof Fragment) {
reflect();
}
}
(2)通过反射获得Activity或者Fragment或者ViewGroup的字段
/**
* 反射获取字段
*/
private void reflect() {
//获得绑定的实体
T t = softReference.get();
if (t == null) {
throw new RuntimeException("null entity");
}
//获得class
aClass = t.getClass();
try {
//初始化private的字段
init(aClass.getDeclaredFields());
//初始化非private的字段
init(aClass.getFields());
} catch (Exception e) {
e.printStackTrace();
Log.e("========", "=========" + e.toString());
}
}
(3)为字段设置id和点击事件
/**
* 为字段设置id
*
* @param f
* @throws Exception
*/
public void init(Field[] f) throws Exception {
if (f == null) return;
for (Field field : f) {
//判断字段是否有我们自己的注解,有就init
if (field.isAnnotationPresent(YzzAnnotation.class)) {
//这里要获得访问权限
field.setAccessible(true);
YzzAnnotation yzz = field.getAnnotation(YzzAnnotation.class);
//获取findViewById()
Method m = aClass.getMethod("findViewById", int.class);
//调用findViewById()返回Object
Object ob = m.invoke(softReference.get(), yzz.id());
//初始化字段(所属的对象,值)
field.set(softReference.get(), ob);
//设置监听
//判断绑定的该类是否实现了View.OnClickListener接口
Class<?> inter[] = aClass.getInterfaces();
for (Class<?> c:inter){
if (c.getName().equals(View.OnClickListener.class.getName())){
if (yzz.click()) {
//得到setOnclickListener()方法
Method setOnclick = field.getType().getMethod("setOnClickListener",View.OnClickListener.class);
//调用该方法,为字段设置监听 setOnclick.invoke(field.get(softReference.get()),softReference.get());
}
}
}
}
}
}
(4)调用(步骤异常的简单)
//id,click两个属性
@YzzAnnotation(id = R.id.button, click = true)
//支持私有字段(butterknife不支持哦)
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//得到绑定帮助类
YzzAnn<MainActivity> yzzAnn = new YzzAnn<>();
//绑定
yzzAnn.bind(this);
//此时的button!=null了,哈哈
}
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "click", Toast.LENGTH_LONG).show();
button.setText("yzz--coding--!");
}
好了看看效果,是不是很有意思呢?该帮助类还应该支持是普通的类,x.bind(this,View)这种形式的,其实都差不多。下次有机会再补充。github访问源码