Android: Как создать кнопку с изображением и текстом, которые оба центрированы

Я застрял в странной проблеме с Android - я хочу иметь кнопку, которая выглядит так:

 |-----------------------------------------------------------------------|
 |                   [icon] <5px> [text text text]                       |
 |-----------------------------------------------------------------------|

и группа ([icon] [text text text]) должна быть центрирована. Обратите внимание, что 5px используется как заполнитель для любого заполнения, которое вы хотите иметь между значком и текстом

Я нашел здесь некоторые ответы, которые более или менее тяготели либо к настройке фона (что я не хочу делать, потому что у меня есть другой фон), либо к использованию android: drawableLeft свойство для установки значка.

однако похоже, что документация метода setCompoundDrawablesWithIntrinsicbounds немного вводит в заблуждение (см. здесь). В нем говорится, что изображение помещается на левую/правую/верхнюю/нижнюю сторону текста, что неверно. Значок располагается на соответствующей стороне кнопки. Например:

настройка android: свойство drawableLeft помещает значок в самую левую позицию и получает меня это (с центр тяжести):

 |-----------------------------------------------------------------------|
 | [icon]                     [text text text]                           |
 |-----------------------------------------------------------------------|

или это (с гравитацией слева):

 |-----------------------------------------------------------------------|
 | [icon] [text text text]                                               |
 |-----------------------------------------------------------------------|

оба уродливы, как ад : (

Я нашел обходной путь, который выглядит так:

public static void applyTextOffset(Button button, int buttonWidth) {
    int textWidth = (int) button.getPaint().measureText(button.getText().toString());
    int padding = (buttonWidth / 2) - ((textWidth / 2) + Constants.ICON_WIDTH  + Constants.ICON_TO_TEXT_PADDING);
    button.setPadding(padding, 0, 0, 0);
    button.setCompoundDrawablePadding(-padding);
}

и он работает более или менее, но я не нахожу его по своему вкусу по следующим причинам:

  • требуется знать ширину кнопки. С авто-размера кнопки не будут известны до фактического структуры. Google рекомендует использовать прослушиватель, чтобы узнать фактическую ширину после завершения рендеринга, но это чрезвычайно усложняет код.
  • я чувствую, что я беру на себя ответственность за макет от Android layout engine

разве нет более элегантного решения?

4 ответов


вы можете использовать следующие Button подкласс для достижения этого эффекта.

  1. вставьте этот класс в проект и при желании измените имя пакета.
    package com.phillipcalvin.iconbutton;

    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Rect;
    import android.graphics.Paint;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.widget.Button;

    public class IconButton extends Button {
      protected int drawableWidth;
      protected DrawablePositions drawablePosition;
      protected int iconPadding;

      // Cached to prevent allocation during onLayout
      Rect bounds;

      private enum DrawablePositions {
        NONE,
        LEFT,
        RIGHT
      }

      public IconButton(Context context) {
        super(context);
        bounds = new Rect();
      }

      public IconButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        bounds = new Rect();
      }

      public IconButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        bounds = new Rect();
      }

      public void setIconPadding(int padding) {
        iconPadding = padding;
        requestLayout();
      }

      @Override
      protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        Paint textPaint = getPaint();
        String text = getText().toString();
        textPaint.getTextBounds(text, 0, text.length(), bounds);

        int textWidth = bounds.width();
        int contentWidth = drawableWidth + iconPadding + textWidth;

        int contentLeft = (int)((getWidth() / 2.0) - (contentWidth / 2.0));
        setCompoundDrawablePadding(-contentLeft + iconPadding);
        switch (drawablePosition) {
        case LEFT:
          setPadding(contentLeft, 0, 0, 0);
          break;
        case RIGHT:
          setPadding(0, 0, contentLeft, 0);
          break;
        default:
          setPadding(0, 0, 0, 0);
        }
      }

      @Override
      public void setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, Drawable bottom) {
        super.setCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom);
        if (null != left) {
          drawableWidth = left.getIntrinsicWidth();
          drawablePosition = DrawablePositions.LEFT;
        } else if (null != right) {
          drawableWidth = right.getIntrinsicWidth();
          drawablePosition = DrawablePositions.RIGHT;
        } else {
          drawablePosition = DrawablePositions.NONE;
        }
        requestLayout();
      }
    }

2. Измените макет, чтобы использовать этот новый подкласс вместо простого Button:

    <com.phillipcalvin.iconbutton.IconButton
        android:id="@+id/search"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:drawableLeft="@drawable/search"
        android:text="@string/search" />

3. Если вы хотите добавить заполнение между drawable и текстом, добавьте следующее вonCreate:

    // Anywhere after setContentView(...)
    IconButton button = (IconButton)findViewById(R.id.search);
    button.setIconPadding(10);

этот подкласс также поддерживает drawableRight. Он не поддерживает более одного выигрышного.

если вы хотите больше функций, таких как возможность указать iconPadding непосредственно в вашем XML-макете у меня есть библиотека, которая поддерживает это.


вы также можете использовать настраиваемое представление для создания настраиваемой кнопки.

Это может быть где угодно от простого до чрезвычайно сложного.

было бы легко, если все, что вам нужно сделать, это переопределить onDraw(). Это будет сложнее, если вам нужно выложить несколько представлений.


просто способ-написать кнопку в LinearLayout. Что-то вроде этого!--2-->

 <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center">

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:drawableLeft="@drawable/ic_left"
                android:drawablePadding="5dp"
                android:duplicateParentState="true"
                android:text="Button text"/>

        </LinearLayout>

Я пробовал это в eclipse, и он не извергал ошибок:

<Button android:id="@+id/test"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@android:color/transparent" >
<LinearLayout
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1"
    android:gravity="center" 
    android:background="@android:color/transparent" >

    </LinearLayout>
    </Button>

поэтому, я полагаю, вы можете добавить изображение и текстовые представления в макет, сам внутри кнопки и центрировать макет.

С наилучшими пожеланиями.