github地址:https://github.com/JakeWharton/butterknife
关于Butter Knife的使用,可以看作者写的使用介绍,挺详细的。在这推荐一个插件Android ButterKnife Zelezny,其作用是从选中的布局文件中生成相关的Butter Knife代码。
点一下图中下方红色框内的按钮
安装好后,重启Android Studio。用的时候,将光标移到引用布局文件的代码处,按快捷键Alt+Insert。
Butter Knife是怎么实例化View的
在工程的依赖项里面会有两个关于Butter Knife的依赖包,一个包全是注解,比如我们会用到的@BindView等;另一个包,就是实现实例化View的关键,它只有六个类文件。
以ComingFragment为例,Butter Knife是怎么实例化mContent的呢?
public class ComingFragment extends BaseFragment implements ComingMvpView {
@BindView(R.id.content)
MyFrameLayout mContent;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_coming, container, false);
ButterKnife.bind(this, view);
return view;
}
}
在onCreateView()
中,调用了ButterKnife.bind(this, view)
;
@NonNull
@UiThread
public static Unbinder bind(@NonNull Object target, @NonNull View source) {
return createBinding(target, source);
}
又调用了createBinding(target, source)
;
private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {
Class> targetClass = target.getClass();
Constructor extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
......
return constructor.newInstance(target, source);
......
}
在这,target就是ComingFragment实例对象,将其类信息传入findBindingConstructorForClass(targetClass)
中;
private static Constructor extends Unbinder> findBindingConstructorForClass(Class> cls) {
........
Class> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
bindingCtor = (Constructor extends Unbinder>) bindingClass.getConstructor(cls, View.class);
........
}
ClassLoader根据得到的类信息,调用loadClass()
去加载另一个class文件。这个类就是在编译项目时候,Butter Knife 的注解编译器(如果对编写注解编译器感兴趣,可以在网上搜索相关的资料)根据注解生成的相应类文件。
public class ComingFragment_ViewBinding implements Unbinder {
private ComingFragment target;
@UiThread
public ComingFragment_ViewBinding(ComingFragment target, View source) {
this.target = target;
target.mContent = Utils.findRequiredViewAsType(source, R.id.content, "field 'mContent'", MyFrameLayout.class);
}
..........
}
target.mContent,target就是转入的ComingFragment实例对象;mContent是怎么实例化的?再看看Utils类中findRequiredViewAsType()
的实现。
public static T findRequiredViewAsType(View source, @IdRes int id, String who,
Class cls) {
View view = findRequiredView(source, id, who);
return castView(view, id, who, cls);
}
调用findRequiredView()
,返回View;离答案很近了,激动。
public static View findRequiredView(View source, @IdRes int id, String who) {
View view = source.findViewById(id);
if (view != null) {
return view;
}
.......
}
原来,还是通过findViewById()
来获取的。
继承抽象类AbstractList,实现不可变的List;也就是只能获取List的信息,不能改变List。Collections.unmodifiableList( )
也可以使List不可变。
@BindViews({R.id.filmmaker_tv_foreign_name,R.id.filmmaker_tv_born_place,R.id.filmmaker_recycler_view})
List mViewList;
将View对象放入mViewList中。在这,如果调用改变mViewList的方法;比如,调用mViewList.remove()
,程序在运行的时候就会Crash,抛出java.lang.UnsupportedOperationException异常。出现这样的情况,是因为mViewList的父类是AbstractList。
target.mViewList = Utils.listOf(
Utils.findRequiredView(source, R.id.filmmaker_tv_foreign_name, "field 'mViewList'"),
Utils.findRequiredView(source, R.id.filmmaker_tv_born_place, "field 'mViewList'"),
Utils.findRequiredView(source, R.id.filmmaker_recycler_view, "field 'mViewList'"));
public static List listOf(T... views) {
return new ImmutableList<>(filterNull(views));
}
final class ImmutableList extends AbstractList implements RandomAccess {
private final T[] views;
ImmutableList(T[] views) {
this.views = views;
}
@Override public T get(int index) {
return views[index];
}
@Override public int size() {
return views.length;
}
@Override public boolean contains(Object o) {
for (T view : views) {
if (view == o) {
return true;
}
}
return false;
}
}
ImmutableList类在butterknife.internal包下;继承了抽象类AbstractList,实现了标记接口RandomAccess(其作用是在对列表进行随机或顺序访问的时候,访问算法能够选择性能最佳方式)。
AbstractList 继承自 AbstractCollection 抽象类,实现了 List 接口 ,是 ArrayList 的父类。是第一个实现随机访问方法的集合类,但不支持添加和替换。
bind()
上有两个注解@NonNull
和@UiThread
(都在android.support.annotation包下);@UiThread
表示注释元素只能在UI线程上调用。如果注释元素是一个类,那么类中的所有方法都应该在UI线程上调用。
关于android.support.annotation包下的其他注解使用,可以看我的下篇博客。
getTintedDrawable()
其作用是为图片着色。方法中ContextCompat
与DrawableCompat
提供了兼容了不同API版本的方法。比如,ContextCompat
中的getColor()
@ColorInt
public static final int getColor(@NonNull Context context, @ColorRes int id) {
if (Build.VERSION.SDK_INT >= 23) {
return context.getColor(id);
} else {
return context.getResources().getColor(id);
}
}
关于DrawableCompat.wrap()
与drawable.mutate()
的用法,可以查看博客Android 图片着色 Tint 详解
关于ContextCompat与DrawableCompat中方法,可以查看官方文档。