ButterKnife是目前常用的一种依托Java注解机制实现辅助代码生成的框架,有了它,妈妈再也不用担心我写大量枯燥的findViewById以及OnXXXListener响应事件了,一行代码就搞定,自从接触它以后我就再也离不开它了。既然如此,我也就抽个时间,好好研究了一下它,总结一下它的使用方法和原理。
因为ButterKnife用到了注解处理器,所以比起一般的框架多了一点配置,如下所示:
在Project的build.gradle里添加如下配置(以8.1.0版本为例子,目前最新的可以访问https://github.com/JakeWharton/butterknife获取):
buildscript {
...
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
在Module的build.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就将传统的写法中1和2进行合并,3和4进行合并,用这样的方式来解决这两个问题。于是同样的代码用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) {
}
多事件的回调:
有一些View的listener是有多个回调方法的,比如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的绑定和解绑
写好了控件和响应事件绑定,我们需要在布局初始化以后将它和我们的ButterKnife用ButterKnife.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);
}
可以看出源码中能和我们布局绑定的来源一共有Activity,View和Dialog三种,值得注意的是,在这些来源生命周期结束时候需要执行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