I got stuck in an odd issue with Android - I want to have a button that looks like this:
|-----------------------------------------------------------------------|
| [icon] <5px> [text text text] |
|-----------------------------------------------------------------------|
and the group ([icon] <5px> [text text text]) should be centred. Note that 5px is used just as a placeholder for any padding you want to have between the icon and the text
I found some answers here that were more or less graviting around either setting a background (which I don't want to do because I have another background) or using the android:drawableLeft property to set the icon.
However looks like the documentation of the setCompoundDrawablesWithIntrinsicBounds method is a bit misleading (see here). It states that the image is placed on the left/right/top/bottom side of the TEXT wich is not true. The icon is placed on the corresponding side of the BUTTON. For example:
Setting the android:drawableLeft property puts the icon on the most left position and gets me this (with gravity CENTER):
|-----------------------------------------------------------------------|
| [icon] [text text text] |
|-----------------------------------------------------------------------|
or this (with gravity LEFT):
|-----------------------------------------------------------------------|
| [icon] [text text text] |
|-----------------------------------------------------------------------|
Both are ugly as hell :(
I found a workaround that looks like this:
public static void applyTextOffset(Button button, int buttonWidth) {
int textWidth = (int) button.getPaint().measureText(button.getText().toString());
int padding = (buttonWidth / 2) - ((textWidth / 2) + Constants.ICON_WIDTH + Constants.ICON_TO_TEXT_PADDING);
button.setPadding(padding, 0, 0, 0);
button.setCompoundDrawablePadding(-padding);
}
And it works more or less but I don't find it to my liking for following reasons:
Isn't there a more elegant solution?
You can use the following Button
subclass to achieve this effect.
package com.phillipcalvin.iconbutton;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.Button;
public class IconButton extends Button {
protected int drawableWidth;
protected DrawablePositions drawablePosition;
protected int iconPadding;
// Cached to prevent allocation during onLayout
Rect bounds;
private enum DrawablePositions {
NONE,
LEFT,
RIGHT
}
public IconButton(Context context) {
super(context);
bounds = new Rect();
}
public IconButton(Context context, AttributeSet attrs) {
super(context, attrs);
bounds = new Rect();
}
public IconButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
bounds = new Rect();
}
public void setIconPadding(int padding) {
iconPadding = padding;
requestLayout();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
Paint textPaint = getPaint();
String text = getText().toString();
textPaint.getTextBounds(text, 0, text.length(), bounds);
int textWidth = bounds.width();
int contentWidth = drawableWidth + iconPadding + textWidth;
int contentLeft = (int)((getWidth() / 2.0) - (contentWidth / 2.0));
setCompoundDrawablePadding(-contentLeft + iconPadding);
switch (drawablePosition) {
case LEFT:
setPadding(contentLeft, 0, 0, 0);
break;
case RIGHT:
setPadding(0, 0, contentLeft, 0);
break;
default:
setPadding(0, 0, 0, 0);
}
}
@Override
public void setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, Drawable bottom) {
super.setCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom);
if (null != left) {
drawableWidth = left.getIntrinsicWidth();
drawablePosition = DrawablePositions.LEFT;
} else if (null != right) {
drawableWidth = right.getIntrinsicWidth();
drawablePosition = DrawablePositions.RIGHT;
} else {
drawablePosition = DrawablePositions.NONE;
}
requestLayout();
}
}
2. Modify your layout to use this new subclass instead of plain Button
:
<com.phillipcalvin.iconbutton.IconButton
android:id="@+id/search"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/search"
android:text="@string/search" />
3. If you want to add padding between the drawable and the text, add the following to your activity's onCreate
:
// Anywhere after setContentView(...)
IconButton button = (IconButton)findViewById(R.id.search);
button.setIconPadding(10);
This subclass also supports drawableRight
. It does not support more than one drawable.
If you want more features, such as the ability to specify the iconPadding
directly in your layout XML, I have a library that supports this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With