Android 将drawableLeft与按钮文本对齐

Android 将drawableLeft与按钮文本对齐,android,android-layout,Android,Android Layout,以下是我的布局: 我面临的问题是可提取的复选标记。我如何将它与文本对齐,两个文本都在按钮的中心?以下是XML: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_heigh

以下是我的布局:

我面临的问题是可提取的复选标记。我如何将它与文本对齐,两个文本都在按钮的中心?以下是XML:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".PostAssignmentActivity" >

    <LinearLayout
        style="?android:attr/buttonBarStyle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal" >

        <Button
            style="?android:attr/buttonBarButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:drawableLeft="@drawable/ic_checkmark_holo_light"
            android:text="Post" />

        <Button
            style="?android:attr/buttonBarButtonStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Cancel" />
    </LinearLayout>

</RelativeLayout>

应用android:gravity=“center\u vertical”将文本和可绘制文本拉到一起,但随后文本不再在中心对齐。

解决方案1 在第一个按钮内设置android:paddingLeft。这将迫使
drawable left
通过
padding left
将金额向右移动。这是一个快速的解决方案

解决方案2 不要使用ButtonView,而是使用同时包含textview和imageview的LinearLayout。这是一个更好的解决方案。它使您能够更灵活地定位复选标记

用以下代码替换ButtonView。您需要使用
LinearLayout
TextView
来使用
buttonBarButtonStyle
,以便选择时背景色正确,文本大小正确。您需要为孩子们设置
android:background=“#0000”
,以便只有LinearLayout处理背景颜色

<LinearLayout
    style="?android:attr/buttonBarButtonStyle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:orientation="horizontal" >
    <ImageView 
        style="?android:attr/buttonBarButtonStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:clickable="false"
        android:background="#0000"
        android:src="@drawable/ic_checkmark_holo_light"/>
    <TextView
        style="?android:attr/buttonBarButtonStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:clickable="false"
        android:background="#0000"
        android:text="Done" />
</LinearLayout>

下面是我在尝试这个时拍摄的一些截图


另一个相当老套的替代方法是在按钮的每一侧添加带有
weight=“1”
的空白间隔视图。我不知道这会如何影响性能

    <View
        android:layout_width="0dp"
        android:layout_height="fill_parent"
        android:layout_weight="1" />

这是我的代码,工作非常完美

<Button
    android:id="@+id/button"
    android:layout_width="200dp"
    android:layout_height="50dp"
    android:layout_gravity="center"
    android:background="@drawable/green_btn_selector"
    android:gravity="left|center_vertical"
    android:paddingLeft="50dp"
    android:drawableLeft="@drawable/plus"
    android:drawablePadding="5dp"
    android:text="@string/create_iou"
    android:textColor="@color/white" />

这些解决方案中没有一个能在不提供不可接受的折衷方案的情况下正常工作(创建包含视图的布局?这不是一个好主意)。那为什么不自己动手呢?这就是我得到的:

首先使用以下命令创建一个
attrs.xml

<resources>
    <declare-styleable name="IconButton">
        <attr name="iconSrc" format="reference" />
        <attr name="iconSize" format="dimension" />
        <attr name="iconPadding" format="dimension" />
    </declare-styleable>
</resources>

这允许在新视图中创建具有特定大小、文本填充和图像的图标。视图代码如下所示:

public class IconButton extends Button {
    private Bitmap mIcon;
    private Paint mPaint;
    private Rect mSrcRect;
    private int mIconPadding;
    private int mIconSize;

    public IconButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs);
    }

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

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

    @Override
    protected void onDraw(Canvas canvas) {
        int shift = (mIconSize + mIconPadding) / 2;

        canvas.save();
        canvas.translate(shift, 0);

        super.onDraw(canvas);

        if (mIcon != null) {
            float textWidth = getPaint().measureText((String)getText());
            int left = (int)((getWidth() / 2f) - (textWidth / 2f) - mIconSize - mIconPadding);
            int top = getHeight()/2 - mIconSize/2;

            Rect destRect = new Rect(left, top, left + mIconSize, top + mIconSize);
            canvas.drawBitmap(mIcon, mSrcRect, destRect, mPaint);
        }

        canvas.restore();
    }

    private void init(Context context, AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.IconButton);

        for (int i = 0; i < array.getIndexCount(); ++i) {
            int attr = array.getIndex(i);
            switch (attr) {
                case R.styleable.IconButton_iconSrc:
                    mIcon = drawableToBitmap(array.getDrawable(attr));
                    break;
                case R.styleable.IconButton_iconPadding:
                    mIconPadding = array.getDimensionPixelSize(attr, 0);
                    break;
                case R.styleable.IconButton_iconSize:
                    mIconSize = array.getDimensionPixelSize(attr, 0);
                    break;
                default:
                    break;
            }
        }

        array.recycle();

        //If we didn't supply an icon in the XML
        if(mIcon != null){
            mPaint = new Paint();
            mSrcRect = new Rect(0, 0, mIcon.getWidth(), mIcon.getHeight());
        }
    }

    public static Bitmap drawableToBitmap (Drawable drawable) {
        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable)drawable).getBitmap();
        }

        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    }
}
<com.example.grennis.myapplication.IconButton
    android:layout_width="200dp"
    android:layout_height="64dp"
    android:text="Delete"
    app:iconSrc="@android:drawable/ic_delete"
    app:iconSize="32dp"
    app:iconPadding="6dp" />
公共类图标按钮扩展按钮{
私有位图mIcon;
私人油漆;
私有Rect-mSrcRect;
私密密码学;
私有的微型计算机;
公共图标按钮(上下文、属性集属性、int-defStyle){
超级(上下文、属性、定义样式);
init(上下文,attrs);
}
公共图标按钮(上下文、属性集属性){
超级(上下文,attrs);
init(上下文,attrs);
}
公共图标按钮(上下文){
超级(上下文);
}
@凌驾
受保护的void onDraw(画布){
int-shift=(mIconSize+mIconPadding)/2;
canvas.save();
canvas.translate(shift,0);
super.onDraw(帆布);
if(mIcon!=null){
float textWidth=getPaint().measureText((字符串)getText());
int left=(int)((getWidth()/2f)-(textWidth/2f)-mIconSize-mIconPadding);
int top=getHeight()/2-mIconSize/2;
Rect DESTECT=新的Rect(左、上、左+微尺寸、上+微尺寸);
drawBitmap(mIcon、mSrcRect、desect、mPaint);
}
canvas.restore();
}
私有void init(上下文上下文、属性集属性){
TypedArray数组=context.obtainStyledAttributes(attrs,R.styleable.IconButton);
对于(int i=0;i
然后它可以这样使用:

public class IconButton extends Button {
    private Bitmap mIcon;
    private Paint mPaint;
    private Rect mSrcRect;
    private int mIconPadding;
    private int mIconSize;

    public IconButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs);
    }

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

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

    @Override
    protected void onDraw(Canvas canvas) {
        int shift = (mIconSize + mIconPadding) / 2;

        canvas.save();
        canvas.translate(shift, 0);

        super.onDraw(canvas);

        if (mIcon != null) {
            float textWidth = getPaint().measureText((String)getText());
            int left = (int)((getWidth() / 2f) - (textWidth / 2f) - mIconSize - mIconPadding);
            int top = getHeight()/2 - mIconSize/2;

            Rect destRect = new Rect(left, top, left + mIconSize, top + mIconSize);
            canvas.drawBitmap(mIcon, mSrcRect, destRect, mPaint);
        }

        canvas.restore();
    }

    private void init(Context context, AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.IconButton);

        for (int i = 0; i < array.getIndexCount(); ++i) {
            int attr = array.getIndex(i);
            switch (attr) {
                case R.styleable.IconButton_iconSrc:
                    mIcon = drawableToBitmap(array.getDrawable(attr));
                    break;
                case R.styleable.IconButton_iconPadding:
                    mIconPadding = array.getDimensionPixelSize(attr, 0);
                    break;
                case R.styleable.IconButton_iconSize:
                    mIconSize = array.getDimensionPixelSize(attr, 0);
                    break;
                default:
                    break;
            }
        }

        array.recycle();

        //If we didn't supply an icon in the XML
        if(mIcon != null){
            mPaint = new Paint();
            mSrcRect = new Rect(0, 0, mIcon.getWidth(), mIcon.getHeight());
        }
    }

    public static Bitmap drawableToBitmap (Drawable drawable) {
        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable)drawable).getBitmap();
        }

        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    }
}
<com.example.grennis.myapplication.IconButton
    android:layout_width="200dp"
    android:layout_height="64dp"
    android:text="Delete"
    app:iconSrc="@android:drawable/ic_delete"
    app:iconSize="32dp"
    app:iconPadding="6dp" />


这对我来说很有用。

这里有一个简单明了的方法,不需要做任何花哨的事情,就可以实现一个按钮的效果,这个按钮比图片和文本居中的内容宽得多

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clickable="true"
    android:background="@drawable/button_background_selector">

    <Button
        android:layout_centerInParent="true"
        android:gravity="center"
        android:duplicateParentState="true"
        android:layout_width="wrap_content"
        android:text="New User"
        android:textSize="15sp"
        android:id="@android:id/button1"
        android:textColor="@android:color/white"
        android:drawablePadding="6dp"
        android:drawableLeft="@drawable/add_round_border_32x32"
        android:layout_height="64dp" />

</RelativeLayout>


这里是另一个解决方案:

     <LinearLayout
        android:id="@+id/llButton"
        android:layout_width="match_parent"
        style="@style/button_celeste"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            style="@style/button_celeste"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:drawablePadding="10dp"
            android:clickable="false"
            android:drawableLeft="@drawable/icon_phone"
            android:text="@string/call_runid"/>
    </LinearLayout>

在我们的例子中,我们希望使用默认按钮类(继承其各种样式和行为),并且需要能够在代码中创建按钮。此外,在我们的例子中,我们可以有文本,一个图标(左可绘制),或两者兼有

目标是当按钮宽度大于包裹内容时,将图标和/或文本作为一个组居中

public class CenteredButton extends Button
{
    public CenteredButton(Context context, AttributeSet attrs, int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);

        // We always want our icon and/or text grouped and centered.  We have to left align the text to
        // the (possible) left drawable in order to then be able to center them in our onDraw() below.
        //
        setGravity(Gravity.LEFT|Gravity.CENTER_VERTICAL);
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        // We want the icon and/or text grouped together and centered as a group.

        // We need to accommodate any existing padding
        //
        float buttonContentWidth = getWidth() - getPaddingLeft() - getPaddingRight();

        // In later versions of Android, an "all caps" transform is applied to buttons.  We need to get
        // the transformed text in order to measure it.
        //
        TransformationMethod method = getTransformationMethod();
        String buttonText = ((method != null) ? method.getTransformation(getText(), this) : getText()).toString();
        float textWidth = getPaint().measureText(buttonText);

        // Compute left drawable width, if any
        //
        Drawable[] drawables = getCompoundDrawables();
        Drawable drawableLeft = drawables[0];
        int drawableWidth = (drawableLeft != null) ? drawableLeft.getIntrinsicWidth() : 0;

        // We only count the drawable padding if there is both an icon and text
        //
        int drawablePadding = ((textWidth > 0) && (drawableLeft != null)) ? getCompoundDrawablePadding() : 0;

        // Adjust contents to center
        //
        float bodyWidth = textWidth + drawableWidth + drawablePadding;
        canvas.translate((buttonContentWidth - bodyWidth) / 2, 0);

        super.onDraw(canvas);
    }
}
我一开始是这样,但它不能很好地处理多行。这种方法很好,因为您仍然可以使用一个可以正确重用的
按钮

如果按钮有多行(请不要问为什么),这是一个经过调整的解决方案

只需扩展
按钮
并在
onDraw
中使用以下命令,
getLineRight()
用于查找每行的实际长度

@Override
protected void onDraw(Canvas canvas) {
    // We want the icon and/or text grouped together and centered as a group.
    // We need to accommodate any existing padding
    final float buttonContentWidth = getWidth() - getPaddingLeft() - getPaddingRight();

    float textWidth = 0f;
    final Layout layout = getLayout();
    if (layout != null) {
        for (int i = 0; i < layout.getLineCount(); i++) {
            textWidth = Math.max(textWidth, layout.getLineRight(i));
        }
    }

    // Compute left drawable width, if any
    Drawable[] drawables = getCompoundDrawables();
    Drawable drawableLeft = drawables[0];
    int drawableWidth = (drawableLeft != null) ? drawableLeft.getIntrinsicWidth() : 0;

    // We only count the drawable padding if there is both an icon and text
    int drawablePadding = ((textWidth > 0) && (drawableLeft != null)) ? getCompoundDrawablePadding() : 0;

    // Adjust contents to center
    float bodyWidth = textWidth + drawableWidth + drawablePadding;

    canvas.save();
    canvas.translate((buttonContentWidth - bodyWidth) / 2, 0);
    super.onDraw(canvas);
    canvas.restore();
}
@覆盖
受保护的void onDraw(画布){
//我们希望图标和/或文本组合在一起,并作为一个整体居中
public class TextViewUtils {
    private static final int[] LEFT_RIGHT_DRAWABLES = new int[]{0, 2};

    public static void setPaddingForCompoundDrawableNextToText(final TextView textView) {
        ViewTreeObserver vto = textView.getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                shinkRoomForHorizontalSpace(textView);
            }
        });

    }

    private static void shinkRoomForHorizontalSpace(TextView textView) {
        int textWidth = getTextWidth(textView);
        int sideCompoundDrawablesWidth = getSideCompoundDrawablesWidth(textView);
        int contentWidth = textWidth + sideCompoundDrawablesWidth;
        int innerWidth = getInnerWidth(textView);
        int totalPadding = innerWidth - contentWidth;
        textView.setPadding(totalPadding / 2, 0, totalPadding / 2, 0);
    }

    private static int getTextWidth(TextView textView) {
        String text = textView.getText().toString();
        Paint textPaint = textView.getPaint();
        Rect bounds = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), bounds);
        return bounds.width();
    }

    private static int getSideCompoundDrawablesWidth(TextView textView) {
        int sideCompoundDrawablesWidth = 0;
        Drawable[] drawables = textView.getCompoundDrawables();
        for (int drawableIndex : LEFT_RIGHT_DRAWABLES) {
            Drawable drawable = drawables[drawableIndex];
            if (drawable == null)
                continue;
            int width = drawable.getBounds().width();
            sideCompoundDrawablesWidth += width;
        }
        return sideCompoundDrawablesWidth;
    }

    private static int getInnerWidth(TextView textView) {
        Rect backgroundPadding = new Rect();
        textView.getBackground().getPadding(backgroundPadding);
        return textView.getWidth() - backgroundPadding.left - backgroundPadding.right;
    }
}
<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginStart="32dp"
    android:layout_marginEnd="32dp"
    android:layout_marginTop="16dp"
    android:text="@string/scan_qr_code"
    android:textColor="@color/colorPrimary"
    android:drawableLeft="@drawable/ic_camera"
    android:paddingRight="90dp"
    android:paddingLeft="90dp"
    android:gravity="center"
    />
<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginStart="32dp"
    android:layout_marginEnd="32dp"
    android:layout_marginTop="16dp"
    android:gravity="center"
    >
    <Button
        android:id="@+id/scanQR"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/white_bg_button"
        />
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_centerInParent="true"
        android:gravity="center"
        android:elevation="10dp"
        >
        <ImageView
            android:id="@+id/scanImage"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="8dp"
            android:src="@drawable/ic_camera"
            />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textAppearance="@style/Base.TextAppearance.AppCompat.Button"
            android:text="@string/scan_qr_code"
            android:textColor="@color/colorPrimary"
            />
    </LinearLayout>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btnCamera"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Click!"
        android:textAllCaps="false"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val buttonLabelBuilder = SpannableStringBuilder(btnCamera.text)
        val iconDrawable = AppCompatResources.getDrawable(this, R.drawable.ic_camera)
        iconDrawable?.setBounds(0, 0, btnCamera.lineHeight, btnCamera.lineHeight)
        val imageSpan = ImageSpan(iconDrawable, ImageSpan.ALIGN_BOTTOM)

        buttonLabelBuilder.insert(0, "i ")
        buttonLabelBuilder.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

        btnCamera.text = buttonLabelBuilder
    }
}
 <com.google.android.material.button.MaterialButton
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:layout_marginStart="16dp"
        android:layout_marginEnd="16dp"
        android:gravity="center"
        android:text="Awesome button"
        app:icon="@drawable/your_icon"
        app:iconGravity="textStart" />
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.view.Gravity
import androidx.appcompat.widget.AppCompatButton
import kotlin.math.max

class CenteredButton @JvmOverloads constructor(
  context: Context,
  attrs: AttributeSet? = null,
  defStyle: Int = R.attr.buttonStyle
) : AppCompatButton(context, attrs, defStyle) {

  init {
    gravity = Gravity.LEFT or Gravity.CENTER_VERTICAL
  }

  override fun onDraw(canvas: Canvas) {
    val buttonContentWidth = (width - paddingLeft - paddingRight).toFloat()

    var textWidth = 0f
    layout?.let {
      for (i in 0 until layout.lineCount) {
        textWidth = max(textWidth, layout.getLineRight(i))
      }
    }

    val drawableLeft = compoundDrawables[0]
    val drawableWidth = drawableLeft?.intrinsicWidth ?: 0
    val drawablePadding = if (textWidth > 0 && drawableLeft != null) compoundDrawablePadding else 0

    val bodyWidth = textWidth + drawableWidth.toFloat() + drawablePadding.toFloat()

    canvas.save()
    canvas.translate((buttonContentWidth - bodyWidth) / 2, 0f)
    super.onDraw(canvas)
    canvas.restore()
  }
}