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 anR.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 toButterKnife.bind
can be made anywhere you would otherwise putfindViewById
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 withButterKnife.bind(this, activity)
.
・ Bind a view's children into fields usingButterKnife.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 theonFinishInflate()
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
andSetter
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 theapply
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);
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);
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 tonull
inonDestroyView
. Butter Knife has anunbind
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 aView
,Activity
, orDialog
. 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
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.
ライブラリとサンプルアプリケーションのソースは、このサイトと同じく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 Mavenpom.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 thekeepclasseswithmembernames
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>;
}