这段时间学习有所懈怠,没有坚持写博文,后续要自我约束继续加油。也警告自己不要陷在业务代码开发的泥淖中,一定要抽空学习总结。
言归正传,ButterKnife的源码地址是:https://github.com/JakeWharton/butterknife。该库使用注解来实现Field和Method的绑定。使用中遇到问题建议优先其次都必须参考官方文档哦!
优点如下:
- Eliminate findViewById calls by using @BindView on fields.
- Group multiple views in a list or array. Operate on all of them at once with actions, setters, or properties.
- Eliminate anonymous inner-classes for listeners by annotating methods with @OnClick and others.
- Eliminate resource lookups by using resource annotations on fields.
简单的使用代码如下:
class ExampleActivity extends Activity {
@BindView(R.id.user) EditText username;
@BindView(R.id.pass) EditText password;
@BindString(R.string.login_error) String loginErrorMessage;
@OnClick(R.id.submit) void submit() {
// TODO call server...
}
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this);
// TODO Use fields...
}
}
Bind使用场景
Activity
具体见上面的示例代码。ButterKnife.bind(activity)
Fragment
public class FancyFragment extends Fragment {
@BindView(R.id.button1) Button button1;
@BindView(R.id.button2) Button button2;
private Unbinder unbinder;
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fancy_fragment, container, false);
unbinder = ButterKnife.bind(this, view);
// TODO Use fields...
return view;
}
@Override public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}
注意,使用ButterKnife.bind(fragment, view)。根据文档,由于Fragment的特殊性,在onCreateView进行bind时,需要在onDestroyView中进行解绑。
Adapter的ViewHolder使用
public class MyAdapter extends BaseAdapter {
@Override public View getView(int position, View view, ViewGroup parent) {
ViewHolder holder;
if (view != null) {
holder = (ViewHolder) view.getTag();
} else {
view = inflater.inflate(R.layout.whatever, parent, false);
holder = new ViewHolder(view);
view.setTag(holder);
}
holder.name.setText("John Doe");
// etc...
return view;
}
static class ViewHolder {
@BindView(R.id.title) TextView name;
@BindView(R.id.job_title) TextView jobTitle;
public ViewHolder(View view) {
ButterKnife.bind(this, view);
}
}
}
还有一个使用的细节觉得蛮有意思,默认情况下,使用@Bind和监听器时,如果目标View不存在就会抛出异常。为了避免这种行为,可以用@Nullable注解(Field)和@Optional注解(methods)。
具体的使用文档参考:http://jakewharton.github.io/butterknife/。
App模块编译配置
在app模块的build.gradle中,添加如下依赖。
dependencies {
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
}
注意:If you are using Kotlin, replace annotationProcessor with kapt.
Library模块编译配置
随着代码复杂度的增加,多组件随之出现,这也给ButterKnife的使用带来了麻烦。参考官方文档,步骤如下:
一、在lib模块的buildscript增加如下代码
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.jakewharton:butterknife-gradle-plugin:8.8.1'
}
}
二、在lib模块中使用
apply plugin: 'com.android.library'
apply plugin: 'com.jakewharton.butterknife'
三、通过R2而不是R来引用
class ExampleActivity extends Activity {
@BindView(R2.id.user) EditText username;
@BindView(R2.id.pass) EditText password;
...
}
填坑记
在多个Lib中使用R2时,Android Studio自动添加在引用始终是第一个添加的module(也可能是最底层编译依赖的模块,未测试),此时可通过直接添加import 模块名.R2来解决。傻傻的我非得用快捷键自动添加引用,浪费自己的时间哦。
注意switch-case语句的使用,在library中不能使用资源id作为case值,否则会出现Validates using resource IDs in a switch statement in Android library module异常。解决方法就是用if-else代替,并注意在代码中使用R来引用资源id。
@OnClick({R2.id.textView, R2.id.button1, R2.id.button2, R2.id.button3, R2.id.image})
public void onViewClicked(View view) {
int i = view.getId();
if (i == R.id.textView) {
} else if (i == R.id.button1) {
} else if (i == R.id.button2) {
} else if (i == R.id.button3) {
} else if (i == R.id.image) {
}
}
在使用中写出了一段代码,将一个Activity中的两个动态View分别调用bin d(this,view),但是其中一个view在运行时绑定的对象示例为null。可参考这个文章,通过ViewHolder来解决。https://segmentfault.com/q/1010000009989285。
参考文章:
Butter Knife —— 最好用的View注入
多个library使用butterknife的坑
Validates using resource IDs in a switch statement in Android library module
butterknife组件化开发library中R类问题的批量解决方案
解决组件化开发butterknife 在 library中使用的坑