ButterKnife是目前常用的一种依托Java注解机制实现辅助代码生成的框架,有了它,妈妈再也不用担心我写大量枯燥的findViewById以及OnXXXListener响应事件了,一行代码就搞定,自从接触它以后我就再也离不开它了。既然如此,我也就抽个时间,好好研究了一下它,总结一下它的使用方法和原理。

 

配置编译环境

  因为ButterKnife用到了注解处理器,所以比起一般的框架多了一点配置,如下所示:

  在Projectbuild.gradle里添加如下配置(以8.1.0版本为例子,目前最新的可以访问https://github.com/JakeWharton/butterknife获取):

buildscript {    ...    dependencies {                classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'    }}


  在Modulebuild.gradle里添加如下配置:

apply plugin: 'com.neenbedankt.android-apt'android {   ...}dependencies {    compile 'com.jakewharton:butterknife:8.1.0'    apt 'com.jakewharton:butterknife-compiler:8.1.0'}

      Sync一下,我们就可以看到它了 


基本使用

      传统的写法,一个控件相关可以拆分为这样几部分:

1.Button btn;

2.btn = (Button) findViewById(R.id.btn);

3.btn.setOnClickListener(this);

4.

@Override

     public void onClick(View v) {

         switch (v.getId()) {

             case R.id.btn:

                 break;

         }

     }


      比如一个简单的例子就是:

public class MainActivity extends AppCompatActivity {    private Button button;    private TextView textView;    @Override    protected void  onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        button=(Button)findViewById(R.id.button);        textView=(TextView)findViewById(R.id.textView);        button.setOnClickListener(new View.OnClickListener() {            @Override            public voidonClick(View v) {                  textView.setText("我被点击了一下");            }        });    }}

 

  但实际上我们对这个控件关心的只有两个部分:从哪来来,干什么。于是我们的ButterKnife就将传统的写法中12进行合并,34进行合并,用这样的方式来解决这两个问题。于是同样的代码用ButterKnife来写就是这样的:

public class MainActivity extends AppCompatActivity {    @BindView(R.id.button)    Button button;    @BindView(R.id.textView)    TextView textView;        @OnClick(R.id.button)    public void click(){        textView.setText("我被点击了一下");    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_knife );        ButterKnife.bind(this);    }}

  也就是用了@BindView执行了控件的绑定,解决了从哪来的问题,用@OnClick执行了控件监听器的绑定,也就是做什么的问题。先解决这两个问题,最后再通过一句ButteKnife.bind就将控件和Activity关联在一起了。

 

  这里需要注意的是被ButterKnife修饰的方法和控件都不能是私有的和静态的,否则会报错,比如:

@BindView(R.id.button)private Button button;@BindView(R.id.textView)private TextView textView;@OnClick(R.id.button)private void click(){    textView.setText("我被点击了一下");}

 

      就会提示:


 

  有人也许就会问,这样写法并没简单多少。其实ButterKnife主要是将控件相关的逻辑上简化了,代码清晰可读性强,这只个很简单的例子,当控件很多、结构比较复杂的时候(比如ViewHolder里),ButterKnife就能更发挥它的优势。


常见使用:

  我们可以看到ButterKnife是通过注解来控件和监听器的绑定的,所以我们先从ButterKnife里的annotations包里看看ButterKnife里包含了多少种注解:

 

  看上半部分可以知道ButterKnife里除了view控件的绑定,也有一些resource资源的绑定,也就是可以通过@BindXXX的注解实现res文件夹里的资源绑定;下半部分看出ButterKnife将常用的监听事件也封装成了注解。

 

      总结一下将常见使用分为三个部分:

1.控件的绑定

View的绑定,使用方法为:

@BindView(R.id.button)private Button button;@BindView(R.id.textView)private TextView textView;


Resource的绑定,使用方法为:

@BindView(R.id.button)private Button button;@BindView(R.id.textView)private TextView textView;


2.响应事件的绑定

单事件的绑定:

可以一个控件制定一个事件回调

/** * 带参数 */@OnClick(R.id.button)public void onButterKnifeBtnClick() {}/** * 带参数 */@OnClick(R.id.button)public void onButterKnifeBtnClick(View view) {}/** * 带参数 * @parambutton */@OnClick(R.id.button)public void onButterKnifeBtnClick(Button button) {}


也可以多个控件指定一个事件回调:

/** * 两个不同的button都相应onButterKnifeBtnClick事件回调 * * @parambutton */@OnClick({R.id.button,R.id.button1})public void onButterKnifeBtnClick(Button button) {}


多事件的回调:

有一些Viewlistener是有多个回调方法的,比如EditText添加addTextChangedListener

editText.addTextChangedListener(newTextWatcher() {    @Override    public voidbeforeTextChanged(CharSequence s, intstart, intcount, intafter) {    }    @Override    public voidonTextChanged(CharSequence s, intstart, intbefore, intcount) {    }    @Override    public voidafterTextChanged(Editable s) {    }});


可以用注解写为:

@OnTextChanged(value = R.id.nameEditText,callback= OnTextChanged.Callback.BEFORE_TEXT_CHANGED)void beforeTextChanged(CharSequence s, intstart, intcount, intafter) {}@OnTextChanged(value = R.id.nameEditText,callback= OnTextChanged.Callback.TEXT_CHANGED)void onTextChanged(CharSequence s, intstart, intbefore, intcount) {}@OnTextChanged(value = R.id.nameEditText,callback= OnTextChanged.Callback.AFTER_TEXT_CHANGED)void afterTextChanged(Editable s) {}

 

选择性绑定:

  默认情况下,@Bind listener绑定都是必须的,如果target view没有被发现,则会报错.为了抑制这种行为,可以用@Optional注解来标记field和方法,绑定变成选择性的,如果targetView存在,绑定,不存在,则什么事情都不做.或者使用 Android's "support-annotations" library.中的@Nullable来修饰

@Nullable@BindView(R.id.might_not_be_there)TextView mightNotBeThere;@Optional@OnClick(R.id.maybe_missing)void onMaybeMissingClicked() {    //TODO ...}

 

3.View的绑定和解绑

  写好了控件和响应事件绑定,我们需要在布局初始化以后将它和我们的ButterKnifeButterKnife.bind()方法绑定起来。这里我们可以看看ButterKnife.bind()方法的源码为:

/** * BindView annotated fields and methods in the specified {@link Activity}. The current content * view is used as the view root. * * @param target Target activity for view binding. */public static Unbinder bind(@NonNull Activity target) {  return getViewBinder(target).bind(Finder.ACTIVITY, target, target);}/** * BindView annotated fields and methods in the specified {@link View}. The view and its children * are used as the view root. * * @param target Target view for view binding. */@NonNullpublic static Unbinder bind(@NonNull View target) {  return getViewBinder(target).bind(Finder.VIEW, target, target);}/** * BindView annotated fields and methods in the specified {@link Dialog}. The current content * view is used as the view root. * * @param target Target dialog for view binding. */@SuppressWarnings("unused") // Public api.public static Unbinder bind(@NonNull Dialog target) {  return getViewBinder(target).bind(Finder.DIALOG, target, target);}/** * BindView annotated fields and methods in the specified {@code target} using the {@code source} * {@link Activity} as the view root. * * @param target Target class for view binding. * @param source Activity on which IDs will be looked up. */public static Unbinder bind(@NonNull Object target, @NonNull Activity source) {  return getViewBinder(target).bind(Finder.ACTIVITY, target, source);}/** * BindView annotated fields and methods in the specified {@code target} using the {@code source} * {@link View} as the view root. * * @param target Target class for view binding. * @param source View root on which IDs will be looked up. */@NonNullpublic static Unbinder bind(@NonNull Object target, @NonNull View source) {  return getViewBinder(target).bind(Finder.VIEW, target, source);}/** * BindView annotated fields and methods in the specified {@code target} using the {@code source} * {@link Dialog} as the view root. * * @param target Target class for view binding. * @param source Dialog on which IDs will be looked up. */@SuppressWarnings("unused") // Public api.public static Unbinder bind(@NonNull Object target, @NonNull Dialog source) {  return getViewBinder(target).bind(Finder.DIALOG, target, source);}


  可以看出源码中能和我们布局绑定的来源一共有ActivityViewDialog三种,值得注意的是,在这些来源生命周期结束时候需要执行ButterKnife.unbind()方法来将绑定的变量变为null,从而释放内存。

 

以上就是ButterKnife的基本用法。


参考文章:

http://www.cnblogs.com/whoislcj/p/5620128.html

http://www.jianshu.com/p/9ad21e548b69

https://www.zhihu.com/question/38742543?sort=created

更多相关文章

  1. 边做iOS边学Android(二):认识几个常用的控件
  2. 一起来学习Android自定义控件1
  3. Android中将控件放到线性布局的任意位置(一)
  4. android 绑定sqlite数据库与程序一起发布
  5. Gallery控件初体验——简单的相册
  6. Android(安卓)自定义标尺控件(选择身高、体重等)
  7. Android(安卓)自定义控件——自定义属性
  8. 荐 android 如何打包自定义控件(转)
  9. Android性能优化之路(二)

随机推荐

  1. Android中设置控件可见与不可见详解
  2. android app 启动会白屏的解决办法
  3. unity与Android相互调用
  4. 【Android】TextView常用属性
  5. Android(安卓)圆角图片,基于Glide4.9 的 B
  6. Android(安卓)从零开始打造异步处理框架
  7. Android之TableLayout布局
  8. Android中attrs.xml文件的使用详解
  9. Android截屏浅析
  10. Mac下发布Unity3d中Android平台下出现“a