Messing with the drawable state

In this post you will learn about some of the details concerning Views and states. And you will learn how you can use your own custom states and manage them in a simple way. For the most part we discuss  StateListDrawable. However everything you will lean here works with ColorStateLists as well. Sample code for this post is available on github.

Using drawables

Sometimes it is very tempting to just set the background of a View manually, depending on the state of your Activity or Fragment. Unfortunately this requires additional code and state transitions won’t work anymore.

In this post I will show you two flavors of another way of doing this. Both of these flavors use the Android framework and will work in any app and with any Drawable.

Our case

We have a TextView somewhere in an app, that displays the current trend of something important for the user. In this case “Solar Radiation” and “Energy Output”. The trend has three possible values: moving up, moving down or stable. And finally, there should just be a single method to set the state and there should be a simple fade between the state changes.

The Android system already has a Drawable that implements all of this behavior, and you probably already know about this Drawable. The Drawable I am talking about is StateListDrawable, more commonly known as a selector. Selectors allow you to respond to state changes, and you can declare them simply using xml.

Creating the drawable

So let’s create that drawable! First we will define the possible states for the trend: state_up, state_equal, and state_down in our attrs.xml file like this:

<attr name="state_up" format="boolean"/>
<attr name="state_equal" format="boolean"/>
<attr name="state_down" format="boolean"/>

Now that we have the different states, we we will create a drawable that uses these different states:

<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
   <item android:drawable="@drawable/ic_state_up" app:state_up="true" />
   <item android:drawable="@drawable/ic_state_equal" app:state_equal="true" />
   <item android:drawable="@drawable/ic_state_down" app:state_down="true" />
   <item android:drawable="@drawable/ic_state_down" />
</selector>

That’s all for step one, we now have the selector we will use to transition between our custom states. As I said, there are two flavors we’ll explore. The first flavor is (not completely coincidental) exactly how Android implements view-state management in the framework. With view-state management I am talking about things such as selected state, checked state, activated state and focussed state. For us this involves creating a subclass of the view we want to use, and integrating the view-state into this class.

The second flavor makes use of a special DrawableWrapper that will manage the state. This is a bit more generic as we no longer need to subclass the view and tightly couple the state to it. Instead we can couple the state into a drawable subclass which we can use within all views.

Flavor 1: View subclass

As noted before, this implementation mirrors the way the framework implements state management. Once you know understand the steps involved here, it becomes real easy to do this yourself. The steps we need to follow are as follows:

  1. Create a subclass of the view we need, for example TrendView.
  2. Add an int-def that contains the possible states we can set.
  3. Add a method to the view to set our custom states.
  4. Implement refreshDrawableState to calculate the new state.

IntDef

I usually choose to implement the state as an IntDef with a setter for the state. This has the lowest memory footprint as discussed on developer.android.com. To quote what is written there:

For example, enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.

First we add the IntDef and the different states to our class, and a setter setTrend to set the state. This method just sets our internal state, and in order to prevent additional work we also verify that the new state is actually different from the old state. After assigning the variable, we call refreshDrawableState. Calling refreshDrawableState triggers the invalidation process for the drawable state and will result in a call to onCreateDrawableState.

@IntDef({
         STATE_UP,
         STATE_DOWN,
         STATE_EQUAL
   })
@Retention(RetentionPolicy.CLASS)
public @interface Trend {}

@Trend
int mTrend;

public void setTrend(@Trend int trend) {
   if (mTrend != trend) {
      mTrend = trend;
      refreshDrawableState();
   }
}

Next we add a state-set for each of the possible states. These state-sets are merged together when the state is being calculated, and each state-set may consist of multiple identifiers. In our case each of them just contains a single constant for its state. These are the same constants we defined in attrs.xml earlier. To clarify, a single state-set looks like this:

private static final int[] UP_STATE_SET = { 
   R.attr.state_up 
}; 

The final part of the puzzle is onCreateDrawableState. In this method we just switch on our state, and merge our state with the thus far calculated drawable state. First we call super.onCreateDrawableState with extraState + 1 (because we want the parent class to reserve one additional slot for our state) and then we merge the states together. And finally, we return the result of the merge.

The full class now looks something like this (without the constructors):

public class TrendView extends AppCompatTextView {

   public static final int STATE_UP = 0;
   public static final int STATE_DOWN = 1;
   public static final int STATE_EQUAL = 2;

   @IntDef({
         STATE_UP,
         STATE_DOWN,
         STATE_EQUAL
   })
   @Retention(RetentionPolicy.CLASS)
   public @interface Trend {}

   private static final int[] UP_STATE_SET = {
      R.attr.state_up
   };

   private static final int[] DOWN_STATE_SET = {
      R.attr.state_down
   };

   private static final int[] EQUAL_STATE_SET = {
      R.attr.state_equal
   };

   @Trend
   int mTrend;

   public void setTrend(@Trend int trend) {
      if (mTrend != trend) {
         mTrend = trend;
         refreshDrawableState();
      }
   }

   @Override
   protected int[] onCreateDrawableState(int extraSpace) {
      // Only add 1 because we only have one state active at
      // any time
      final int[] drawableState =
         super.onCreateDrawableState(extraSpace + 1);
      switch (mTrend) {
         case STATE_UP:
         mergeDrawableStates(
            drawableState, UP_STATE_SET);
         break;
      case STATE_DOWN:
         mergeDrawableStates(
               drawableState, DOWN_STATE_SET);
         break;
      case STATE_EQUAL:
         mergeDrawableStates(
               drawableState, EQUAL_STATE_SET);
         break;
      }
      return drawableState;
   }
}

Now we can simply call setTrend on our custom View and the drawable will automatically be updated as well. In fact any Drawable that has the same states will work just fine.

Flavor 2: Using a wrapper drawable

As you might have suspected, the state being part of the view is not ideal. We can’t reuse the states or the drawables, in other classes without subclassing those views. It would be easier if we could decouple the state from the View. Let’s take a look at a different approach!

Why we need a wrapper

This part of the post is called using a wrapper drawable. Let’s first try to understand why we need a wrapper. What is wrong with calling setState directly?

As we have seen in the previous approach, the drawable state is controlled by the view. The state calculated in onCreateDrawableState is applied to each of the drawables by calling the setState method.  This means that in case the view’s state changes, it will synchronize its state to the drawable and call setState. So in case we manually call setState, we risk the view overriding our state with its own state.

Our solution

That is why we are going to create a StateDrawableWrapper. This wrapper will have two tasks, its first task is to prevent the view from propagating its state to the wrapped drawable, and its second task is applying our custom state to the wrapped drawable.

To accomplish the first task, we will override the setState method to prevent the View from updating the state. For now we will just return false from this method. For the second task we will add a new method to the wrapper to set our own state, called setCustomState. This method will simply call setState on the wrapped drawable. This wrapper might look like this:

public class StateDrawableWrapper extends DrawableWrapper {

	int[] mStateSet;

	public StateDrawableWrapper(Drawable drawable) {
 		super(drawable);
	}

	@Override
	public boolean setState(int[] stateSet) {
		// do nothing
		return false;
	}

	public void setCustomState(int[] stateSet) {
		if (!Arrays.equals(mStateSet, stateSet)) {
			mStateSet = stateSet;
			getWrappedDrawable().setState(stateSet);
			invalidateSelf();
		}
	}
	public int[] getCustomState() {
		return mStateSet;
	}
}

Above drawable wrapper is all there is to this. It prevents the view from updating the state of the wrapped drawable, and it can set our custom state on the View. And, it even works fine with transitions.

The one thing that is still missing here is the ability to also handle the states the view sets. For example selection and pressed states. In my next post I will discuss a way to implement this in a different way that also works correctly with the view’s state.

Thank you for reading! Cheers and see you next time.

Packages are like classes

Packages with classes grouped by type, are like utility classes. This may not always be what you want.

This post is not specific to Android, or Java for that matter. Some other languages may offer similar features.

Introduction

Packages are something we usually don’t give much thought. For that reason developers sometimes just use them to group classes together, for example classes that perform similar actions or share a common ancestor. I think packages can offer a lot more than just grouping similar classes and should be thought of as an OOP (Object Oriented Programming) concept. That’s why I say: “Packages are like classes”. Now let’s investigate what other possibilities of organizing code are available with packages, and how that relates to classes.

Packages in app-architecture

Like I said before, I consider packages an OOP concept, just like inheritance and delegates. Using packages as such will help you design better libraries and apps, because using these features will help you to hide the implementation details, clients don’t need to know about. (Clients being any code using your code, even in the same app)

Access modifiers

Before diving deeper into this, let’s discuss how we organize code and how we use access modifiers to determine what methods should be visible.

The first building block is code. Your application consists of code. This code is grouped into methods. These methods are grouped into classes and the classes, in turn, are grouped into packages. And finally we use access modifiers to control the visibility of these methods. By doing so we can make them internal to the class or to their subclasses. Or private within the package. Package private is like protected, except that the methods and fields are not available to subclasses (in other packages). The full access table looks like this:

ModifierClassPackageSubclassWorld
publicYYYY
protectedYYYN
no modifierYYNN
privateYNNN

credits: https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html

Now think about this for a moment, think how you usually use these various modifiers to hide certain methods from external clients or subclasses. Think about how private methods contain very specific implementation details you don’t want a client to use.

Think again!

Now imagine, that you can also use these modifiers on your classes, as members of your package. Now think about grouping classes in a package “because they are all fragments or activities” or “because they are all helpers”. Think about them as if they were methods of a class, which ones would you like your clients to use, and which ones would you like to hide.

When you group your classes by type, you lose package private, and thereby the option to minimize the visibility of these classes. You can’t hide your implementation details because the class needs to be accessible from other packages as well, for example to create an instance.

Grouping by type

So what packaging by type really brings is the same thing static utility classes bring. They allow you to group a set of classes together. You can see this for example in Java’s collections framework. Most of the implementations are intentionally grouped in the java.util package. This is intentional because they are all supposed to be used by any client that needs them.

Conclusion

So spreading your implementation details over multiple packages leaks your implementation details to the client. If you are creating a library this is even worse. These implementation details are now part of your public API. You need to maintain it, and make sure it is keeps working for all consumers of your library. People will also start using it in ways you never intended and are going to start reporting bugs on those use-cases.

So next time you create a class, specifically for a single feature, think whether it is something that should be public or package private. And remember, you can always refactor your code if you are not satisfied with the result.