浏览Butter Knife源码收获

github地址:https://github.com/JakeWharton/butterknife

使用介绍

关于Butter Knife的使用,可以看作者写的使用介绍,挺详细的。在这推荐一个插件Android ButterKnife Zelezny,其作用是从选中的布局文件中生成相关的Butter Knife代码。

浏览Butter Knife源码收获_第1张图片

点一下图中下方红色框内的按钮

浏览Butter Knife源码收获_第2张图片

安装好后,重启Android Studio。用的时候,将光标移到引用布局文件的代码处,按快捷键Alt+Insert。

浏览Butter Knife源码收获_第3张图片

疑问

Butter Knife是怎么实例化View的

在工程的依赖项里面会有两个关于Butter Knife的依赖包,一个包全是注解,比如我们会用到的@BindView等;另一个包,就是实现实例化View的关键,它只有六个类文件。

浏览Butter Knife源码收获_第4张图片

以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 constructor = findBindingConstructorForClass(targetClass);

    if (constructor == null) {
      return Unbinder.EMPTY;
    }

    ......

    return constructor.newInstance(target, source);

    ......
}

在这,target就是ComingFragment实例对象,将其类信息传入findBindingConstructorForClass(targetClass)中;


private static Constructor findBindingConstructorForClass(Class cls) {
    ........
    Class bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");

    bindingCtor = (Constructor) 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()来获取的。

收获

get 技能点一

继承抽象类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 的父类。是第一个实现随机访问方法的集合类,但不支持添加和替换。

get 技能点二

浏览Butter Knife源码收获_第5张图片

bind()上有两个注解@NonNull@UiThread(都在android.support.annotation包下);@UiThread表示注释元素只能在UI线程上调用。如果注释元素是一个类,那么类中的所有方法都应该在UI线程上调用。

关于android.support.annotation包下的其他注解使用,可以看我的下篇博客。

get 技能点三

浏览Butter Knife源码收获_第6张图片

getTintedDrawable()其作用是为图片着色。方法中ContextCompatDrawableCompat提供了兼容了不同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中方法,可以查看官方文档。

你可能感兴趣的:(Android,Android,ButterKnife)