Loklak Wok Android being a sophisticated Android app uses a lot of views, and of those most are manipulated at runtime. In Android to play with a View or ViewGroup defined in XML at runtime requires developers to add the following line:
(TypeOfView) parentView.findViewById(R.id.id_of_view);
This leads to lengthy code. And very often, more than one Views respond to a particular event. For example, hiding Views if a network request fails and showing a message to the user to “Try Again!”. Let’s say you have to hide 4 Views, are you going to do the following:
view1.setVisibility(View.GONE); view2.setVisibility(View.GONE); view3.setVisibility(View.GONE); view4.setVisibility(View.GONE); textView.setVisibility(View.VISIBLE); // has "Try Again!" message. // more 5 lines of code when hiding textView and displaying 4 other Views
Surely not! And the old fashioned way to get a string value defined as a resource in string.xml
String appName = getActivity().getResources().getString(R.id.app_name);
Surely, all this works good, but being a developer while working on a sophisticated app you would like to focus on the logic of the app, rather than scratching your head to debug whether you properly did a findViewById or not, did you typecast it to the proper View, or where did you miss to change the visibility of a view in response to an event.
Well, all of this can be easily handled by using a library which provides you the dependency, here resources. All you need to do is just declare your resources, and that’s it, the library provides the resources to you, yes you don’t need to initialize it using findViewById. So let’s dive in and see how ButterKnife is used in Loklak Wok Android to handle these issues.
Adding ButterKnife to Android Project
In the app/build.gradle:
dependencies { compile 'com.jakewharton:butterknife:8.6.0' annotationProcessor 'com.jakewharton:butterknife-compiler:8.6.0' ... }
Dealing with Views in Fragments
When views are declared, BindView annotation is used with its parameter as the ID of the view, for example, views in TweetHarvestingFragment :
@BindView(R.id.toolbar) Toolbar toolbar; @BindView(R.id.harvested_tweets_count) TextView harvestedTweetsCountTextView; @BindView(R.id.harvested_tweets_container) RecyclerView recyclerView; @BindView(R.id.network_error) TextView networkErrorTextView;
NOTE: Views declared can’t be private.
Once Views are declared, then it needs to be injected, it is done using ButterKnife.bind(Object target, View Source). Here in TweetHarvestingFragment the target will be the fragment itself and source i.e. the parent view will be rootView (obtained by inflating the layout file of fragment). All this needs to be done in onCreateView method
View rootView = inflater.inflate(R.layout.fragment_tweet_harvesting, container, false); ButterKnife.bind(this, rootView);
That’s it, we are done!
The same paradigm can be used to bind views to a ViewHolder of a RecyclerView, as implemented in HarvestTweetViewHolder:
@BindView(R.id.user_fullname) TextView userFullname; @BindView(R.id.username) TextView username; @BindView(R.id.tweet_date) TextView tweetDate; @BindView(R.id.harvested_tweet_text) TextView harvestedTweetTextView; public HarvestedTweetViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); }
Injecting resources like strings, dimensions, colors, drawables etc. is even easier, only the related annotation and ID needs to be provided. Example the string app_name is used in TweetHarvestingFragment to display the app name i.e. “Loklak Wok” in toolbar
@BindString(R.string.app_name) String appName; // directly used inside onCreateView to set the title in toolbar toolbar.setTitlet(appName);
Using ButterKnife onClickListeners can be implemented in separate method, a clean way to define click events rather than polluting onCreate(in Activity) or onCreateView(in Fragment), as implemented in SuggestFragment
@OnClick(R.id.clear_image_button) public void onClickedClearImageButton(View view) { tweetSearchEditText.setText(""); }
Multiple Views responding to a single Event
Using @BindViews annotation a list of multiple views can be created, and then a common ButterKnife.Action can be defined to act on the list of views. In TweetHarvestingFragment visibility of some views are changed if a network request fails or succeeds using this feature of ButterKnife:
// declaring List of Views @BindViews({ R.id.harvested_tweets_count, R.id.harvested__tweet_count_message, R.id.harvested_tweets_container}) List<View> networkViews; // defining action for views private final ButterKnife.Action<View> VISIBLE = (view, index) -> view.setVisibility(View.VISIBLE); private final ButterKnife.Action<View> GONE = (view, index) -> view.setVisibility(View.GONE); // applying action on the views ButterKnife.apply(networkViews, VISIBLE); ButterKnife.apply(networkViews, GONE);
Resources
- Butterknife guide: http://jakewharton.github.io/butterknife/