Android Butterknife 框架源码解析(1)——ButterKnife的使用

      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("我被点击了一下");
}

 

      就会提示:

Android Butterknife 框架源码解析(1)——ButterKnife的使用_第1张图片

 

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


常见使用:

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

 Android Butterknife 框架源码解析(1)——ButterKnife的使用_第2张图片

  看上半部分可以知道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.
 */
@NonNull
public 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.
 */
@NonNull
public 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

你可能感兴趣的:(Android,Android开源框架源码分析)