28

I am generating RadioButtons dynamically with

RadioButton radioButton=new RadioButton(context);  

LayoutParams layoutParams=new LayoutParams(radioWidth,radioHeight);
layoutParams.gravity=Gravity.CENTER;

radioButton.setLayoutParams(layoutParams);
radioButton.setGravity(Gravity.CENTER);

BitmapDrawable bitmap = ((BitmapDrawable)drawableResource);
bitmap.setGravity(Gravity.CENTER);

radioButton.setBackgroundDrawable(getResources().getDrawable(R.drawable.itabs_radio));
radioButton.setButtonDrawable(bitmap);

as you can see I am desperately trying to set gravity of button drawable to center, but without a reason its always center and left aligned, heres the reason- the default style of android radio button:

<style name="Widget.CompoundButton">
<item name="android:focusable">true</item> 
<item name="android:clickable">true</item>
<item name="android:textAppearance">?android:attr/textAppearance</item> 
<item name="android:textColor">?android:attr/textColorPrimaryDisableOnly</item> 
<item name="android:gravity">center_vertical|left</item> 
</style>

<style name="Widget.CompoundButton.RadioButton">
<item name="android:background">@android:drawable/btn_radio_label_background</item> 
<item name="android:button">@android:drawable/btn_radio</item> 
</style>

Is there any way I can align button drawable to center?

Shardul
  • 786
  • 3
  • 11
  • 20

7 Answers7

68

According to CompoundButton.onDraw() source code it's always left-aligned.

(Note the line buttonDrawable.setBounds(0, y, buttonDrawable.getIntrinsicWidth(), y + height);)

You will have to derive a new class from RadioButton and override onDraw().

EXAMPLE ADDED LATER:

Ok, so here's what you do. Firstly, here's a layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<org.test.TestProj.RadioButtonCenter
    android:id="@+id/myview"
    android:layout_width="fill_parent" 
    android:layout_height="100dp" 
    android:layout_centerInParent="true"
    android:text="Button test"
    />
</RelativeLayout>

Secondly here's the custom-drawing RadioButtonCenter:

package org.test.TestProj;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.Gravity;
import android.widget.RadioButton;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;

public class RadioButtonCenter extends RadioButton {

    public RadioButtonCenter(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CompoundButton, 0, 0);
        buttonDrawable = a.getDrawable(1);
        setButtonDrawable(android.R.color.transparent);
    }
    Drawable buttonDrawable;


     @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);

            if (buttonDrawable != null) {
                buttonDrawable.setState(getDrawableState());
                final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
                final int height = buttonDrawable.getIntrinsicHeight();

                int y = 0;

                switch (verticalGravity) {
                    case Gravity.BOTTOM:
                        y = getHeight() - height;
                        break;
                    case Gravity.CENTER_VERTICAL:
                        y = (getHeight() - height) / 2;
                        break;
                }

            int buttonWidth = buttonDrawable.getIntrinsicWidth();
            int buttonLeft = (getWidth() - buttonWidth) / 2;
            buttonDrawable.setBounds(buttonLeft, y, buttonLeft+buttonWidth, y + height);
                buttonDrawable.draw(canvas);
            }
        }   
}

Finally, here's an attrs.xml file you need to put in res/values so the code can get at platform-defined attributes.

<?xml version="1.0" encoding="utf-8"?>
<resources>    
     <declare-styleable name="CompoundButton">
        <attr name="android:button" />
    </declare-styleable>
</resources>
Reuben Scratton
  • 38,595
  • 9
  • 77
  • 86
  • Can you provide a sample snippet ? I'm not able to figure it out! – Shardul Dec 10 '10 at 11:33
  • Yes its really worth of 1000s. But I am not accepting it since I approached it with according to me a simpler way. No disrespect. Thanx again! – Shardul Dec 10 '10 at 12:54
  • 8
    Thanks a bunch Shardul. Allow me to point out that your version has a custom constructor, does not use attributes properly, and therefore cannot be used as a drop-in replacement for RadioButton. Never mind. That's the last time I ever go to that kind of trouble. – Reuben Scratton Dec 10 '10 at 13:02
  • 2
    Also, allow me to point out that I *did* answer your question: you subclass RadioButton. – Reuben Scratton Dec 10 '10 at 13:03
  • 3
    instead of android.R.id.empty , define transparent color and use R.color.transparent instead, otherwise,InflateException get thrown but the real cause of it is `android.content.res.Resources$NotFoundException: File from drawable resource ID #0x1020004` – Win Myo Htet May 25 '12 at 15:30
  • Thanks Theo, but what do you mean by "define transparent color"?? – Ethan Allen Jul 19 '12 at 00:30
  • 1
    `setButtonDrawable(android.R.color.transparent);` – Freewind Nov 29 '12 at 03:51
  • Hi @ReubenScratton , i tried your solution , but i am getting error com.packagename.RadioButtonCenter fail to instantiate , may I know what is left ? – abc cba Feb 06 '14 at 17:00
  • @ReubenScratton how to change the images ? – abc cba Feb 10 '14 at 15:48
  • is there any way to get drawable and text both centre aligned of radio button? please check here http://stackoverflow.com/q/26710100/2624806 – CoDe Nov 03 '14 at 08:55
  • I did find that in newer Targets, the call to super.onDraw() actually causes issues, and draws the image left justified, before your ondraw executes... this did not happen in lower targets, but started in higher targets, so food for thought. – Speckpgh Nov 17 '14 at 07:49
  • Excellent answer and in fact the only solution for this problem that works properly. It is a damn shame that you have to do things like this for such a simple task, though. – AlBirdie Feb 23 '15 at 13:11
  • 6
    When using `compile 'com.android.support:design:23.0.1'` `a.getDrawable(1);` returns null. Using `a.getDrawable(R.styleable.CompoundButton_android_button);` works for me though. – jayeffkay Oct 05 '15 at 09:32
  • When I set `android:layout_height="wrap_content"` on the button the button image is croped. – LuisComS Oct 08 '15 at 14:46
  • @ReubenScratton, how to use it to align text and left drawable to right of radiobutton – Reprator Mar 02 '16 at 06:26
  • on android 7.0. It not highlight when choose tab diff. – Nguyễn Trung Hiếu May 12 '17 at 12:30
  • 1
    I have filled a bug report with the solution https://issuetracker.google.com/u/1/issues/125138960 If you think this will be fixed, push google to fix it, thx – Sulfkain Feb 21 '19 at 11:06
8

Simple solution, you can add a background to RadioButton, or set background="@null", .

<RadioButton
                android:id="@+id/cp_rd_btn"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="@null"/>

updated:

<RadioGroup
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal" >

                <RadioButton
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:background="@null"
                    android:button="@null"
                    android:drawableTop="@drawable/account_coolme_selector"
                    android:gravity="center" />

                <RadioButton
                    android:layout_width="0dp"
                    android:layout_height="fill_parent"
                    android:layout_weight="1"
                    android:background="@null"
                    android:button="@null"
                    android:drawableTop="@drawable/account_qq_selector"
                    android:gravity="center"
                    />
            </RadioGroup>
hoot
  • 1,215
  • 14
  • 15
  • this doesn't resolve the problem - the button of the radio will still be aligned to the left – ılǝ Jun 12 '14 at 07:01
  • hoot, thanks for the effort - but still no. The task is to align the `button` object, you are using drawableTop. See the accepted answer - you need to implement a custom RadioButton to achieve this. – ılǝ Jun 13 '14 at 07:28
  • 1
    Good, it's definitely an hack but it works. I had to have a radio button only with an image no text and the image needed to be centered. I just made textsize = 0 and got exactly what I needed +1 – Fabio Marcolini Feb 03 '16 at 15:57
3

Based on @hoot answers, I had customised it to make both text and drawable to the center without using attars,

class RadioButtonCenter(context: Context, attrs: AttributeSet) : RadioButton(context, attrs) {
internal var buttonDrawable: Drawable? = null


init {
    buttonDrawable = CompoundButtonCompat.getButtonDrawable(this@RadioButtonCenter)

}

override fun onDraw(canvas: Canvas) {
    val iconHeight = buttonDrawable!!.intrinsicHeight
    val buttonWidth = buttonDrawable!!.intrinsicWidth

    val totalWidth =
        buttonWidth + paint.measureText(text.toString()) + paddingLeft + paddingRight + compoundDrawablePadding
    if (totalWidth >= width) {
        super.onDraw(canvas)
    } else {
        setButtonDrawable(android.R.color.transparent)

        val availableSpace = ((width - totalWidth) / 2).toInt()

        buttonDrawable!!.state = drawableState
        val height = height
        var yTop = 0
        val verticalGravity = gravity and Gravity.VERTICAL_GRAVITY_MASK
        when (verticalGravity) {
            Gravity.BOTTOM -> yTop = height - iconHeight
            Gravity.CENTER_VERTICAL -> yTop = (height - iconHeight) / 2
        }
        var rightWidth = availableSpace + buttonWidth

        buttonDrawable!!.setBounds(availableSpace, yTop, rightWidth, yTop + iconHeight)
        buttonDrawable!!.draw(canvas)

        rightWidth += compoundDrawablePadding

        val yPos = (height / 2 - (paint.descent() + paint.ascent()) / 2) as Float
        canvas.drawText(
            text.toString(),
            (rightWidth).toFloat(),
            yPos,
            paint
        )
    }
}

}

Reprator
  • 2,859
  • 2
  • 32
  • 55
2

Based on @Reprator answers.

JAVA version:

public class RadioButtonCentered extends AppCompatRadioButton {

  private Drawable buttonDrawable;


  public RadioButtonCentered(Context context) {
    super(context);
  }

  public RadioButtonCentered(Context context, AttributeSet attrs) {
    super(context, attrs);
  }

  public RadioButtonCentered(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
  }




  @Override
  protected void onDraw(Canvas canvas) {
      if (buttonDrawable != null) {
        int iconHeight = buttonDrawable.getIntrinsicHeight();
        int buttonWidth = buttonDrawable.getIntrinsicWidth();
        int width = getWidth();
        float totalWidth = buttonWidth + getPaint().measureText(getText().toString()) + getPaddingLeft() + getPaddingRight() + getCompoundDrawablePadding();

        if (totalWidth >= width) { super.onDraw(canvas); }
        else {
            int yTop = 0;
            int height = getHeight();
            int availableSpace = (int) ((width - totalWidth) / 2);
            int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;
            int rightWidth = availableSpace + buttonWidth;

            switch (verticalGravity) {
                case Gravity.BOTTOM:
                    yTop = height - iconHeight;
                    break;
                case Gravity.CENTER_VERTICAL:
                    yTop = (height - iconHeight) / 2;
                    break;
            }

            setButtonDrawable(android.R.color.transparent);
            buttonDrawable.setState(getDrawableState());
            buttonDrawable.setBounds(availableSpace, yTop, rightWidth, yTop + iconHeight);
            buttonDrawable.draw(canvas);

            float yPos = (height / 2 - (getPaint().descent() + getPaint().ascent()) / 2);

            canvas.drawText(getText().toString(), ((float) (rightWidth + getCompoundDrawablePadding())), yPos, getPaint());
        }
    } else {buttonDrawable = CompoundButtonCompat.getButtonDrawable(this); invalidate();}
  }
}
DJ. XYZ
  • 99
  • 1
  • 9
1

I also think this sounds like a bug since it's always left-aligned. In my case I solved the issue by setting android:minWidth="0dp" and android:layout_width="wrap_content", since Material components had set the android:minWidth to a width larger than the drawable width. If the RadioButton needs to be centered it can then be added to a container and thus no custom view needs to be implemented.

Here's an example of how it could look:

<FrameLayout
    android:layout_width="200dp"
    android:layout_height="200dp">

    <RadioButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:clickable="false"
        android:minWidth="0dp" />

 </FrameLayout>

However, be aware that the minimum width was set there for a reason, Material design used ?attr/minTouchTargetSize. So if you do like above, the container should maybe also be touchable.

Anigif
  • 872
  • 8
  • 17
0

<radiogroup android:paddingLeft = "20dp" android:background="@color/gray">

Basically - I have a horizontally aligned radio group, and by expanding the background color to the left 20dp (or whatever 1/2 of your width of radio button) it appears as if it's centered.

Anna Billstrom
  • 2,482
  • 25
  • 33
0

you need foreground. not background. see args for layout and set em programmatically:

<RadioButton>
    ...
    android:button="@null"
    android:foreground="@drawable/your_selector_for_center_drawable"
    android:background="@drawable/your_selector_for_background_drawable"
    android:foregroundGravity="center"
</RadioButton>