欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

浏览Butter Knife源码收获

程序员文章站 2022-06-17 09:38:26
...

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

使用介绍

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

浏览Butter Knife源码收获

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

浏览Butter Knife源码收获

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

浏览Butter Knife源码收获

疑问

Butter Knife是怎么实例化View的

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

浏览Butter Knife源码收获

以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> T findRequiredViewAsType(View source, @IdRes int id, String who,
      Class<T> 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<View> 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 <T> List<T> listOf(T... views) {
    return new ImmutableList<>(filterNull(views));
}
final class ImmutableList<T> extends AbstractList<T> 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源码收获

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

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

get 技能点三

浏览Butter Knife源码收获

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 详解

关于ContextCompatDrawableCompat中方法,可以查看官方文档。

相关标签: Android ButterKnife