一、ButterKnife使用
1、在build.gradle的dependencies添加类库
implementation 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
2、初始化绑定以Activity为例,其他类同。
unbinder =ButterKnife.bind(this);
3、对各个元素进行绑定
//绑定单个view @BindView(R.id.title_tv) TextView titleTv; //绑定字符串 @BindString(R.string.bind_string) String bindString; //绑定Color @BindColor(R.color.red) int red; //绑定Dimen @BindDimen(R.dimen.bind_dimen) float bindDimen; // dip * 屏幕密度 //绑定Drawable @BindDrawable(R.mipmap.sky) Drawable bindDrawable; //绑定多个view @BindViews({R.id.tv_1,R.id.tv_2,R.id.tv_3}) List<TextView> textTvs;
//绑定点击事情
@OnClick({R.id.onclick_tv})
public void onClick(View view){
switch (view.getId()){
case R.id.onclick_tv:
Toast.makeText(ActivityButterKnife.this,"点击事件绑定",Toast.LENGTH_SHORT).show();
break;
}
}
4、对事件,View等进行解绑
unbinder.unbind();
二、ButterKnife原理分析
- ButterKnife原理--创建Class_ViewBinding.class,需要三步
安卓编译时注解,在安卓编译时遍历注解,注解部分生成java类,保存在相同目录下。
(1)在安卓编译时遍历注解,对各个类型注解处理。
(2)注解部分生成java类
(3)保存在相同目录下
@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
TypeElement typeElement = entry.getKey();
BindingSet binding = entry.getValue();
JavaFile javaFile = binding.brewJava(sdk, debuggable, useAndroidX);
try {
javaFile.writeTo(filer);
} catch (IOException e) {
error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
}
}
return false;
}
2.ButterKnife原理—获取 Class_ViewBinding.class
(1)ButterKnife.bind(this) 相当于 new Class_ViewBinding(this)
(2)Activity为例:获取View,创建Class的绑定实例
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull Activity source) {
View sourceView = source.getWindow().getDecorView();
return createBinding(target, sourceView);
}
(3)通过反射获取构造函数
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
//根据ClassName从缓存中获取构造函数
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
if (bindingCtor != null) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
//不搜寻,系统类
String clsName = cls.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return null;
}
try {
//如果没有,拿到Class_ViewBinding字节码,获取构造函数
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
//noinspection unchecked
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor.");
} catch (ClassNotFoundException e) {
if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
//找不Class_ViewBinding,一直遍历父类,直到遇到(android 或java开头包名的类)
bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
} catch (NoSuchMethodException e) {
throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
}
BINDINGS.put(cls, bindingCtor);
return bindingCtor;
}
(4)利用构造函数实例化Class_ViewBinding对象
private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {//反射通过构造函数获取实例
return constructor.newInstance(target, source);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InstantiationException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException("Unable to create binding instance.", cause);
}
}