My last job was all about Android internals, all the UI was the sole premises of our Core team, i was on the porting side so i have to support their interfaces and just make sure they work flawlessly. I did that, but got tiered of it.
Found another job, one that required me to make a product from scratch.
Not much was around the product, meaning it’s a web service with a GUI (when u put it like that you can consider that all android application are pretty lame
) but i had to work my ass off to create a GUI that was somewhat similar to iPhone, cause that was the original product in the company so all specs followed.
In the course of my work i got to discover how to create custom components right, meaning with xml attributes support, how the styles.xml should look like what should i put in a style and what should i put in a theme, what should go to the values (stings\colors\ dimensions), but what’s most important i got to get to know the Google different View’s intimately, code wise
I can tell you some components are pretty “heavy” long lines of code, sometimes not so organized (like EditText and TextView which i think could have been one View…) and the worst is sometimes they are not really made for inheritance which causes the need to duplicate code – which is plain stupid! but sometimes there’s no choice. I’ll show you here in a minute what i mean.
So take a look on the following image:
you can see the ‘Stops’ section, each has a check box. The check box is not only checked but the whole line turn yellow when it’s checked. not so complicated to do this kind of thing if you got the right images, some xml files for StateListdrawable with check states and some code. But i want you to notice something else, not that the check box itself (the Place with the ‘V’ icon) is not close to the edge, nor to the text itself. it has padding, sound logical right ? that you can set up your own padding on an image of a check-box ? well google didn’t think so.
Let’s assume you have ‘check_box_background.xml’ which contains states for the colors of the check box when pressed, selected, checked and pressed, checked and selected and default. Now let’s assume you have a file with the same states but with the check-box image itself named ‘check_box_image_square.xml’ so the simple naive method of doing things is to use in your ‘sample_layout.xml’ file:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:padding="15dp"> <CheckBox android:layout_width="fill_parent" android:layout_height="wrap_content" android:button="@drawable/btn_check_square" android:background="@drawable/filter_item_background" android:textColor="@android:color/black" android:text="check box text"/> </LinearLayout> |
This however doesn’t work… the result is:
There are two reason why this happens (if you didn’t notice there’s not space between the check box and the text)
- We use regular image png, not a 9 patch – I’ll explain the effect in a bit.
- The android code for Check box does not allow check box padding anywhere, actually, the code really just takes the button image and throws it first thing to the screen and then paints the text box according to the padding it has
So like i wrote in #2 the only reason your regular check box has space between the image and the text is because the TextView it inherits from got left padding. but you didn’t set the padding, well the 9-path did.
I’ll be honest i didn’t check the 9-patch internals so till this very day i’ not sure how adding the lines around the image actually hands the bitmap and creates spaces and padding, but it’s a fact that whenever you’ll put the 9-path as a background you’ll get 27 dip padding.
you are probably thinking: but this is stupid! i need to consider 9-path for padding just so I’ll some spaces ? why not define our own padding when drawing the view ?
I’m still asking that question…
Another question is why i can’t access the image itself of the check box, or more like, wjy is it private and not protected ? why is it’s srawing method private and not protected ?
so let’s say i decide it’s time to make things better and add in a PaddableCheckBox that wil be like a regualr checkbox but can define leftCheckPadding and rightCheckPadding that will be the pixels from right of the check image and left of te check image respectivly.
Syou you extend check box and override it’s onDraw since Checkbox onDraw calls super.onDraw for the TextView to aint itself, we need a way to prevent it from painting the button drawable, so naive code would be to:
1 2 3 4 5 6 7 8 | protected void onDraw(Canvas c){ Drawable button = mButton; mButton = null; super.onDraw(c); mButton = button; mButton.setBounds(mPaddingLeft, 0, mButton.getIntrictWidth(), mButton.getIntrictHeight()); } |
this does not work for several reasons:
- you don’t get access to the button instance, it’s private(wwhhhyyy ??)
- the super.onDraw() will paint the textView under you image if the padding is too big, you need to shift the TextView painting as well, but this is easy using translate on the Canvas object or setting padding that will push the text in the text view all the way to the right position.
- once u add padding u are chaining the widget width, you need to add those padding to the widget onLayout and onMeasure.
So concluding from all the above i reached the conclusion that the best way will be to calculate the pixels needed for the left p+image+right p and add it as left padding. this will cause the view to take it into account when measuring AND it gives you the space to draw what you need.
We are still left with the mButton being private and that i can’t stop it from being drawn.
This is what i think google did wrong, i think most of the members and method related to drawing had to be ‘protected’ and not ‘private’, that what it really means not ready for inheritance.
If you want the CheckBox on the left side, there is a hack for you.
consider the following onDraw method for a class that extends CheckBox:
1 2 3 4 5 6 | @Override protected void onDraw(Canvas canvas) { canvas.translate(mImagePaddingLeft, 0); super.onDraw(canvas); canvas.translate(-mImagePaddingLeft, 0); } |
Ugly hack for all to think, no doubt about it… but it works… what we do is tell the canvas to translate itself mImagePaddingLeft pixels left so it’s (0,0) coordinates will really be (mImagePaddingLeft, 0) so the image that is drawn on (0, 0) will actually start to be drawn in the designated left padding, which is just what we wanted.
To complete the picture here is the rest of the code to adjust the TextView padding:
1 2 3 4 5 6 7 8 9 10 11 12 | @Override public void setButtonDrawable(Drawable d) { super.setButtonDrawable(d); mImage = d; } @Override public int getCompoundPaddingLeft() { int padding = super.getCompoundPaddingLeft(); int imagePlusPadding = mImage.getIntrinsicWidth() + mImagePaddingRight; return Math.max(padding, imagePlusPadding); } |
As you can see we have an extra pointer for the drawable as we can’t touch it in our parent since it’s protected.
we use getCompungPadding method and not getPaddingLeft and getPaddingRight because after a small research it appears that this is what TextView uses to calculate it’s padding, it was try and error.
However if you want to put your checkbox on the right for some reason, you simply can’t.
the hack will not work because the call to super() paints both the checkbox and the TextView since checkBox itself calls super() to paint the TextView, if we translate the canvas too much to the right we wont see the TextView, since the only way CompoundButton will draw the mButtonImage is from (0,0) and not dependent on anything we just can’t do it (unless you want to try your luck with reflection…)
I did not have a proper solution to this issue but to copy the compound button class from the android source, change whatever is needed (like styles etc since they are internal attributes and classes) for it to compile and then change the drawing method to protected method:
1 2 |
Then edit onMeasure to take into account the whole padding thing, this will allow other extenders to paint however they want and still call onDraw() in the parent without any problem (skip the previous implementation and use your own).
To complete the picture i like using attributes (attrs.xml) and themse to allow the class to define custom attributes in xml.
But this in my next post.


I am trying to do the same thing as you describe and I have problems setting the checkbox image padding. what is the solution for this?
really depandes on where u want the padding.
if you want left side padding i can give u a lead on how to do it, it basically includes overrding the ondraw and using translate t shift the whole paint to the right so when it paints your image it will be wth your padding, and using padding extra ot increase the actual size of the component so the onLayout and onMeasure will function correctly WITH your extra padding.
I did find unfortunatly that the best, really reall best way to add padding is create your own set of checkboxes that injerit from View, copy the code from whatever version of android you whicsh y use and also inherti from the right style and put it as the default one. if i’ll have the time i’ll post an example, this post is not done and should have included some examples on how to do it.
i did discover later on the padding issue on the 9-patch, it was my bad not reading the documentation: http://developer.android.com/guide/topics/graphics/2d-graphics.html the bottom and right edges of the 9-path define the content area that means that whatever is the diff between the left edge and the right edge and the top edge and the bottom edge is the padding that will be associated with the bitmap. you can create padding the size you want, but it’s not customizable in run time