ButterKnife的基本使用和实现原理

    xiaoxiao2022-06-25  167

    一.基本使用 1.在Project的build.gradle中添加如下配置

    buildscript { repositories { mavenCentral() } dependencies { //添加插件 classpath 'com.jakewharton:butterknife-gradle-plugin:8.8.1' } }

    2.在Module的build.gradle添加如下配置

    //引入插件 apply plugin: 'com.jakewharton.butterknife' android { ... } dependencies { //导入 compile 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' }

    使用的相关注解 @BindView—->绑定一个view;id为一个view 变量 @BindViews —-> 绑定多个view;id为一个view的list变量 @BindArray—-> 绑定string里面array数组;@BindArray(R.array.city ) String[] citys ; @BindBitmap—->绑定图片资源为Bitmap;@BindBitmap( R.mipmap.wifi ) Bitmap bitmap; @BindBool —->绑定boolean值 @BindColor —->绑定color;@BindColor(R.color.colorAccent) int black; @BindDimen —->绑定Dimen;@BindDimen(R.dimen.borth_width) int mBorderWidth; @BindDrawable —-> 绑定Drawable;@BindDrawable(R.drawable.test_pic) Drawable mTestPic; @BindFloat —->绑定float @BindInt —->绑定int @BindString —->绑定一个String id为一个String变量;@BindString( R.string.app_name ) String meg; @OnClick—->点击事件 @OnCheckedChanged —->选中,取消选中 @OnLongClick —->长按事件 @OnPageChange —->页面改变事件 @OnTextChanged —->EditText里面的文本变化事件 @OnTouch —->触摸事件 等等

    在Activity中的使用

    public class MainActivity extends AppCompatActivity { //相当于findViewById @BindView(R.id.tv_show) TextView tvShow; //加载资源文件 @BindDrawable(R.drawable.ic_launcher_background) Drawable icon; //点击事件 @OnClick(R.id.btn_go) public void go(){ } Unbinder unbinder; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //绑定 unbinder = ButterKnife.bind(this); tvShow.setText("绑定成功"); } @Override protected void onDestroy() { super.onDestroy(); //解绑 if (unbinder != null) { unbinder.unbind(); } } }

    更多详情查看官方说明https://github.com/JakeWharton/butterknife 二.源码分析 从绑定ButterKnife.bind(this)源码开始

    public static Unbinder bind(@NonNull Activity target) { View sourceView = target.getWindow().getDecorView(); return createBinding(target, sourceView); }

    ButterKnife中createBinding(target, sourceView)方法

    /** * target activity * source decorView */ private static Unbinder createBinding(@NonNull Object target, @NonNull View source) { Class<?> targetClass = target.getClass(); //返回Unbinder的实现类的构造函数 这里即MainActivity_ViewBinding的构造方法 Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass); if (constructor == null) { return Unbinder.EMPTY; } try { //相当于创建了MainActivity_ViewBinding对象 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); } }

    ButterKnife的findBindingConstructorForClass()方法

    //存储着bind类对应的Unbinder对象 static final Map<Class<?>, Constructor<? extends Unbinder>> BINDINGS = new LinkedHashMap<>(); //cls activity对应的class private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) { //从缓存中获取Unbinder的构造方法 Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls); if (bindingCtor != null) { return bindingCtor; } String clsName = cls.getName(); //排除android和java包下的 if (clsName.startsWith("android.") || clsName.startsWith("java.")) { return null; } try { //clsName + "_ViewBinding" 相当于 MainActivity_ViewBinding 实现了implements Unbinder Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding"); //获取MainActivity_ViewBinding的构造方法 public MainActivity_ViewBinding(final MainActivity target, View source) bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class); } catch (ClassNotFoundException e) { bindingCtor = findBindingConstructorForClass(cls.getSuperclass()); } catch (NoSuchMethodException e) { throw new RuntimeException("Unable to find binding constructor for " + clsName, e); } //将对应绑定类和Unbinder对象映射存储起来 BINDINGS.put(cls, bindingCtor); //返回Unbinder的实现类 return bindingCtor; }

    Unbinder的实现类MainActivity_ViewBinding 的源码如下

    public class MainActivity_ViewBinding implements Unbinder { //这里拥有MainActivity的引用 private MainActivity target; private View view2131165218; @UiThread public MainActivity_ViewBinding(MainActivity target) { this(target, target.getWindow().getDecorView()); } @UiThread public MainActivity_ViewBinding(final MainActivity target, View source) { this.target = target; View view; //这里实际是调用了findViewById方法 target.tvShow = Utils.findRequiredViewAsType(source, R.id.tv_show, "field 'tvShow'", TextView.class); view = Utils.findRequiredView(source, R.id.btn_go, "method 'go'"); view2131165218 = view; //设置点击事件 view.setOnClickListener(new DebouncingOnClickListener() { @Override public void doClick(View p0) { target.go(); } }); Context context = source.getContext(); target.icon = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background); } @Override @CallSuper public void unbind() { MainActivity target = this.target; if (target == null) throw new IllegalStateException("Bindings already cleared."); //将绑定的数据清空 this.target = null; target.tvShow = null; view2131165218.setOnClickListener(null); view2131165218 = null; } 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); } public static View findRequiredView(View source, @IdRes int id, String who) { //实际调用了findViewById这句代码 View view = source.findViewById(id); if (view != null) { return view; } .......................................... }

    那么我们并没有MainActivity_ViewBinding 这个类,而MainActivity_ViewBinding 这个类是通过APT在编译时给我们生成的

    APT(Annotation Processing Tool)是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,使用Annotation进行额外的处理。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的java源文件和其它的文件 (文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。 关于APT生成ViewBinding类相关源码参考https://www.jianshu.com/p/0f3f4f7ca505


    最新回复(0)