Android Butter Knife 框架——最好用的View注入

最近在看GitHub上的一些代碼時凌箕,發(fā)現(xiàn)很多工程都用到了Butter Knife這個框架拧篮,能節(jié)省很多代碼量。像findViewById這種代碼就不用再出現(xiàn)了牵舱,而且這個框架也提供了很多其他有用的注解他托。
抱著學(xué)習(xí)的心態(tài)看了官網(wǎng)上的文檔,挺簡單仆葡,也很實用赏参,決定以后就用這個庫了。
下面是我翻譯的官方文檔沿盅,諸位看官輕噴把篓。官方文檔也挺簡單,英語好的不好的腰涧,都建議去看看原文韧掩。

另外注意,這個庫的版本更新挺快的窖铡,我第一次用到的時候是7.1.0疗锐,而現(xiàn)在的最新版本已經(jīng)是8.5.1了坊谁,也就是說大家可能需要去ButterKnife的Github查看最近的版本。

image.png

Butter Knife

本文章翻譯自:http://jakewharton.github.io/butterknife/

Butter Knife滑臊,專門為Android View設(shè)計的綁定注解口芍,專業(yè)解決各種findViewById

簡介

對一個成員變量使用@BindView注解雇卷,并傳入一個View ID鬓椭, ButterKnife 就能夠幫你找到對應(yīng)的View,并自動的進(jìn)行轉(zhuǎn)換(將View轉(zhuǎn)換為特定的子類):

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使用再編譯時生成的代碼來執(zhí)行View的查找小染,因此不必?fù)?dān)心注解的性能問題。調(diào)用bind來生成這些代碼贮折,你可以查看或調(diào)試這些代碼裤翩。

例如上面的例子,生成的代碼大致如下所示:

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。使用時對應(yīng)的注解需要傳入對應(yīng)的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);
        }
    }
}

你能在提供給的例子中找到上述實現(xiàn)富蓄。

ButterKnife.bind的調(diào)用可以被放在任何你想調(diào)用findViewById的地方。

提供的其他綁定API:

  • 使用Activity作為跟布局在任意對象中進(jìn)行綁定慢逾。如果你使用了類似MVC的編程模式立倍,你可以對controller使用它的Activity用ButterKnife.bind(this, activity)進(jìn)行綁定。

  • 使用ButterKnife.bind(this)綁定一個布局的子布局侣滩。如果你在布局中使用了<merge>標(biāo)簽并且在自定義的控件構(gòu)造時inflate這個布局口注,你可以在inflate之后立即調(diào)用它【椋或者寝志,你可以在onFinishInflate()回調(diào)中使用它。

View 列表

你可以一次性將多個views綁定到一個List或數(shù)組中:

@BindViews({ R.id.first_name, R.id.middle_name, R.id.last_name })
List<EditText> nameViews;

apply函數(shù),該函數(shù)一次性在列表中的所有View上執(zhí)行一個動作:

ButterKnife.apply(nameViews, DISABLE);
ButterKnife.apply(nameViews, ENABLED, false);

ActionSetter接口能夠讓你指定一些簡單的動作:

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方法進(jìn)行設(shè)置:

ButterKnife.apply(nameViews, View.ALPHA, 0.0f);

監(jiān)聽器綁定

使用本框架材部,監(jiān)聽器能夠自動的綁定到特定的執(zhí)行方法上:

@OnClick(R.id.submit)
public void submit(View view) {
  // TODO submit data to server...
}

而監(jiān)聽器方法的參數(shù)都時可選的:

@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時,綁定事件監(jiān)聽不需要指定ID

public class FancyButton extends Button {
    @OnClick
    public void onClick() {
        // TODO do something!
    }
}

重置綁定:

Fragment的生命周期與Activity不同兽叮。在Fragment中芬骄,如果你在onCreateView中使用綁定猾愿,那么你需要在onDestroyView中設(shè)置所有view為null鹦聪。為此,ButterKnife返回一個Unbinder實例以便于你進(jìn)行這項處理蒂秘。在合適的生命周期回調(diào)中調(diào)用unbind函數(shù)就可完成重置泽本。

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();
    }
}

可選綁定:

在默認(rèn)情況下, @bind和監(jiān)聽器的綁定都是必須的姻僧,如果目標(biāo)view沒有找到的話规丽,Butter Knife將會拋出個異常。

如果你并不想使用這樣的默認(rèn)行為而是想創(chuàng)建一個可選的綁定撇贺,那么你只需要在變量上使用@Nullable注解或在函數(shù)上使用@Option注解赌莺。

注意:任何名為@Nullable的注解都可以使用在變量上。但還時強(qiáng)烈建議使用Android注解庫中的@Nullable松嘶。使用這個庫對你的代碼有很多好處艘狭,關(guān)于該庫的詳情,可以點擊此處:Android Tools Project

@Nullable @BindView(R.id.might_not_be_there) TextView mightNotBeThere;

@Optional @OnClick(R.id.maybe_missing) void onMaybeMissingClicked() {
    // TODO ...
}

對于包含多個方法的監(jiān)聽

當(dāng)一個監(jiān)聽器包含多個回調(diào)函數(shù)時翠订,使用函數(shù)的注解能夠?qū)ζ渲腥魏我粋€函數(shù)進(jìn)行綁定巢音。每一個注解都會綁定到一個默認(rèn)的回調(diào)。你也可以使用callback參數(shù)來指定一個其他函數(shù)作為回調(diào)尽超。

@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官撼,用這個方法可以在ViewActivityDialog中找到想要View似谁,而且傲绣,該方法使用的泛型來對返回值進(jìn)行轉(zhuǎn)換,也就是說巩踏,你可以省去findViewById前面的強(qiáng)制轉(zhuǎn)換了秃诵。

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);

如果你只是使用這個方法,可以使用靜態(tài)引入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.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末顷链,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嗤练,老刑警劉巖榛了,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異煞抬,居然都是意外死亡霜大,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門革答,熙熙樓的掌柜王于貴愁眉苦臉地迎上來战坤,“玉大人,你說我怎么就攤上這事残拐⊥久#” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵溪食,是天一觀的道長囊卜。 經(jīng)常有香客問我,道長错沃,這世上最難降的妖魔是什么栅组? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮枢析,結(jié)果婚禮上玉掸,老公的妹妹穿的比我還像新娘。我一直安慰自己醒叁,他們只是感情好司浪,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著辐益,像睡著了一般断傲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上智政,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天认罩,我揣著相機(jī)與錄音,去河邊找鬼续捂。 笑死垦垂,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的牙瓢。 我是一名探鬼主播劫拗,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼得糜,長吁一口氣:“原來是場噩夢啊……” “哼戚宦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起执庐,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎酒繁,沒想到半個月后滓彰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡州袒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年揭绑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片郎哭。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡他匪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出夸研,到底是詐尸還是另有隱情邦蜜,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布陈惰,位于F島的核電站畦徘,受9級特大地震影響毕籽,放射性物質(zhì)發(fā)生泄漏抬闯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一关筒、第九天 我趴在偏房一處隱蔽的房頂上張望溶握。 院中可真熱鬧,春花似錦蒸播、人聲如沸睡榆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胀屿。三九已至,卻和暖如春包雀,著一層夾襖步出監(jiān)牢的瞬間宿崭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工才写, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留葡兑,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓赞草,卻偏偏與公主長得像讹堤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子厨疙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容