这是我的布局:
我面临的问题是可绘制的复选标记。我如何在文本旁边对齐它们,它们都在按钮的中心?这是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_vertical”拉动文本并将其绘制在一起,但随后文本不再在中心对齐。
答案 0 :(得分:62)
在第一个按钮内设置android:paddingLeft
。这会强制drawableLeft
向paddingLeft
金额向右移动。这是快速/苛刻的解决方案。
使用包含textview和imageview的LinearLayout,而不是使用ButtonView。这是更好的解决方案。它为您提供了更大的选项标记定位灵活性。
使用以下代码替换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>
以下是我尝试此操作时的一些截图。
答案 1 :(得分:31)
如果没有出现不可接受的权衡,这些解决方案都无法正常工作(创建一个带有视图的布局?不是一个好主意)。那么为什么不自己动手呢?这就是我得到的:
首先使用以下内容创建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" />
这适合我。
答案 2 :(得分:15)
这是一个干净简单的方法,没有做任何花哨的事情,以实现一个比图像和文本居中的内容宽得多的按钮的结果。
<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>
答案 3 :(得分:9)
这是我的代码并且工作正常。
<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" />
答案 4 :(得分:8)
在我们的例子中,我们想要使用默认的Button类(继承其各种样式和行为),我们需要能够在代码中创建按钮。此外,在我们的例子中,我们可以有文本,图标(左侧可绘制)或两者兼而有之。
当按钮宽度比wrap_content宽时,目标是将图标和/或文本作为一个组居中。
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);
}
}
答案 5 :(得分:3)
public class DrawableCenterTextView extends TextView {
public DrawableCenterTextView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public DrawableCenterTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DrawableCenterTextView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
Drawable[] drawables = getCompoundDrawables();
if (drawables != null) {
Drawable drawableLeft = drawables[0];
Drawable drawableRight = drawables[2];
if (drawableLeft != null || drawableRight != null) {
float textWidth = getPaint().measureText(getText().toString());
int drawablePadding = getCompoundDrawablePadding();
int drawableWidth = 0;
if (drawableLeft != null)
drawableWidth = drawableLeft.getIntrinsicWidth();
else if (drawableRight != null) {
drawableWidth = drawableRight.getIntrinsicWidth();
}
float bodyWidth = textWidth + drawableWidth + drawablePadding;
canvas.translate((getWidth() - bodyWidth) / 2, 0);
}
}
super.onDraw(canvas);
}
}
答案 6 :(得分:1)
我从@BobDickinson's answer开始,但它不能很好地应对多行。这种方法很好,因为你最终仍然可以正确地重用Button
。
这是一个经过改编的解决方案,如果按钮有多行,也可以使用(请不要问为什么。)
只需展开Button
并在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();
}
答案 7 :(得分:0)
这是另一种解决方案:
<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>
和事件:
LinearLayout btnCall = (LinearLayout) findViewById(R.id.llButton);
btnCall.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
call(runid.Phone);
}
});
答案 8 :(得分:0)
我遇到了同样的问题,我提出了一个不需要XML更改或自定义视图的解决方案。
此代码段检索文本和左/右drawable的宽度,并设置Button的左/右填充,以便只有足够的空间来绘制文本和drawable,并且不再添加填充。 这可以应用于Buttons以及TextViews,它们的超类。
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;
}
}
请注意:
要使用它,只需在TextViewUtils.setPaddingForCompoundDrawableNextToText(button)
或onCreate
上致电onViewCreated()
。
答案 9 :(得分:0)
这个问题有几种解决方案。也许在某些设备上最简单的方法是使用paddingRight
和paddingLeft
将图片和文字相互移动,如下所示。
<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"
/>
这里的问题是在较小的设备上,这种填充会导致不幸的问题,例如:
其他解决方案都是&#34;在布局图像和文本视图中构建按钮&#34;。他们工作,但完全模仿按钮可能会很棘手。我提出了一个解决方案; &#34;在布局中构建一个按钮,一个图像,一个textview,和一个按钮&#34;
这里提供了与我提议的相同的按钮:
<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>
正如您所看到的,该按钮现在位于相对布局中,但它的text和drawableLeft不是按钮的一部分,它们位于按钮顶部的单独布局中。这样,按钮仍然像按钮一样。陷阱是:
答案 10 :(得分:0)
我知道有些晚了,但是如果有人在寻找另一个答案,这是添加图标的另一种方式,而无需使用ViewGroup包裹按钮
<?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>
*需要将textAllCaps设置为false才能使跨范围工作
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
}
}
答案 11 :(得分:0)
您可以使用
head = csv.reader(open(f'{sys.argv[1]}.csv'))
。
https://material.io/develop/android/components/material-button/
它最终允许设置图标重力。
<com.google.android.material.button.MaterialButton/>
答案 12 :(得分:0)
现在默认情况下,Material Button的app:iconGravity
属性可用。但是,Material Button不允许将背景设置为可绘制(RIP渐变)。
我将上面@BobDickinson和@David-Medenjak的答案转换为kotlin,效果很好。
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()
}
}
答案 13 :(得分:-4)
另一个非常糟糕的选择是在按钮的每一侧添加weight="1"
的空白间隔视图。我不知道这会如何影响性能。
<View
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1" />