1. 程式人生 > >Android框架式編程之BufferKnife

Android框架式編程之BufferKnife

there binder them fine eve 簡化 include nds support

Android 開發過程中,我們需要寫大量的findViewById()和setonclicktListener()等事件, 那麽現在有一個框架可以很好的解決這個問題 ---- BufferKnife。BufferKnife可以大量精簡代碼,而且不影響性能,可以通過查看Butter Knife了解到,其自定義註解的實現都是限定為RetentionPolicy.CLASS,也就是到編譯出.class文件為止有效,在運行時不會額外消耗性能。

下面是說明一下如何使用Butter Knife :

基本的使用方法如下:

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); // 必須在設置好布局事件後綁定當前的Activity // TODO Use fields... } }

上面的慢反射代碼執行下來生成的class代碼如下:

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

另外Butter Knife 也可以用於資源綁定:

可以使用@BindBool, @BindColor, @BindDimen, @BindDrawable

, @BindInt, @BindString來預綁定一些資源到對應的字段。

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的類如何綁定呢?其實只需要比Activity綁定時多傳遞一個參數即可,即根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;
  }
}

Butter Knife 還有另一個用途就是簡化List的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);
    }
  }
}

基本上你想調用findViewById的方法的地方應該都是可以設置ButterKnife.bind的。


使用ButterKnife.bind(這)將視圖的孩子綁定到字段中。 如果您在布局中使用<merge>標簽,並在自定義視圖構造函數中展開,則可以立即調用它。 或者,從XML擴展的自定義視圖類型可以在onFinishInflate()回調中使用它。

提供的其他的綁定的APIs:

  • 使用Activity作為視圖根來綁定任意對象。如果你使用類似MVC 的模式,你可以使用ButterKnife.bind(this, activity)來綁定控制器。

ButterKnife還可以處理View Lists:

你可以將多個view分組到列表或者數組:

View Lists

You can group multiple views into a List or array.

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

The apply method allows you to act on all the views in a list at once.

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

Action and Setter interfaces allow specifying simple behavior.

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

An Android Property can also be used with the apply method.

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

Listener Binding

Listeners can also automatically be configured onto methods.

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

All arguments to the listener method are optional.

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

Define a specific type and it will automatically be cast.

@OnClick(R.id.submit)
public void sayHi(Button button) {
  button.setText("Hello!");
}

Specify multiple IDs in a single binding for common event handling.

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

Custom views can bind to their own listeners by not specifying an ID.

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

Binding Reset

Fragments have a different view lifecycle than activities. When binding a fragment in onCreateView, set the views to null in onDestroyView. Butter Knife returns an Unbinderinstance when you call bind to do this for you. Call its unbind method in the appropriate lifecycle callback.

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

Optional Bindings

By default, both @Bind and listener bindings are required. An exception will be thrown if the target view cannot be found.

To suppress this behavior and create an optional binding, add a @Nullable annotation to fields or the @Optional annotation to methods.

Note: Any annotation named @Nullable can be used for fields. It is encouraged to use the @Nullable annotation from Android‘s "support-annotations" library.

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

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

Multi-Method Listeners

Method annotations whose corresponding listener has multiple callbacks can be used to bind to any one of them. Each annotation has a default callback that it binds to. Specify an alternate using the callback parameter.

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

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

Bonus

Also included are findById methods which simplify code that still has to find views on a View, Activity, or Dialog. It uses generics to infer the return type and automatically performs the cast.

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

Add a static import for ButterKnife.findById and enjoy even more fun.

Download

Gradle

compile ‘com.jakewharton:butterknife:(insert latest version)
annotationProcessor ‘com.jakewharton:butterknife-compiler:(insert latest version)

Android框架式編程之BufferKnife