1. 摘要
JakeWharton/butterknife[1]是一个支持Android开发通过注解来减少代码的一个开源库。本文翻译了博客《Butter Knife Field and method binding for Android views》[2]。但该链接需要翻墙才能阅读。翻译该博客以便于后来者学习和使用butterknife。
2. 正文
2.1 介绍
Butter Knife通过@BindView的field注解和view id来绑定在layout中对应的view。
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...
}
}
Butter Knife并没有使用相对较慢的反射机制,而是生成了查找对应view的代码。通过bind的方式,可以生成对应代码,你也可以调试看看。
上面的ExampleActivity给出的事例代码,大致会生成这样的代码:
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);
}
2.2 资源绑定
可以通过@BindBool, @BindColor, @BindDimen, @BindDrawable, @BindInt, @BindString来绑定R.bool id(或者特定类型)。class ExampleActivity extends Activity {
@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
// ...
}
2.3 非Activity绑定
只要提供了root view,可以绑定任意对象。
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);
ButterKnife.bind(this, view);
// TODO Use fields...
return view;
}
}
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);
}
}
}
你可以在提供的sample的action中看到它的实现。
ButterKnife.bind可以在任何地方使用,用来代替findViewById。
其它已提供的binding API为以下两类:
<1> bind任何使用activity作为root view的对象,可以通过调用BufferKnife.bind(this, activity)。如果你使用MVC模式,可以通过这种方式,来绑定其对应的Controller。
<2> bind某个view的child view某些field,可以通过调用ButterKnife.bind(this)。如果你在某个layout中使用<merge>标签,同时inflate一个自定义view容器,你可以在inflate之后,直接调用它。或者,可以在onFinishInflate()回调中,调用它。(译者注:view的onFinishInflate()会在所有child views从XML加载完成后被调用)
2.4 View列表
你可以把多个view放入一个List或数组中。
@BindViews({ R.id.first_name, R.id.middle_name, R.id.last_name })
List<EditText> nameViews;
apply方法让你可以让你马上把views放入一个list中。
ButterKnife.apply(nameViews, DISABLE);
ButterKnife.apply(nameViews, ENABLED, false);
Action和Setter接口让你指定一些简单行为。
static final ButterKnife.Action<View> DISABLE = new ButterKnife.Action<View>() {
@Override public void apply(View view, int index) {
view.setEnabled(false);
}
};
static final ButterKnife.Setter<View, Boolean> ENABLED = new ButterKnife.Setter<View, Boolean>() {
@Override public void set(View view, Boolean value, int index) {
view.setEnabled(value);
}
};
ButterKnife.apply(nameViews, View.ALPHA, 0.0f);
2.5 监听器绑定
监听器可以自动配置到方法上。@OnClick(R.id.submit)
public void submit(View view) {
// TODO submit data to server...
}
@OnClick(R.id.submit)
public void submit() {
// TODO submit data to server...
}
@OnClick(R.id.submit)
public void sayHi(Button button) {
button.setText("Hello!");
}
可以给一个监听器绑定多个id。
@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也可以不指定id,并且绑定到它们自己到监听器。
public class FancyButton extends Button {
@OnClick
public void onClick() {
// TODO do something!
}
}
2.6 绑定重置
Fragment的生命周期与Activity不同。当在onCreateView回调方法中,绑定一个Fragment时,需要在onDestroyView回调方法中,设置view为null。当你在进行bind的时候,Butter Knife会返回一个Unbinder引用。请在适当的生命周期回调方法中,调用Unbinder的unbind方法。
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();
}
}
2.7 可选绑定(OPTIONAL BINDINGS)
默认情况下,@Bind和监听器绑定是必要的。如果对象view无法被找到,会抛出一个异常。
为了避免这样的情况,可以添加一个可选绑定,增加一个@Nullable注解给field,或者增加一个@Optional注解给method。
注意:@Nullable注解可以被用在任何field。应该尽量使用来自Android's "support-annotations"库中的@Nullable。
@Nullable @BindView(R.id.might_not_be_there) TextView mightNotBeThere;
@Optional @OnClick(R.id.maybe_missing) void onMaybeMissingClicked() {
// TODO ...
}
2.8 多方法监听器
如果某个方法注解对应的监听器有多个回调,那么方法注解可以和它们中的任何一个绑定。每个注解都有它默认的callback。也可以通过callback参数来设置。
@OnItemSelected(R.id.list_view)
void onItemSelected(int position) {
// TODO ...
}
@OnItemSelected(value = R.id.maybe_missing, callback = NOTHING_SELECTED)
void onNothingSelected() {
// TODO ...
}
2.9 BONUS
另外,还提供了findById方法,用来简化代码,关于查找某个view的child view、activity的child view或者dialog的child view的。它通过类的推断来自动执行类型转换。
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);
为ButterKnife.findById添加一个静态的import就可以使用它了。
2.10 下载
GRADLEcompile 'com.jakewharton:butterknife:8.5.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
2.11 License
Copyright 2013 Jake Wharton
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
3. 引用
[2] 《Butter Knife Field and method binding for Android views》