最近在看GitHub上的一些代碼時,發現很多工程都用到了Butter Knife這個框架,能節省很多代碼量。像findViewById
這種代碼就不用再出現了,而且這個框架也提供了很多其他有用的注解。
抱著學習的心態看了官網上的文檔,挺簡單,也很實用,決定以后就用這個庫了。
下面是我翻譯的官方文檔,諸位看官輕噴。官方文檔也挺簡單,英語好的不好的,都建議去看看原文。
另外注意,這個庫的版本更新挺快的,我第一次用到的時候是7.1.0,而現在的最新版本已經是8.5.1了,也就是說大家可能需要去ButterKnife的Github查看最近的版本。
Butter Knife
本文章翻譯自:http://jakewharton.github.io/butterknife/
Butter Knife,專門為Android View設計的綁定注解,專業解決各種findViewById
。
簡介
對一個成員變量使用@BindView
注解,并傳入一個View ID, ButterKnife 就能夠幫你找到對應的View,并自動的進行轉換(將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
來生成這些代碼,你可以查看或調試這些代碼。
例如上面的例子,生成的代碼大致如下所示:
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);
}
資源綁定
綁定資源到類成員上可以使用@BindBool
、@BindColor
、@BindDimen
、@BindDrawable
、@BindInt
、@BindString
。使用時對應的注解需要傳入對應的id資源,例如@BindString
你需要傳入R.string.id_string
的字符串的資源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
// ...
}
在非Activity中使用綁定
Butter Knife提供了bind的幾個重載,只要傳入跟布局,便可以在任何對象中使用注解綁定。
例如在Fragment中:
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;
}
}
還有一種比較常見的場景,就是在ListView的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);
}
}
}
你能在提供給的例子中找到上述實現。
ButterKnife.bind
的調用可以被放在任何你想調用findViewById
的地方。
提供的其他綁定API:
使用Activity作為跟布局在任意對象中進行綁定。如果你使用了類似MVC的編程模式,你可以對controller使用它的Activity用
ButterKnife.bind(this, activity)
進行綁定。使用
ButterKnife.bind(this)
綁定一個布局的子布局。如果你在布局中使用了<merge>
標簽并且在自定義的控件構造時inflate這個布局,你可以在inflate之后立即調用它。或者,你可以在onFinishInflate()
回調中使用它。
View 列表
你可以一次性將多個views綁定到一個List
或數組中:
@BindViews({ R.id.first_name, R.id.middle_name, R.id.last_name })
List<EditText> nameViews;
apply
函數,該函數一次性在列表中的所有View上執行一個動作:
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);
}
};
Android中的Property
屬性也可以使用apply
方法進行設置:
ButterKnife.apply(nameViews, View.ALPHA, 0.0f);
監聽器綁定
使用本框架,監聽器能夠自動的綁定到特定的執行方法上:
@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...
}
指定一個特定的類型,Butter Knife也能識別:
@OnClick(R.id.submit)
public void sayHi(Button button) {
button.setText("Hello!");
}
可以指定多個View ID到一個方法上,這樣,這個方法就成為了這些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時,綁定事件監聽不需要指定ID
public class FancyButton extends Button {
@OnClick
public void onClick() {
// TODO do something!
}
}
重置綁定:
Fragment的生命周期與Activity不同。在Fragment中,如果你在onCreateView
中使用綁定,那么你需要在onDestroyView
中設置所有view為null
。為此,ButterKnife返回一個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();
}
}
可選綁定:
在默認情況下, @bind
和監聽器的綁定都是必須的,如果目標view沒有找到的話,Butter Knife將會拋出個異常。
如果你并不想使用這樣的默認行為而是想創建一個可選的綁定,那么你只需要在變量上使用@Nullable
注解或在函數上使用@Option
注解。
注意:任何名為@Nullable
的注解都可以使用在變量上。但還時強烈建議使用Android注解庫中的@Nullable
。使用這個庫對你的代碼有很多好處,關于該庫的詳情,可以點擊此處:Android Tools Project
@Nullable @BindView(R.id.might_not_be_there) TextView mightNotBeThere;
@Optional @OnClick(R.id.maybe_missing) void onMaybeMissingClicked() {
// TODO ...
}
對于包含多個方法的監聽
當一個監聽器包含多個回調函數時,使用函數的注解能夠對其中任何一個函數進行綁定。每一個注解都會綁定到一個默認的回調。你也可以使用callback
參數來指定一個其他函數作為回調。
@OnItemSelected(R.id.list_view)
void onItemSelected(int position) {
// TODO ...
}
@OnItemSelected(value = R.id.maybe_missing, callback = NOTHING_SELECTED)
void onNothingSelected() {
// TODO ...
}
福利
Butter Knife提供了一個findViewById
的簡化代碼:findById
,用這個方法可以在View
、Activity
和Dialog
中找到想要View,而且,該方法使用的泛型來對返回值進行轉換,也就是說,你可以省去findViewById
前面的強制轉換了。
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
方法。
下載
dependencies {
compile 'com.jakewharton:butterknife:8.5.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
}
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.