Butterknife

1,简介

butterKnife是一个view依赖注入框架,可以将一些模块代码如findViewById和OnClick等通过注解方式简化;

依赖如下:(注意,annotationProcessor与dagger产生冲突)

 // https://mvnrepository.com/artifact/com.jakewharton/butterknife
    implementation'com.jakewharton:butterknife:10.2.3'
    annotationProcessor'com.jakewharton:butterknife-compiler:10.2.3'

2,实例

创建一个Activity,使用ButterKnife绑定View并且设置事件,


public class ButterKnifeActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    // 通过注解方式,绑定对应view,或者views
    @BindView(R.id.text_view)
    TextView textView;

    @BindView(R.id.button)
    Button button;

    @BindView(R.id.image_view)
    ImageView imageView;

    @BindView(R.id.checkbox)
    CheckBox checkBox;

    @BindView(R.id.progressBar)
    ProgressBar progressBar;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // onCreate编译期间生成代码
        ButterKnife.bind(this);

        Log.d(TAG, "onCreate:  "+textView+" "+button+" "+imageView+" "+checkBox+" "+progressBar);
    }

    @OnClick(value = {R.id.button})
    public void click(View view){
        switch (view.getId()){
            case R.id.button:
                Log.d(TAG, "button was be click!");
                textView.setText("button was be click!");
                break;
            default:
                break;
        }
    }

    @OnTouch(value = R.id.text_view)
    public void touch(){
        Log.d(TAG, "on touch!");
    }

}
2021-12-20 10:12:38.675 11677-11677/com.zjw.demoApp D/MainActivity: onCreate:  com.google.android.material.textview.MaterialTextView{e7406c1 V.ED..... ......ID 0,0-0,0 #7f0801ad app:id/text_view} com.google.android.material.button.MaterialButton{bbbde66 VFED..C.. ......I. 0,0-0,0 #7f080062 app:id/button} androidx.appcompat.widget.AppCompatImageView{5a4f5a7 V.ED..... ......I. 0,0-0,0 #7f0800d5 app:id/image_view} com.google.android.material.checkbox.MaterialCheckBox{3faa54 VFED..C.. ......I. 0,0-0,0 #7f08006c app:id/checkbox} android.widget.ProgressBar{1f94dfd V.ED..... ......I. 0,0-0,0 #7f080146 app:id/progressBar}
2021-12-20 10:12:45.903 11677-11677/com.zjw.demoApp D/MainActivity: button was be click!
2021-12-20 10:13:03.852 11677-11677/com.zjw.demoApp D/MainActivity: on touch!

当然,除此之外还有其它控件事件处理,

//listView item点击事件
@OnItemSelected(R.id.list_view)
void onItemSelected(int position) {
// TODO …
}

//view的onTouchEvent
@OnTouch(R.id.example) boolean onTouch() {
Toast.makeText(this, “Touched!”, Toast.LENGTH_SHORT).show();
return false;
}

//监听EditText的addTextChangedListener
@OnTextChanged(R.id.example) void onTextChanged(CharSequence text) {
Toast.makeText(this, "Text changed: " + text, Toast.LENGTH_SHORT).show();
}

//设置ViewPager的OnPageChangeListener
@OnPageChange(R.id.example_pager) void onPageSelected(int position) {
Toast.makeText(this, "Selected " + position + “!”, Toast.LENGTH_SHORT).show();
}

//设置TextView的OnEditorActionListener(该事件主要用来设置软键盘上的按钮)
@OnEditorAction(R.id.example) boolean onEditorAction(KeyEvent key) {
Toast.makeText(this, "Pressed: " + key, Toast.LENGTH_SHORT).show();
return true;
}

//设置View的OnFocusChangeListener事件
@OnFocusChange(R.id.example) void onFocusChanged(boolean focused) {
Toast.makeText(this, focused ? “Gained focus” : “Lost focus”, Toast.LENGTH_SHORT).show();
}

//设置View的OnLongClickListener长按事件
@OnLongClick(R.id.example) boolean onLongClick() {
Toast.makeText(this, “Long clicked!”, Toast.LENGTH_SHORT).show();
return true;
}

//关于资源的绑定
@BindString(R.string.title) String title; //字符串
@BindDrawable(R.drawable.graphic) Drawable graphic; //drawable
@BindColor(R.color.red) int red; // int or ColorStateList field
@BindDimen(R.dimen.spacer) Float spacer; // int (for pixel size) or float (for exact value) field
@BindArray(R.array.countries) String[] countries; 字符串数组
@BindArray(R.array.icons) TypedArray icons;
@BindBool(R.bool.is_tablet) boolean isTablet;

3,原理

(1)与dagger相同,通过生成代码减少性能影响,生成代码如下,


public class ButterKnifeActivity_ViewBinding implements Unbinder {
  private ButterKnifeActivity target;

  private View view7f0801ad;

  private View view7f080062;

  @UiThread
  public ButterKnifeActivity_ViewBinding(ButterKnifeActivity target) {
    this(target, target.getWindow().getDecorView());
  }

  @UiThread
  @SuppressLint("ClickableViewAccessibility")
  public ButterKnifeActivity_ViewBinding(final ButterKnifeActivity target, View source) {
    this.target = target;

    View view;
    view = Utils.findRequiredView(source, R.id.text_view, "field 'textView' and method 'touch'");
    target.textView = Utils.castView(view, R.id.text_view, "field 'textView'", TextView.class);
    view7f0801ad = view;
    view.setOnTouchListener(new View.OnTouchListener() {
      @Override
      public boolean onTouch(View p0, MotionEvent p1) {
        target.touch();
        return true;
      }
    });
    view = Utils.findRequiredView(source, R.id.button, "field 'button' and method 'click'");
    target.button = Utils.castView(view, R.id.button, "field 'button'", Button.class);
    view7f080062 = view;
    view.setOnClickListener(new DebouncingOnClickListener() {
      @Override
      public void doClick(View p0) {
        target.click(p0);
      }
    });
    target.imageView = Utils.findRequiredViewAsType(source, R.id.image_view, "field 'imageView'", ImageView.class);
    target.checkBox = Utils.findRequiredViewAsType(source, R.id.checkbox, "field 'checkBox'", CheckBox.class);
    target.progressBar = Utils.findRequiredViewAsType(source, R.id.progressBar, "field 'progressBar'", ProgressBar.class);
  }

  @Override
  @CallSuper
  public void unbind() {
    ButterKnifeActivity target = this.target;
    if (target == null) throw new IllegalStateException("Bindings already cleared.");
    this.target = null;

    target.textView = null;
    target.button = null;
    target.imageView = null;
    target.checkBox = null;
    target.progressBar = null;

    view7f0801ad.setOnTouchListener(null);
    view7f0801ad = null;
    view7f080062.setOnClickListener(null);
    view7f080062 = null;
  }
}

(2)然后通过ButterKnife.bind(this)反射实例化已经生成的对象;

拿到decorView,跟进bind方法,

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

扫描构造方法,然后实例化,跟进findBindingConstructorForClass;

@NonNull @UiThread
  public static Unbinder bind(@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);
    }
  }

findBindingConstructorForClass方法,会排除framework或者java中的对象,然后通过loadClass方法加载自动生成的XXXX_ViewBinding对象,此类一定继承Unbinder,然后返回此构造方法;如果找不到此构造方法,在ClassNotFoundException异常中会去拿超类的构造方法,由此可知butterKnife注入view子类是可继承的;

@Nullable @CheckResult @UiThread
  private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
    Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
    if (bindingCtor != null || BINDINGS.containsKey(cls)) {
      if (debug) Log.d(TAG, "HIT: Cached in binding map.");
      return bindingCtor;
    }
    String clsName = cls.getName();
    if (clsName.startsWith("android.") || clsName.startsWith("java.")
        || clsName.startsWith("androidx.")) {
      if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
      return null;
    }
    try {
      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());
      bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
    } catch (NoSuchMethodException e) {
      throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
    }
    BINDINGS.put(cls, bindingCtor);
    return bindingCtor;
  }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值