프로그래밍/Android

Butter Knife (Field and method binding for android views)

freemmer 2016. 8. 25. 19:48



Field and method binding for Android views which uses annotation processing to generate boilerplate code for you.

출처 : GitHub - JakeWharton / butterknife

Butter Knife는 Injection Library로써, Boilerplate 코드를 줄이는 목적으로 사용하실 수 있습니다.
이 라이브러리는 Annotation만으로 Resouce와 Class의 멤버 변수/함수간의 Binding을 자동으로 처리해줍니다. Boilerplate 코드를 줄여 개발 속도 향샹을 목적하신다면 좋은 솔루션이 될 수 있겠죠.
일단 프로젝트에 세팅해 놓으면 직관적으로 사용할 수 있기 때문에 별다른 설명없이 Butter Knife의 Web Page에서 제공하는 샘플 코드만으로도 충분히 사용할 수 있습니다.
아래에 나오는 예제 코드는 모두 Butter Knife의 Web Page에서 가져온 것임을 밝힙니다.
Butter Knife은 Apache 2.0 License를 따릅니다.

Android App Project의 gradle 설정

Project의 build.gradle

buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}

App Module의 build.gradle

apply plugin: 'android-apt'
android {
...
}
dependencies {
compile 'com.jakewharton:butterknife:8.3.0'
apt 'com.jakewharton:butterknife-compiler:8.3.0'
}
참고) 새롭게 추가되는 Jake compiler(Android Studio 2.2.0 이상)를 사용하는 경우에는 apt를 사용하실 필요가 없습니다.

Library Project 에서 사용할 경우 gradle 설정

Project의 build.gradle

buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.jakewharton:butterknife-gradle-plugin:8.3.0'
}
}

Library Module의 build.gradle

apply plugin: 'com.android.library'
apply plugin: 'com.jakewharton.butterknife'

Library Project에서 사용하실 때에는 R대신 R2를 사용해야 합니다.

class ExampleActivity extends Activity {
@BindView(R2.id.user) EditText username;
@BindView(R2.id.pass) EditText password;
...
}

Proguard Ruls

# Retain generated class which implement Unbinder.
-keep public class * implements butterknife.Unbinder { public <init>(...); }
# Prevent obfuscation of types which use ButterKnife annotations since the simple name
# is used to reflectively look up the generated ViewBinding.
-keep class butterknife.*
-keepclasseswithmembernames class * { @butterknife.* <methods>; }
-keepclasseswithmembernames class * { @butterknife.* <fields>; }

Resouce Binding

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
// ...
}
@BindBool, @BindColor, @BindDimen, @BindDrawable, @BindInt, @BindString 이 있습니다.

Non-Activity Binding

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

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);
여러 View를 (위 예제에서는 EditText) List로 관리하는 예제입니다. View들을 List에 담아 놓고 ButterKnife.apply()함수로 속성값을 일괄 적용하는 모습을 보여주고 있는데요 DISABLE, ENABLED와 같은 interface가 어떻게 되어 있는지 구현부 까지 보여주고 있습니다.

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

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

@Nullable
@BindView(R.id.might_not_be_there)
TextView mightNotBeThere;
@Optional
@OnClick(R.id.maybe_missing)
void onMaybeMissingClicked() {
// TODO ...
}

Multi-Method Listeners

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

기타

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


반응형