LoginSignup
24
25

More than 5 years have passed since last update.

[翻訳] Butter Knife (version7.0.1)

Posted at

Butter Knife を翻訳してみました。 
いまさらな感じですが、全部入りが欲しかったので。

(おおむね問題ないと思いますが)一部不明な点もありましたので、
もしおかしな点がありましたらコメント頂ければと思います。

あと、ButterKnifeZeleznyにもリンクしておきます。

Introduction

Annotate fields with @Bind and a view ID for Butter Knife to find and automatically cast the corresponding view in your layout.

class ExampleActivity extends Activity {
  @Bind(R.id.title) TextView title;
  @Bind(R.id.subtitle) TextView subtitle;
  @Bind(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...
  }
}

Instead of slow reflection, code is generated to perform the view look-ups. Calling bind delegates to this generated code that you can see and debug.
The generated code for the above example is roughly equivalent to the following:

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用に@Bindとview IDでアノテートします。

class ExampleActivity extends Activity {
  @Bind(R.id.title) TextView title;
  @Bind(R.id.subtitle) TextView subtitle;
  @Bind(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...
  }
}

時間のかかるリフレクションではなく、ビューの検索を行うコードが生成されます。 ButterKnife#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);
}

RESOURCE BINDING

Bind pre-defined resources with @BindBool, @BindColor, @BindDimen, @BindDrawable, @BindInt, @BindString, which binds an R.bool ID (or your specified type) to its corresponding field.

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
  // ...
}

R.bool ID(または、あなたが指定した型)をバインドしてくれる@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
  // ...
}

NON-ACTIVITY BINDING

You can also perform binding on arbitrary objects by supplying your own view root.

public class FancyFragment extends Fragment {
  @Bind(R.id.button1) Button button1;
  @Bind(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;
  }
}

Another use is simplifying the view holder pattern inside of a list adapter.

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 {
    @Bind(R.id.title) TextView name;
    @Bind(R.id.job_title) TextView jobTitle;

    public ViewHolder(View view) {
      ButterKnife.bind(this, view);
    }
  }
}

You can see this implementation in action in the provided sample.
Calls to ButterKnife.bind can be made anywhere you would otherwise put findViewById calls.

Other provided binding APIs:
 ・ Bind arbitrary objects using an activity as the view root. If you use a pattern like MVC you can bind the controller using its activity with ButterKnife.bind(this, activity).
 ・ Bind a view's children into fields using ButterKnife.bind(this). If you use <merge> tags in a layout and inflate in a custom view constructor you can call this immediately after. Alternatively, custom view types inflated from XML can use it in the onFinishInflate() callback.

またビュールートを渡せば、任意のオブジェクトでバインドすることも出来ます。

public class FancyFragment extends Fragment {
  @Bind(R.id.button1) Button button1;
  @Bind(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;
  }
}

他の使い方として、リストアダプター内のビューホルダーパターンの簡素化もあります。

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 {
    @Bind(R.id.title) TextView name;
    @Bind(R.id.job_title) TextView jobTitle;

    public ViewHolder(View view) {
      ButterKnife.bind(this, view);
    }
  }
}

提供されているサンプルの動きでこの実装を確かめることができます。
ButterKnife.bindの呼出は、findViewByIdを呼ぶであろう場所ならどこでも行えます。

その他のAPI:
 ・ activityをビュールートとして使用した任意のオブジェクトとのバインド。 もしMVCのようなパターンを使用するなら、そのactivityとButterKnife.bind(this, activity)を使用してコントローラーとバインドできます。
 ・ ビューの子要素とフィールドをButterKnife.bind(this)でバインド。 もしレイアウト内で<merge>タグを使用し、カスタムビューのコンストラクタでインフレートするなら、直後にこれを呼び出すことができます。 また、XMLからインフレートされたカスタムビューはonFinishInflate()コールバックでこれを使用できます。

VIEW LISTS

You can group multiple views into a List or array.

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

複数のビューをListまたは配列にまとめることができます。

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

applyメソッドでリスト内の全ビューを一括で操作できます。

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のPropertyapplyメソッドで使用できます。

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

リスナーも自動でメソッドにバインドできます。

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

具体的な型を明記すると、自動でキャストされます。

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

共通のイベント処理向けに、ひとつのバインドに複数IDを指定できます。

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

カスタムビューでは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 has an unbind method to do this automatically.

public class FancyFragment extends Fragment {
  @Bind(R.id.button1) Button button1;
  @Bind(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;
  }

  @Override public void onDestroyView() {
    super.onDestroyView();
    ButterKnife.unbind(this);
  }
}

フラグメントは、activityとは異なるビューのライフサイクルを持っています。 onCreateViewでフラグメントにバインドしたら、onDestroyViewでビューにnullをセットします。 Butter Knifeにはこれを自動で行うunbindメソッドがあります。

public class FancyFragment extends Fragment {
  @Bind(R.id.button1) Button button1;
  @Bind(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;
  }

  @Override public void onDestroyView() {
    super.onDestroyView();
    ButterKnife.unbind(this);
  }
}

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 the field or method.
Note: Any annotation named @Nullable can be used for this purpose. It is encouraged to use the @Nullable annotation from Android's "support-annotations" library, see Android Tools Project.

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

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

通常、@Bindとリスナーバインドの両方とも対象のビューが必要です。 対象のビューが見つからないと例外が発生します。 例外を抑止し、必須ではないバインドを作成するには、フィールドまたはメソッドに@Nullableアノテーションを追加します。

注意:@Nullableが指定されたどのアノテーションも、この用途で使用できます。 アンドロイドの"support-annotations"ライブラリの@Nullableアノテーションの使用が推奨されています。 Android Tools Projectを参照してください。

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

@Nullable @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 ...
}

リスナー用のメソッドのアノテーションには、バインドできる複数のコールバックがあります。 それぞれのアノテーションにはデフォルトのバインド対象のコールバックがあり、callbackパラメータでその他のものを指定します。

@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.

また、依然としてビューactivityやダイアログ上でビューを探さざるを得ないコードを簡素化するfindByIdメソッドもあります。 それは戻り値の型を推測するためジェネリクスを使用し、自動でキャストします。

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をスタティック インポートして活用しましょう。

Download

Butter Knife v7.0.1 JAR

The source code to the library and sample application as well as this website is available on GitHub. The Javadoc is also available to browse.

Butter Knife v7.0.1 JAR

ライブラリとサンプルアプリケーションのソースは、このサイトと同じくGitHubで利用可能です。 Javadocも閲覧できます

MAVEN

If you are using Maven for compilation you can declare the library as a dependency.

<dependency>
  <groupId>com.jakewharton</groupId>
  <artifactId>butterknife</artifactId>
  <version>7.0.1</version>
</dependency>

あなたがコンパイルにMavenを使用している場合は、依存関係としてライブラリを宣言できます。

<dependency>
  <groupId>com.jakewharton</groupId>
  <artifactId>butterknife</artifactId>
  <version>7.0.1</version>
</dependency>

GRADLE

compile 'com.jakewharton:butterknife:7.0.1'

Be sure to suppress this lint warning in your build.gradle.

lintOptions {
  disable 'InvalidPackage'
}

Some configurations may also require additional exclusions.

packagingOptions {
  exclude 'META-INF/services/javax.annotation.processing.Processor'
}
compile 'com.jakewharton:butterknife:7.0.1'

build.gradle内でlint警告を抑制してください。

lintOptions {
  disable 'InvalidPackage'
}

構成によっては、追加で除外設定が必要な場合があります。

packagingOptions {
  exclude 'META-INF/services/javax.annotation.processing.Processor'
}

IDE CONFIGURATION

Some IDEs require additional configuration in order to enable annotation processing.
 ・ IntelliJ IDEA — If your project uses an external configuration (like a Maven pom.xml) then annotation processing should just work. If not, try manual configuration.
 ・ Eclipse — Set up manual configuration.

いくつかのIDEでは、アノテーションを有効にするために追加の構成が必要です。

 ・ IntelliJ IDEA - プロジェクトが(Mavenのpom.xmlのような)外部の設定を使用している場合、アノテーションは動作するでしょう。 そうでない場合は手動での設定を試してみてください。
 ・ Eclipse — 手動で設定してください。

PROGUARD

Butter Knife generates and uses classes dynamically which means that static analysis tools like ProGuard may think they are unused. In order to prevent them from being removed, explicitly mark them to be kept. To prevent ProGuard renaming classes that use @Bind on a member field the keepclasseswithmembernames option is used.

-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }

-keepclasseswithmembernames class * {
    @butterknife.* <fields>;
}

-keepclasseswithmembernames class * {
    @butterknife.* <methods>;
}

Butter Knifeは動的にクラスを生成し使用します。 これはProGuardのような静的解析ツールが、それらのクラスが未使用だと見なすかもしれないことを意味します。 それらが除去されないように明示します。 メンバーに@Bindアノテーションを使用しているクラスに対するProGuardによるリネームを防止するため、keepclasseswithmembernamesオプションを使います。

-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }

-keepclasseswithmembernames class * {
    @butterknife.* <fields>;
}

-keepclasseswithmembernames class * {
    @butterknife.* <methods>;
}
24
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
25