将drawableLeft与button的文本alignment

这是我的布局:

在这里输入图像说明

我面临的问题是可绘制的复选标记。 我将如何去alignment它旁边的文本,他们都集中在button? 这里是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”将文本和绘图拉到一起,但是文本不再在中心alignment。

解决scheme1

在第一个button中设置android:paddingLeft 。 这将强制drawableLeft通过paddingLeft数量在右边。 这是快/哈克解决scheme。

解决scheme2

使用包含textview和imageview的LinearLayout,而不是使用ButtonView。 这是一个更好的解决scheme 。 它使您在复选标记的定位方面有更大的灵活性。

用下面的代码replace你的ButtonView。 您需要使用LinearLayoutTextView来使用buttonBarButtonStyle以便背景颜色在select上正确,并且文本大小是正确的。 您需要为子项设置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> 

以下是我在尝试此操作时截取的一些屏幕截图。

在这里输入图像说明在这里输入图像说明在这里输入图像说明

这些解决scheme没有一个能够正确地工作,不会出现不可接受的折衷(创build一个包含视图的布局?不是一个好主意)。 那么为什么不把自己推出? 这是我得到的:

在这里输入图像说明

首先用这个创build一个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> 

这允许创build一个具有特定大小的图标,从文本填充,在我们的新视图中的图像。 视图代码如下所示:

 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" /> 

这对我有用。

这是一个干净简单的方法,不用做任何事情,就可以实现一个button的结果,这个button比以图片和文本为中心的内容要宽得多。

 <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> 

在这里输入图像说明

在我们的例子中,我们想使用默认的Button类(inheritance它的各种样式和行为),我们需要能够在代码中创buildbutton。 此外,在我们的案例中,我们可以有文字,图标(左边的可绘制)或两者。

目标是当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); } } 

这是我的代码和完美的工作。

 <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" /> 
 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); } } 

我从@鲍勃·迪金森(BobDickinson)的回答开始,但并没有很好地处理多行。 这个方法很好,因为你最终还是会得到一个可以正确重用的Button

这是一个改进的解决scheme,如果button有多行,也可以工作(请不要问为什么。)

只需要扩展Button并在onDraw使用onDrawgetLineRight()就可以用来查看每一行的实际长度。

 @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(); } 

这是另一个解决scheme:

  <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); } }); 

我有同样的问题,我想出了一个不需要XML更改或自定义视图的解决scheme。

这个代码片段检索文本的宽度和左/右的drawables,并设置Button的左/右填充,所以只有足够的空间来绘制文本和drawables,不会再添加填充。 这可以应用于button以及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; } } 

请注意:

  • 它实际上仍然留下比需要更多的空间(足够我的目的,但你可能会寻找错误)
  • 它会覆盖所有左/右填充。 我想这不难解决。

要使用它,只需在onCreateonViewCreated()上调用TextViewUtils.setPaddingForCompoundDrawableNextToText(button) onViewCreated()

有几个解决这个问题的方法。 也许最简单的一些设备是使用paddingRightpaddingLeft来移动图像和文本相邻的如下。

原始button

 <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" /> 

使用填充可以工作

这里的问题是在较小的设备上,这种填充会导致不幸的问题,例如: 在这里输入图像说明

其他解决scheme都是“从布局中构buildbutton的图像和文本视图”的一些版本。 他们工作,但完全模仿button可能会很棘手。 我提出一个解决scheme; “从一个图像,一个文本视图和一个button的布局中build立一个button

下面是我提出的同样的button:

 <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> 

正如你所看到的,button现在在一个相对的布局中,但是它的文本和drawableLeft不是button的一部分,它们是放置在button顶部的一个单独的布局。 有了这个,button仍然像一个button。 陷阱是:

  1. 内部布局需要升级到较新版本的Android。 button本身具有比ImageView和TextView更高的仰angular,所以即使它们是 Button 之后定义的,它们仍然会在高度上“下方”并且不可见。 设置'android:elevation'为10解决了这个问题。
  2. 必须设置TextView的外观,使其具有与button中相同的外观。

另一个相当不好的select是在button的每一侧添加weight="1"空白间隔视图。 我不知道这将如何影响性能。

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