I recently learned about a small feature in Android which has been there since API level 1 (the variant that we’ll use has been in there since API 3, but others appeared in API 1): Compound Drawables. In this article we’ll have a look at what they are and see how we can use them to simplify some of our layouts.
Before we start, a little background: Recently I was working on a project where one of my layouts contained typical “icon” configuration (consisting of a graphic with a text label below it), quite similar to what we used in the article on Intelligent Layouts. My layout worked perfectly well, but I updated to ADT 16 and the new Lint tool gave me a warning which I didn’t understand: This tag and its children can be replaced by one
I my code to be free of warnings because they are often indicative of a potential bug or inefficiency, so I wanted to learn what this warning meant in order to eradicate it. First let’s have a look at the block in my XML which caused this warning:
[xml]
[/xml]
Which looks like this:
The warning was given on the LinearLayout element. A quick dig through the documentation for TextView lead me to the setCompoundDrawableWithIntrinsicBounds() method which is a method of attaching drawables to a TextView. We can replace the LinearLayout and its two children with a single TextView:
[xml][/xml]
Then in the onCreate() method of our activity we can assign a drawable to appear above the text:
[java] @Overridepublic void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
setContentView( R.layout.main );
TextView tv = (TextView) findViewById( R.id.textView );
tv.setCompoundDrawablesWithIntrinsicBounds( 0,
R.drawable.ic_launcher, 0, 0 );
}
[/java]
setCompoundDrawablesWithIntrinsicBounds() takes four parameters representing drawables which will be placed to the left, above, to the right of, and below the text respectively. In this case we only want an image above the text, so we set the set the second parameter to the drawable we require and the others to 0. There are a couple of variants of the setCompoundDrawables*() method which I won’t cover here, but are easy enough to understand. In addition you can use the setCompoundDrawablePadding() method to apply padding to the drawables.
I won’t bother with a screen capture of this because it is identical to the previous one – it just uses a single Widget in the layout compared to the initial one which used three.
As ever in Android, there are multiple ways of achieving the same result, and we can achieve the same thing purely in layout so that there is no java code required:
[xml][/xml]
This is almost identical to the previous solution except that we have replaced the id attribute with a drawableTop one. drawableTop is effectively calling setCompoundDrawablesWithIntrinsicBounds() for us, so removing the need for any java.
As well as the simple icon layout that I’ve demonstrated here, there are plenty of other use cases where compound drawables can simplify your layouts. If you use the Lint tool in ADT 16+ you will be alerted to instances where you can make this optimisation. It my only sound like a small improvement to create one Widget instead of three, but if we expand this to the layout that we created in Intelligent Layouts, we would be creating 18 fewer widgets.
This optimisation can also be used in the Views that we create for individual ListView items to make your scrolling smoother – fewer object creations speeds up layout inflation (but always recycle your views as discussed here).
The source code for this article can be found here.
© 2012, Mark Allison. All rights reserved.
Copyright © 2012 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.
The compound drawables are pretty useful, but not for all cases.
The drawable has different gravity than the text itself, meaning that it won’t be close to the text when the component is wide or tall relatively to the size of the text itself.
For instance, try to create a text item with left drawable, set its gravity to center and make it wide. You could see the drawable is always docked to the left, and the text is positioned in the center. If you want the drawable to be closer to the text, you need to play with the drawablePadding attribute (which depends on the text’s length) or just use LinearLayout :/
Agreed – not for all use cases. But there a lot of use cases where where they *are* applicable. Knowing about them is the important thing!
Udinic, I know this is over a year later, but do you know if there’s a way to set the gravity on the drawable? I just ran into this exact problem. Alternatively, do you know if there’s a way to get Eclipse to shut up about the warning?
Better approach is embed the ViewImage into another LinearLayout (allow handle it with alone id):
Erm, did you actually read the article? If so, then you should know that is precisely how I started the article, and showed how to make the layout much more efficient by using compound drawables.