Using ButterKnife in PSLab Android App

ButterKnife is an Android Library which is used for View injection and binding. Unlike Dagger, ButterKnife is limited to views whereas Dagger has a much broader utility and can be used for injection of anything like views, fragments etc. Being limited to views makes it much more simpler to use compared to dagger.

The need for using ButterKnife in our project PSLab Android was felt due to the fact binding views would be much more simpler in case of layouts like that of Oscilloscope Menu which has multiple views in the form of Textboxes, Checkboxes, Seekbars, Popups etc. Also, ButterKnife makes it possible to access the views outside the file in which they were declared. In this blog, the use of ButterKnife is limited to activities and fragments.

ButterKnife can used anywhere we would have otherwise used findViewById(). It helps in preventing code repetition while instantiating views in the layout. The ButterKnife blog has neatly listed all the possible uses of the library.

The added advantage of using Butterknife are-

  • The hassle of using Boilerplate code is not needed and the code volume is reduced significantly in some cases.
  • Setting up ButterKnife is quite easy as all it takes is adding one dependency to your gradle file.
  • It also has other features like Resource Binding (i.e binding String, Color, Drawable etc.).
  • Other uses like simplification of code while using buttons. For example, there is no need of using findViewById and setOnClickListener, simple annotation of the button ID with @OnClick does the task.

Using butterknife was essential for PSLab Android since for the views shown below which has too many elements, writing boilerplate code can be a very tedious task.

Using ButterKnife in activities

The PSLab App defines several activities like MainActivity, SplashActivity, ControlActivity etc. each of which consists of views that can be injected with ButterKnife.

After setting up ButterKnife by adding dependencies in gradle files, import these modules in every activity.

import butterknife.BindView;
import butterknife.ButterKnife;

Traditionally, views in Android are defined as follows using the ID defined in a layout file. For this findViewById is used to retrieve the widgets.

private NavigationView navigationView;
private DrawerLayout drawer;
private Toolbar toolbar;

navigationView = (NavigationView) findViewById(org.fossasia.pslab.R.id.nav_view);
drawer = (DrawerLayout) findViewById(org.fossasia.pslab.R.id.drawer_layout);
toolbar = (Toolbar) findViewById(org.fossasia.pslab.R.id.toolbar);

However, with the use of Butterknife, fields are annotated with @BindView and a View ID for finding and casting the view automatically in the layout files.
After the annotation, finding and casting of views ButterKnife.bind(this) is called to bind the views in the corresponding activity.

@BindView(R.id.nav_view) NavigationView navigationView;
@BindView(R.id.drawer_layout) DrawerLayout drawer;
@BindView(R.id.toolbar) Toolbar toolbar;
setContentView(R.layout.activity_main);
ButterKnife.bind(this);

Using ButterKnife in fragments

The PSLab Android App implements ApplicationFragment, DesignExperimentsFragment, HomeFragment etc., so ButterKnife for fragments is also used.

Using ButterKnife is fragments is slightly different from using it in activities as the binded views need to be destroyed on leaving the fragment as the fragments have different life cycle( Read about it here). For this an Unbinder needs to be defined to unbind the views before they are destroyed.

Quoting from the official documentation

When binding a fragment in OnCreateView, set the views to null in OnDestroyView. Butter Knife returns an Unbinder instance when you call bind to do this for you. Call its unbind method in the appropriate lifecycle callback.

So, an additional module of ButterKnife is imported

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;

Similar as that of activities, the code below can be replaced by the corresponding ButterKnife code.

TextView tvDeviceStatus = (TextView)view.findViewById(org.fossasia.pslab.R.id.tv_device_status);
TextView tvVersion = (TextView)view.findViewById(org.fossasia.pslab.R.id.tv_device_version);
ImageView imgViewDeviceStatus = (ImageView)view.findViewById(org.fossasia.pslab.R.id.img_device_status);
@BindView(R.id.tv_device_status) TextView tvDeviceStatus;
@BindView(R.id.tv_device_version) TextView tvVersion;
@BindView(R.id.img_device_status) ImageView imgViewDeviceStatus;
private Unbinder unbinder;

Additionally, the unbinder object is used to store the returned Unbinder instance when ButterKnife.bind is called for Fragments.

unbinder = ButterKnife.bind(this,view);

Finally, the unbind method of unbinder is called while view is destroyed.

@Override public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}