04/07/2015
Butter Knife es una librería Open Source para Android que permite asociar los widgets y eventos a sus correspondientes atributos y métodos en el código Java mediante el uso de anotaciones. El objetivo es ahorrar la escritura de código eliminando las llamadas a findById, setOnClickListener, etc, haciendo de paso el código más simple y legible. Estas anotaciones pueden ser utilizadas en Activities, Fragments y Adapters. También permite «inyectar» recursos tales como textos del strings.xml, dimensiones de los ficheros dimens.xml, etc.
Tomemos como ejemplo el siguiente layout con un EditText, un TextView y un Button
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="16dp" android:paddingRight="16dp" android:paddingTop="16dp" android:id="@+id/layout"> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/editText" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" android:hint="@string/editText"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:id="@+id/textView" android:layout_below="@+id/editText" android:layout_marginTop="15dp" android:layout_alignRight="@+id/editText" android:layout_alignEnd="@+id/editText" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/button" android:id="@+id/button" android:layout_marginTop="15dp" android:layout_below="@+id/textView" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_alignParentRight="true" android:layout_alignParentEnd="true" /> </RelativeLayout>
Seguimos los siguientes pasos para utilizar Butter Knife
- Añadir la dependencia en el fichero build.gradle.
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' }
Para evitar problemas con la ofuscación de código es necesario añadir las siguientes líneas al fichero de configuración de proguard.
-keep class butterknife.** { *; } -dontwarn butterknife.internal.** -keep class **$$ViewBinder { *; } -keepclasseswithmembernames class * { @butterknife.* ; } -keepclasseswithmembernames class * { @butterknife.* ; }
- Los atributos en los que se guardará la referencia a los widgets de la interfaz se anotarán con @BindView, indicando el id del layout.
public class MainActivity extends Activity { @BindView(R.id.editText) EditText editText; @BindView(R.id.textView) TextView textView; ...
- Para los métodos que actúen como listeners la anotación dependerá del evento, disponemos entre otros de los siguientes: @OnCheckedChanged, @OnClick, @OnEditorAction, @OnFocusChange, @OnItemClick, @OnItemLongClick, @OnItemSelected, @OnLongClick, @OnPageChange, @OnTextChanged, @OnTouch.
@OnClick(R.id.button) public void copy(Button button) { textView.setText(editText.getText()); }
Nota: en el caso del @OnClick, el parámetro con el View que lanza el evento es opcional.
- Iniciar Butter Knife en cuanto se establezca el layout de la Activity.
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //IMPORTANTE!!! ButterKnife.bind(this); ...
ListView
Además de Activities, Butter Knife se puede utilizar en Fragments y Adapters. Por ejemplo, consideremos una ListView cuyas filas tienen el siguiente layout:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:id="@+id/title" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceSmall" android:id="@+id/subtitle" /> </LinearLayout>
En la Activity, tal y como hemos visto, vamos a «inyectar» el ListView y a definir un listener que muestre un Toast cada vez que se pulse en una de sus filas.
@BindView(R.id.listView) ListView listView; private List<String> objects; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //IMPORTANTE!!! ButterKnife.bind(this); objects = new ArrayList<String>(20); for (int i = 0; i < 20; i++) { objects.add("object " + i); } listView.setAdapter(new CustomAdapter(this, objects)); } @OnItemClick(R.id.listView) public void onItemClick(int position) { Toast.makeText(this, objects.get(position), Toast.LENGTH_SHORT).show(); }
La implementación de un ArrayAdapter siguiendo el ViewHolder pattern.
public class CustomAdapter extends ArrayAdapter<String> { private LayoutInflater layoutInflater; public CustomAdapter(Context context, List<String> objects) { super(context, 0, objects); layoutInflater = LayoutInflater.from(context); } @Override public View getView(int position, View view, ViewGroup parent) { ViewHolder holder; if (view != null) { holder = (ViewHolder) view.getTag(); } else { view = layoutInflater.inflate(R.layout.listview_row, parent, false); holder = new ViewHolder(view); view.setTag(holder); } holder.title.setText(getItem(position)); holder.subtitle.setText(getItem(position)); return view; } static class ViewHolder { @BindView(R.id.title) TextView title; @BindView(R.id.subtitle) TextView subtitle; public ViewHolder(View view) { ButterKnife.bind(this, view); } }
El proyecto completo para Android Studio se encuentra en Github. Para más información sobre cómo utilizar GitHub, consultar este artículo.