最近在一直在看GitHub上看了些代碼,特別關注Jake Wharton神的,所以本文主要是Butter Knife這個框架,為什么選擇它呢?能節省很多代碼量,性能也很好。像findViewById這種代碼就不用再出現了,而且這個框架也提供了很多其他有用的注解,Butter Knife內部原理使用apt在編譯時,就完成代碼生成的類或者接口(還沒有仔細研究源碼),所以我們使用Butter Knife的時候不能private。抱著學習的心態看了官網上的文檔,挺簡單,也很實用,決定以后就用這個庫了。
下面是我翻譯的官方文檔;
Butter Knife
本文章翻譯自:http://jakewharton.github.io/butterknife/
Butter Knife,專門為Android View設計的綁定注解,專業解決各種findViewById,字段和方法綁定。
使用注釋處理為您Android視圖字段和方法綁定生成樣板代碼。
- 通過使用@BindView字段消除調用findViewById方法。
- 在列表或數組中組合多個視圖。一次使用操作,設置器或屬性操作它們。
- 通過使用@OnClick和其他方法注釋方法來消除匿名內部類的偵聽器。
- 通過在字段上使用資源注釋來消除資源查找。
對一個成員變量使用@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;
i f (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之后立即調用它?;蛘撸憧梢栽趏nFinishInflate()
回調中使用它。
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 WhartonLicensed 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
athttp://www.apache.org/licenses/LICENSE-2.0Unless required by
applicable law or agreed to in writing, softwaredistributed 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 andlimitations under the License.