使用ButterKnife可以直接对View进行绑定,简化我们项目中的代码量.
参考:http://jakewharton.github.io/butterknife/
@BindView的使用View的绑定
@BindView
是一个属性注解,作用是将View和它的id进行绑定
class ExampleActivity extends Activity {
@BindView(R.id.title) TextView title;
@BindView(R.id.subtitle) TextView subtitle;
@BindView(R.id.footer) TextView footer;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this);
// TODO Use fields...
}
}
ButterKnife使用编译时注解进行处理,不会造成运行时的性能损耗,在编译以后会在ButterKnife.bind(this);
后生成如下代码
public void bind(ExampleActivity activity) {
activity.subtitle = (android.widget.TextView) activity.findViewById(2130968578);
activity.footer = (android.widget.TextView) activity.findViewById(2130968579);
activity.title = (android.widget.TextView) activity.findViewById(2130968577);
}
这个就是ButterKnife的实现原理.
资源的绑定
使用@BindBool, @BindColor, @BindDimen, @BindDrawable, @BindInt, @BindString
之类的字段注解,可以将定义的资源值进行绑定.
@BindString(R.string.title) String title;
@BindDrawable(R.drawable.graphic) Drawable graphic;
@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
// ...
}
非Activity情况下进行绑定
在ButterKnife中,可以对任意对象进行bind,只要这个对象持有view的根布局,如Fragement.
public class FancyFragment extends Fragment {
@BindView(R.id.button1) Button button1;
@BindView(R.id.button2) Button button2;
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fancy_fragment, container, false);
// 此处将根布局的View传入
ButterKnife.bind(this, view);
// TODO Use fields...
return view;
}
}
在ListView或者RecyclerView的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) {
// 将需要bind的根布局进行传入
ButterKnife.bind(this, view);
}
}
}
在官方的示例中还有很多其他的Demo, 事实上我们可以在任何需要使用findViewById()
的地方替换成bind()
, 然后使用@BindView()进行编译时替换.
其他可能出现的场景
ButterKnife允许在任何对象中进行bind()代码, 比如在MVC架构中, 你需要将Activity作为参数传入.
直接调用ButterKnife.bind(Object target, Activity source)
方法.ButterKnife允许对View的子View进行bind(),使用
ButterKnife.bind(View target)
即可, 如果在你的自定义View的XML中引入了,<merge>
标签,你可以在自定义View的构造器调用时进行bind(),或者在onFinishInflate()
回调中进行.
使用@BindViews({})绑定多个View
当使用List之类的容器持有View时,可以同时使用@BindViews({})
绑定多个View
@BindViews({ R.id.first_name, R.id.middle_name, R.id.last_name })
List<EditText> nameViews;
然后可以使用ButterKnife.apply()对这个List中的View进行统一操作
// 对所有View进行统一操作
ButterKnife.apply(mViewList, new ButterKnife.Action<View>() {
@Override
public void apply(View view, int index) {
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "onClick: ");
}
});
}
});
// 对所有View进行统一操作,此处和Action的区别是可以给view设置参数
ButterKnife.apply(mViewList, new ButterKnife.Setter<View, Boolean>() {
@Override
public void set(View view, Boolean value, int index) {
view.setEnabled(value);
}
}, false);
使用apply()也可以直接设置View的属性
ButterKnife.apply(nameViews, View.ALPHA, 0.0f);
View监听器的绑定
使用’@OnClick()’之类的注解可以对View绑定监听器
@OnClick(R.id.submit)
public void submit() {
// TODO submit data to server...
}
将类型信息传入,ButterKnife会自动将类型进行强转
@OnClick(R.id.submit)
// Butterknife将自动强转成Button类型
public void sayHi(Button button) {
button.setText("Hello!");
}
同样可以对多个View同时绑定监听器
@OnClick({ R.id.door1, R.id.door2, R.id.door3 })
public void pickDoor(DoorView door) {
if (door.hasPrizeBehind()) {
Toast.makeText(this, "You win!", LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Try again", LENGTH_SHORT).show();
}
}
在我们的自定义view中同样可以使用@OnClick()
进行绑定
public class FancyButton extends Button {
@OnClick
public void onClick() {
// TODO do something!
}
}
在不同的生命周期内对View进行解绑
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对象
unbinder = ButterKnife.bind(this, view);
// TODO Use fields...
return view;
}
// 在对应的生命周期回调进行解绑
@Override public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}
@BindView与其他注解配合使用
@BindView 时如果没有找到对应的View,这时可能会产生运行时异常,解决这个问题的办法可以在@BindView前加上@Nullable,
这样编译器会帮我们进行静态代码检查时会发现这些问题.
@Nullable @BindView(R.id.might_not_be_there) TextView mightNotBeThere;
@Optional @OnClick(R.id.maybe_missing) void onMaybeMissingClicked() {
// TODO ...
}
针对AdapterView中的监听器设置
@OnItemSelected(R.id.list_view)
void onItemSelected(int position) {
// TODO ...
}
@OnItemSelected(value = R.id.maybe_missing, callback = NOTHING_SELECTED)
void onNothingSelected() {
// TODO ...
}
使用ButterKnife直接进行绑定,可以避免强制类型转换
View view = LayoutInflater.from(context).inflate(R.layout.thing, null);
TextView firstName = ButterKnife.findById(view, R.id.first_name);
TextView lastName = ButterKnife.findById(view, R.id.last_name);
ImageView photo = ButterKnife.findById(view, R.id.photo);