Using the Butterknife library for view injection in Android. This tutorial explains the usage of the butterknife library to inject views into Android application.

1. Butterknife

Butterknife is a light weight library to inject views into Android components. It uses annotation processing.

The @BindView annotation allow to inject views and performs the cast to the correct type for you. The @@OnClick(R.id.yourid) annotation allows to add OnClickListener to a view. You can optional define the method parameter of the view in case you want it injected.

Butterknife includes also 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.

import static butterknife.ButterKnife.findById;
....

View view = LayoutInflater.from(context).inflate(R.layout.thing, null);
TextView firstName = findById(view, R.id.first_name);
TextView lastName = findById(view, R.id.last_name);
ImageView photo = findById(view, R.id.photo);

You can also bind to fragments. Butterknife also allows to unbind again, via the Unbinder object.

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

Annotated attributes and methods cannot be private, as ButterKnife needs to be able to access them from a separate class.

2. How does Butterknife work

Butterknife uses annotation processing to generated modified Java classes based on your annotations. Annotation processing is a tool build in javac for scanning and processing annotations at compile time.

You can define custom annotations and a custom processor to handle them. These annotations are scanned and processed at compile time. The annotation processor does not change the exiting input class but it generates a new Java class. This generated Java code is compiled again as a regular Java class.

The Butterknife annotation processor scans all Java classes looking for the Butterknife annotations. If a class contains these annotations, it generates a new class based on the <original_class>__ViewBinding schema.

Here is the generate class from our example.

// Generated code from Butter Knife. Do not modify!
package com.vogella.android.butterknifeexample;

import android.support.annotation.CallSuper;
import android.support.annotation.UiThread;
import android.view.View;
import android.widget.TextView;
import butterknife.Unbinder;
import butterknife.internal.DebouncingOnClickListener;
import butterknife.internal.Utils;
import java.lang.IllegalStateException;
import java.lang.Override;

public class MainActivity_ViewBinding implements Unbinder {
  private MainActivity target;

  private View view2131165194;

  @UiThread
  public MainActivity_ViewBinding(MainActivity target) {
    this(target, target.getWindow().getDecorView());
  }

  @UiThread
  public MainActivity_ViewBinding(final MainActivity target, View source) {
    this.target = target;

    View view;
    target.title = Utils.findRequiredViewAsType(source, R.id.textView, "field 'title'", TextView.class);
    view = Utils.findRequiredView(source, R.id.button, "method 'submit'");
    view2131165194 = view;
    view.setOnClickListener(new DebouncingOnClickListener() {
      @Override
      public void doClick(View p0) {
        target.submit();
      }
    });
  }

  @Override
  @CallSuper
  public void unbind() {
    MainActivity target = this.target;
    if (target == null) throw new IllegalStateException("Bindings already cleared.");
    this.target = null;

    target.title = null;

    view2131165194.setOnClickListener(null);
    view2131165194 = null;
  }
}

Once you call ButterKnife.bind(this), this will call into the generated constructor and perform the view injections and listener registration.

3. Exercise: Using Butterknife in your Android project

3.1. Create project

Create a new Android project with the package com.vogella.android.usinglibs. Add a text view with the @+id/textView to it and a button to the existing layout with the @+id/button ID.

3.2. Add Butterknife to your Gradle build configuration

Add the com.jakewharton:butterknife in its latest version as compile dependency build.gradle file.

apply plugin: 'com.android.application'

android {
    ...
}

dependencies {
    ...
    implementation 'com.jakewharton:butterknife:8.5.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
}

3.3. Use view injection in your Android activity

Use @BindView, @OnClick and ButterKnife.bind to get the views injected.

The following shows a possible solution.

package com.vogella.android.usinglibs;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class MainActivity extends Activity {
    @BindView(R.id.textView)
    TextView title;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        title.setText("Hello from Butterknife");
    }

    @OnClick(R.id.button)
    public void submit() {
        Toast.makeText(MainActivity.this,
                "Hello from Butterknife OnClick annotation", Toast.LENGTH_SHORT).show();
    }

}

3.4. Validate setup

Run your application and ensure that the TextView gets injected and the button event is fired, if you click the button.