1. 程式人生 > >Android Butter Knife使用詳解

Android Butter Knife使用詳解

1. 簡介

Butter Knife是一種View註解框架,可以大量減少模板類程式碼的書寫,極大簡化了類似findViewById 和OnClickedListener等View相關的程式碼。Butter Knife使用的java annotation Process技術實現,即在程式碼編譯階段就已經解析了註解內容並生成了模板程式碼,不會影響程式碼執行階段的效能。
Butter Knife最新版本為8.x, 官方程式碼託管在gitHub上,程式碼地址如下:

https://github.com/JakeWharton/butterknife

該庫的License是Apache License 2.0

2. 使用場景程式碼示例

對一個成員變數使用@BindView註解,並傳入一個View ID, ButterKnife 就能夠幫你找到對應的View, 並自動的進行轉換, 與緩慢的反射相比,Butter Knife 使用再編譯時生成的程式碼來執行View的查詢, 因此不必擔心註解的效能問題。呼叫bind來生成這些程式碼,你可以檢視或除錯這些程式碼。

要使用Butter Knife首先需要在 app Module下的build.gradle下引入相關的依賴庫

# 註解庫
implementation 'com.jakewharton:butterknife:8.8.1'
#編譯庫
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

2.1 Activity下使用Butter Knife

class ExampleActivity extends Activity {
    @BindView(R.id.title)  TextView title;
    @BindView(R.id.subtitle) TextView subtitle;
    @BindView(R.id.footer) TextView footer;

    //繫結多個View
    @BindViews({R.id.link_enterprise_text, R.id.link_enterprise_table,R.id.enterprise_sign,
            R.id.enterprise_manager})
    List<TextView> enterpriseInfoList;

    @Override public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.simple_activity);
        ButterKnife.bind(this);
        // TODO Use fields...
    }
}

2.2 資源類繫結

繫結資源到類成員上可以使用@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
  // ...
}

2.3 非Activity場景——Fragment中繫結

Butter Knife提供了bind的幾個過載,只要傳入跟佈局,便可以在任何物件中使用註解繫結。

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

2.4 非Activity場景——Adapter中繫結

在ListView的Adapter中,我們常常會使用ViewHolder, 在該類下需要繫結相關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);
        }
    }
}

ButterKnife.bind的呼叫可以被放在任何你想呼叫findViewById的地方。

提供的其他繫結API:

  • 使用Activity作為跟佈局在任意物件中進行繫結。如果你使用了類似MVC的程式設計模式,你可以對controller使用它的Activity用ButterKnife.bind(this, activity)進行繫結。

  • 使用ButterKnife.bind(this)繫結一個佈局的子佈局。如果你在佈局中使用了標籤並且在自定義的控制元件構造時inflate這個佈局,你可以在inflate之後立即呼叫它。或者,你可以在onFinishInflate()回撥中使用它。

2.5 其他特性

1. 繫結View列表

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

2. 對View應用動作

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

3. 對View應用屬性
Android中的Property屬性也可以使用apply方法進行設定:

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

2.6 監聽器繫結

使用本框架,監聽器能夠自動的繫結到特定的執行方法上:

@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!
    }
}

2.7 重置繫結

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

2.8 可選繫結

可選繫結:
在預設情況下, @bind和監聽器的繫結都是必須的,如果目標view沒有找到的話,Butter Knife將會丟擲個異常。

如果你並不想使用這樣的預設行為而是想建立一個可選的繫結,那麼你只需要在變數上使用@Nullable註解或在函式上使用@Option註解。

注意:任何名為@Nullable的註解都可以使用在變數上。但還時強烈建議使用Android註解庫中的@Nullable。

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

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

2.9 對包含多個監聽方法的繫結——ListView OnItemSelectedListener

@OnItemSelected(R.id.list_view)
void onItemSelected(int position) {
    // TODO ...
}

@OnItemSelected(value = R.id.maybe_missing, callback = NOTHING_SELECTED)
void onNothingSelected() {
    // TODO ...
}

2.10 Butter Knife簡單findById方法

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

3. Android Studio的外掛——Android ButterKnife Zelezny

image

配合ButterKnife實現註解,從此不用寫findViewById,想著就爽啊。在Activity,Fragment,Adapter中選中佈局xml的資源id自動生成butterknife註解。

使用方法:Ctrl+Shift+B選擇圖上所示選項.