Выравнивание drawableLeft с текстом кнопки
Вот мой макет:
![enter image description here]()
Проблема, с которой я столкнулся, - с помощью флажка. Как бы я начал выравнивать его рядом с текстом, оба они были сосредоточены внутри кнопки? Вот 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>
Применение андроида: gravity = "center_vertical" вытягивает текст и извлекается вместе, но затем текст больше не выравнивается по центру.
Ответы
Ответ 1
Решение 1
Установите android:paddingLeft
внутри вашей первой кнопки. Это приведет к тому, что значение drawableLeft
на paddingLeft
будет равно справа. Это быстрое/хакерское решение.
Решение 2
Вместо использования ButtonView используйте 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>
Вот несколько скриншотов, которые я сделал, пытаясь понять это.
![enter image description here]()
![enter image description here]()
![enter image description here]()
Ответ 2
Ни одно из этих решений не работало корректно, не представляя неприемлемых компромиссов (создать макет с представлениями в нем? Не очень хорошая идея). Так почему же не сворачивать свои собственные? Это то, что я получил:
![enter image description here]()
Сначала создайте 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" />
Это работает для меня.
Ответ 3
Вот простой простой способ, не делая ничего необычного, для достижения результатов наличия кнопки, которая намного шире, чем содержимое с изображением и текстом, которые центрированы.
<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>
![enter image description here]()
Ответ 4
В нашем случае мы хотели использовать класс 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
Вот мой код и работающий отлично.
<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" />
Ответ 6
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);
}
}
Ответ 7
Я начал с ответа @BobDickinson, но он не очень хорошо справился с несколькими строками. Этот подход хорош, потому что вы все равно получаете 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();
}
Ответ 8
Вот еще одно решение:
<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);
}
});
Ответ 9
У меня была такая же проблема, и я придумал решение, которое не требует изменений XML или пользовательских представлений.
Этот фрагмент кода извлекает ширину текста и левые/правые рисунки, а также устанавливает пустую кнопку влево/вправо, поэтому будет достаточно места для рисования текста и чертежей, и больше не будет добавлено дополнение.
Это можно применить к кнопкам, а также к 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()
.
Ответ 10
Существует несколько решений этой проблемы. Возможно, самым простым на некоторых устройствах является использование 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"
/>
![Использование Padding может работать]()
Проблема здесь в меньших устройствах, это дополнение может привести к таким неприятным проблемам:
![введите описание изображения здесь]()
Другие решения - это всякая версия "выстроить кнопку из макета изображения и текстового вида". Они работают, но полностью эмуляция кнопки может быть сложной. Я предлагаю еще одно решение; "создайте кнопку из макета изображения, текстового поля и кнопки"
Здесь та же самая кнопка, которую я предлагаю:
<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>
Как вы можете видеть, кнопка теперь находится в относительном макете, но текст и drawableLeft не являются частью кнопки, они находятся в отдельном макете, который помещается поверх кнопки. При этом кнопка по-прежнему действует как кнопка. Захваты:
- Внутренняя компоновка требует повышения для новых версий Android. Сама кнопка имеет возвышение больше, чем ImageView и TextView, поэтому, хотя они определены после кнопки, они все равно будут "ниже" на высоте и будут невидимыми. Установка "android: elevation" на 10 разрешает это.
- TextAppearance TextView должен быть установлен таким образом, чтобы он имел тот же вид, что и в кнопке.
Ответ 11
Еще одна довольно хакерская альтернатива заключается в том, чтобы добавлять пустые проставки с weight="1"
с каждой стороны кнопок. Я не знаю, как это повлияет на производительность.
<View
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="1" />