56

これが私のレイアウトです:

ここに画像の説明を入力してください

私が直面している問題は、描画可能なチェックマークです。どちらもボタンの中央に配置して、テキストの横に配置するにはどうすればよいですか?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"を適用すると、テキストと描画可能オブジェクトが一緒にプルされますが、テキストは中央に配置されなくなります。

4

14 に答える 14

76

解決策1

android:paddingLeft最初のボタンの内側に設定します。drawableLeftこれにより、 byのpaddingLeft量が右に強制されます。これは高速でハッキーなソリューションです。

解決策2

ButtonViewを使用する代わりに、textviewとimageviewの両方を含むLinearLayoutを使用してください。これはより良い解決策です。これにより、チェックマークの配置をより柔軟に行うことができます。

ButtonViewを次のコードに置き換えます。選択時に背景色が正しく、テキストサイズが正しくなるように、LinearLayoutTextViewを使用する必要があります。LinearLayoutのみが背景色を処理するように、子buttonBarButtonStyleを設定する必要があります。android:background="#0000"

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

これを試してみたときに撮ったスクリーンショットをいくつか示します。

ここに画像の説明を入力してください ここに画像の説明を入力してください ここに画像の説明を入力してください

于 2013-02-03T01:15:41.513 に答える
36

これらのソリューションはいずれも、許容できないトレードオフを提示せずに正しく機能しませんでした(ビューを含むレイアウトを作成しますか?お勧めできません)。では、自分でロールしてみませんか?これは私が得たものです:

ここに画像の説明を入力してください

まず、これを使用してを作成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" />

これは私のために働きます。

于 2015-03-23T22:01:57.073 に答える
19

これは、画像とテキストが中央に配置されたコンテンツよりもはるかに幅の広いボタンを持つという結果を達成するために、特別なことを何もせずに、クリーンで簡単な方法です。

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

ここに画像の説明を入力してください

于 2015-04-02T19:35:01.667 に答える
17

を使用できます <com.google.android.material.button.MaterialButton/>
https://material.io/develop/android/components/material-button/

最後に、アイコンの重力を設定できます。

 <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" />
于 2019-03-18T15:47:36.050 に答える
11

この例では、デフォルトの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);
    }
}
于 2016-05-29T21:22:01.747 に答える
8

これが私のコードで、完璧に機能しています。

<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" />
于 2014-05-08T06:41:13.847 に答える
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);
    }
}
于 2013-12-09T13:08:52.853 に答える
3

これは、プロパティでデフォルトでマテリアルボタンで使用できるようになりました。app:iconGravityただし、マテリアルボタンでは、背景を描画可能(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()
  }
}
于 2019-10-22T17:52:10.423 に答える
2

少し遅れていることは承知していますが、別の答えを探している人がいる場合は、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
    }
}

imagespan付きボタン

于 2018-08-23T04:10:07.673 に答える
1

@BobDickinsonの回答から始めましたが、複数の行にうまく対応できませんでした。Buttonあなたはまだ適切に再利用できるものになってしまうので、アプローチは良いです。

これは、ボタンに複数の行がある場合にも機能する適応ソリューションです(理由は聞かないでください)。

を拡張Buttonして使用するだけで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();
}
于 2016-09-19T08:18:38.927 に答える
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);
        }
    });
于 2015-08-26T06:09:52.200 に答える
0

同じ問題が発生し、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)てください。onCreateonViewCreated()

于 2016-10-03T13:25:24.117 に答える
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"
    />

パディングを使用すると機能します

ここでの問題は、このパディングが次のような不幸な問題を引き起こす可能性がある小さなデバイスにあります。 ここに画像の説明を入力してください

他の解決策はすべて、「画像とテキストビューのレイアウトからボタンを作成する」のいくつかのバージョンです。それらは機能しますが、ボタンを完全にエミュレートするのは難しい場合があります。もう1つの解決策を提案します。「レイアウト、画像、テキストビュー、およびボタンからボタンを作成します」

これが私が提案したのと同じボタンです。

<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はボタンの一部ではなく、ボタンの上に配置された別のレイアウトになっています。これにより、ボタンはボタンのように機能します。落とし穴は次のとおりです。

  1. Androidの新しいバージョンでは、内側のレイアウトに高さが必要です。ボタン自体の高さはImageViewおよびTextViewよりも大きいため、ボタンのに定義されていても、ボタンの高さはボタンの「下」にあり、非表示になります。'android:elevation'を10に設定すると、これが解決されます。
  2. TextViewのtextAppearanceは、ボタンの場合と同じ外観になるように設定する必要があります。
于 2017-09-18T23:34:42.907 に答える
-4

もう1つの非常にハッキーな代替手段はweight="1"、ボタンの両側に空白のスペーサービューを追加することです。これがパフォーマンスにどのように影響するかわかりません。

    <View
        android:layout_width="0dp"
        android:layout_height="fill_parent"
        android:layout_weight="1" />
于 2013-11-21T11:10:31.303 に答える