Using Butter Knife to Inject Views in Android

Introduction

In every Android Application, we use a very frequently repeated method called findViewById() for every view that we want to use in our application’s code. Especially when application’s design gets more complex layouts the repetitive use of this function becomes a real pain and this is where Jake Wharton’s Butter Knife Library comes in.

In this tutorial, we’ll learn a different type of annotations and their use in binding views, strings, colors, drawable, click events and a lot more. Let’s first look at the different type of annotations provided in Butter Knife Library.


1. Annotations

Below is the list of annotations that are available in Butter Knife Library.

@BindView: Binds view object. TextView, Button, Spinner or any view object like below
@BindView(R.id.logo) ImageView ivLogo;

@BindViews: Binds array of views into List
@BindViews({R.id.tvFirstName, R.id.tvLastName}) List<TextView> tvArray;

@BindDrawable: Binds drawable element. Loads the drawable image from res folder
@BindDrawable(R.mipmap.ic_launcher) Drawable logo;
 
@BindString: Binds drawable element. Loads the drawable image from res folder
@BindString(R.string.app_name) String appName;

@BindColor: Binds color resource like below
@BindColor(R.color.colorPrimaryDark) int colorTitle;

@BindDimen: Binds dimen resource as follows
@BindDimen(R.id.padding_horizontal) float paddingHorizontal;

@BindAnim: Binds animation from anim resource
@BindAnim(R.anim.move_up) Animation animMoveUp;

@BindBitmap: Binds bitmap object like this
@BindBitmap(R.mipmap.ic_launcher) Bitmap logo;

@BindFloat: 
@BindFloat(R.dimen.radius) float radius;

@BindInt: 
@BindInt(R.integer.size) int size;



2. Adding ButterKnife Gradle Dependency
First of all, you’ve to add Gradle dependencies of ButterKnife library. Go to app/build.gradle file and add below lines, once added sync your project.
// butter knife
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

 

3. Activity Binding
Let’s do some action and use @BindView and @OnClick annotations in an Activity considering following layout with EditText, TextView and Button.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    android:gravity="center_horizontal"
    tools:context="com.nthreads.butterknifedemo.MainActivity">

    <EditText
        android:id="@+id/etFirstName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="First Name" />

    <EditText
        android:id="@+id/etLastName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Last Name" />

    <Button
        android:id="@+id/btnSubmit"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Submit" />

    <TextView
        android:id="@+id/tvFullName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

</LinearLayout>

 

To bind the views in the activity, we need to follow the below steps.

1. Annotate fields with @BindView and @OnClick with a view ID.
2. Call ButterKnife.bind(this) in onCreate() method after setContentView() is called.

That’s all, the view injection happens and no need to typecast the view variable using findViewById() method anymore.

public class MainActivity extends AppCompatActivity {

    @BindView(R.id.etFirstName) EditText etFirstName;
    @BindView(R.id.etLastName) EditText etLastName;
    @BindView(R.id.tvFullName) EditText tvFullName;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // bind the view using ButterKnife
        ButterKnife.bind(this);
    }

    @OnClick(R.id.btnSubmit)
    public void btnSubmitOnClick(View view) {
        tvFullName.setText(String.format("%s %s", etFirstName.getText().toString(),
                etLastName.getText().toString()));
    }
}

 

4. Non-Activity Binding & Unbinding
You can also perform binding on arbitrary objects by supplying your own view root.
Fragments have a different view lifecycle than activities. When binding a fragment in onCreateView, set the views to null in onDestroyView. Butter Knife returns an Unbinder instance when you call bind method. Call its unbind method in the appropriate lifecycle callback.

public class CustomFragment extends Fragment {
    @BindView(R.id.btnYes) Button btnYes;
    @BindView(R.id.btnNo) Button btnNo;
    private Unbinder unbinder;

    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.custom_fragment, container, false);
        unbinder = ButterKnife.bind(this, view);
        // TODO Use fields...
        return view;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if(unbinder != null) unbinder.unbind();
    }
}

 

5. Binding in List Adapter
ButterKnife can also be used in list adapters. Below is the example of @BindView in recyclerview’s adapter class.

public class ProfileAdapter extends RecyclerView.Adapter<ProfileAdapter.ProfileViewHolder> {

    private List<Profile> profiles;

    public ProfileAdapter(List<Profile> profiles) {
        this.profiles = profiles;
    }

    @Override
    public ProfileViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_profile, parent, false);

        return new ProfileViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(ProfileViewHolder holder, int position) {
        Profile contact = profiles.get(position);
        holder.firstName.setText(contact.getFirstName());
        holder.lastName.setText(contact.getLastName());
        holder.mobile.setText(contact.getMobile());
    }

    @Override
    public int getItemCount() {
        return profiles.size();
    }

    public class ProfileViewHolder extends RecyclerView.ViewHolder {

        @BindView(R.id.tvFirstName) TextView firstName;
        @BindView(R.id.tvLastName) TextView lastName;

        @BindView(R.id.tvMobile) TextView mobile;

        public ProfileViewHolder(View view) {
            super(view);

            // binding view
            ButterKnife.bind(this, view);
        }
    }
}

 
6. Binding List Views
You can group multiple views into a List or array as below;

@BindViews({ R.id.etFirstName, R.id.etMiddleName, R.id.etLastName })
List<EditText> nameViews;

The apply method allows you to act on all the views in a list at once.

ButterKnife.apply(nameViews, DISABLE);
//OR
ButterKnife.apply(nameViews, ENABLED, false);

//Action and Setter interfaces allow specifying simple behavior.

static final ButterKnife.Action DISABLE = new ButterKnife.Action() {
        @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);
        }
    };

Events can also be grouped as follows

@OnClick({ R.id.btnOne, R.id.btnTwo, R.id.btnThree })
public void btnOnClick(View view) {
    // TODO : Handle events
}

I hope you get the basic idea of how to use Butter Knife library to bind your views. If you find this post helpful please don’t forget to share it, like it and leave suggestions, criticisms in the comments section below. 🙂



Leave a Comment

Your email address will not be published. Required fields are marked *