将CheckBox的可绘制对象居中在其自身内部

55

我有一个 CheckBox,我希望它在自己的边界内居中显示,而不是被推到一侧。可能比解释更容易演示:

输入图片描述

请注意,它并没有居中。当前定义为:

<CheckBox
    android:id="@+id/checkbox_star"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:button="@drawable/btn_favorite"

    android:layout_gravity="center"
    android:minWidth="48dp" />

不用在意自定义的按钮图像。对于原始的CheckBox也是一样的(小的复选框也一样)。

14个回答

34
我认为问题在于复选框小部件使用具有drawableLeft属性的常规TextView,因为它希望文本也显示出来。(这就是为什么您会看到它在垂直居中,但稍微向左偏移。)
如果您只想要一个具有多个状态的图像按钮,则建议使用ToggleButtonstate list selector中的自定义图像。或者您可以创建一个扩展ImageView并实现Checkable的自定义类。

18
如果按钮大小大于图像大小,ToggleButton 如何帮助居中图像? - Yura Shinkarev
1
对于最后一条语句:“或者你可以创建一个自定义的类,继承ImageView并实现Checkable接口。”,你们不需要自己制作,只需直接从我的回答中复制即可。链接为https://dev59.com/8mYr5IYBdhLWcg3wxM0k#37986765。 - HendraWD
3
为了澄清,CheckBox 不使用 TextView 的 drawableLeft 作为其按钮。它会自己绘制按钮,并且只支持 Gravity.BOTTOM 或 Gravity.CENTER_VERTICAL。参考链接:https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/widget/CompoundButton.java#424 - Alex Fu
如果您这样做,还将失去与复选框相关联的动画效果。 - Jeff Padgett
1
@JeffPadgett那么有更好的方法吗?我正在寻找答案,仍然无法想出来。 - oyeraghib
显示剩余2条评论

23

我正在使用来自material-components-android(或 AppCompatCheckBox)的MaterialCheckBox。它们很相似。

我发现,如果一个复选框没有文本,设置其android:minWidth="0dp"android:minHeight="0dp"可以移除所有填充并居中绘图。


天啊,这就是我寻找了几个小时的东西!谢谢你! - Leo
为什么这个有效我不知道,但你刚刚帮我省了很多时间。谢谢!这个有文档记录吗? - Aloha
2
@PNDA 我没有找到任何文档,只是尝试了每种组合。 - Dewey Reed
@SamChen 我已经使用 AS 4.1 和 MDC 1.2.1 验证了解决方案,它可以工作。 - Dewey Reed
3
虽然这个答案有效,但是存在一个问题:视图实际上变得更小了,因此触摸区域也变得更小了。Material Design 准则建议触摸目标不应小于 48x48 dp(https://material.io/design/usability/accessibility.html#layout-and-typography)。默认的 Material 复选框恰好是这个大小,因此它可能不应该被缩小。 - Erik
我和@Erik遇到了同样的问题。如果填充大于0dp,则设置minHeightminWidth是无效的。 - James

16
您可以使用父布局来实现此目的:
<LinearLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center">

    <CheckBox
        android:id="@+id/checkbox_star"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="Check box text" />
</LinearLayout>

6
谢谢您的回复,但这正是我说“将中心[...]放在自身”而不是“将中心[...]放在其他东西”原因;-),否则我认为我已经这样做了。考虑小部件的点击区域时,这个问题非常重要。 - davidcesarino
如果点击区域是问题,则将复选框按钮和复选框文本分开作为两个控件。并排使用复选框和TextView。 - Eldhose M Babu
2
我没有(也不想要)任何文本,它也不能解决手头的问题。这是关于创建一个比其可绘制对象更大的“CheckBox”,同时将该可绘制对象定位在小部件边界的中心而不是左侧。你所说的并不能做到这一点。 - davidcesarino
这有助于将复选框居中在父布局的区域内。我甚至不需要为复选框本身设置重力。可能不是问题所要求的答案,但它帮助我实现了我想要的效果。 - Marcell

13
android:button="@null"
android:foreground="@drawable/btn_favorite" 
android:foregroundGravity="center"

这对我的使用情况非常完美。结合 android:background 的可绘制状态选择器,可以实现完全定制的复选框。 - jhauberg

13

唯一的正确解决方案是向图像添加插页。大多数“前景”解决方案在API 23以下无法使用。

drawable目录中的btn_favorite_inset.xml

<inset xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/btn_favorite"
    android:insetLeft="6dp"
    android:insetTop="6dp"
    android:insetRight="6dp"
    android:insetBottom="6dp" />

你的布局文件

<CheckBox
    android:id="@+id/checkbox_star"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:button="@drawable/btn_favorite_inset"
    android:layout_gravity="center"
    android:minWidth="48dp" />

9

这个对齐问题可以通过使用CheckableImageView来解决,它是一个自定义的View,继承了ImageViewAppCompatImageView并直接实现了Checkable接口。它还具有其他ImageView属性。

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Checkable;
import android.widget.ImageView;

/**
 * @author hendrawd on 6/23/16
 */
public class CheckableImageView extends ImageView implements Checkable {

    public CheckableImageView(Context context) {
        super(context);
    }

    public CheckableImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    private boolean mChecked = false;

    private static final int[] CHECKED_STATE_SET = {android.R.attr.state_checked};

    @Override
    public int[] onCreateDrawableState(final int extraSpace) {
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
        if (isChecked())
            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
        return drawableState;
    }

    @Override
    public void setChecked(boolean checked) {
        if (mChecked != checked) {
            mChecked = checked;
            refreshDrawableState();
        }
    }

    @Override
    public boolean isChecked() {
        return mChecked;
    }

    @Override
    public void toggle() {
        setChecked(!mChecked);
    }

    @Override
    public void setOnClickListener(final OnClickListener l) {
        View.OnClickListener onClickListener = new OnClickListener() {
            @Override
            public void onClick(View v) {
                toggle();
                l.onClick(v);
            }
        };
        super.setOnClickListener(onClickListener);
    }
}

只需在XML的src属性中设置选择器drawable,check的绘制状态将自动跟随。

使用示例:

<your.package.name.CheckableImageView
    android:id="@+id/some_id"
    android:layout_width="48dp"
    android:layout_height="48dp"
    android:src="@drawable/set_your_selector_here"
    android:padding="14dp"
    android:gravity="center" />

选择器示例(将其放入 drawable 文件夹并添加扩展名 .xml)

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/ic_cb_check" android:state_checked="true" android:state_focused="true" />
    <item android:drawable="@drawable/ic_cb_uncheck" android:state_checked="false" android:state_focused="true" />
    <item android:drawable="@drawable/ic_cb_uncheck" android:state_checked="false" />
    <item android:drawable="@drawable/ic_cb_check" android:state_checked="true" />
</selector>

请用您喜欢的图片替换 ic_cb_checkic_cb_uncheck

此代码也可在https://gist.github.com/hendrawd/661824a721c22b3244667379e9358b5f找到。


@MayankSharma 你是如何处理在点击CheckableImageView时更改drawable的呢? - Parth Patel
@parthpatel 我们在 XML 选择器中定义它。 - Mayank Sharma
@HendraWD 是我代码中处理 OnClickListener 的问题,后来已经解决了。感谢您的建议。 - Parth Patel

4

默认情况下,它具有最小高度和最小宽度。 将它们设置为0dp将使其在自身边界内居中对齐。


<CheckBox
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:minWidth="0dp"
    android:minHeight="0dp" />


2

只需使用以下方法:

          <CheckBox
                  android:button="@null"                        
                  android:foreground="@drawable/checkbox_selector"
                  android:foregroundGravity="center"/>

对我很有效。 但它只适用于

targetSdkVersion >= Build.VERSION_CODES.M || view instanceof FrameLayout


2
这将按照需要正确地工作。只需对背景和按钮属性进行小改动即可。此代码片段已经成功测试。 希望这能帮到您。
 <CheckBox
android:id="@+id/checkbox_star"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@drawable/btn_favorite"
android:button="@color/transparent"
android:layout_gravity="center"
android:minWidth="48dp" />

1

如果您打算在recyclerview中使用复选框,请不要使用此代码来绕过>SDK23的居中对齐复选框错误。

<CheckBox
android:button="@null"
android:drawableLeft="@drawable/checkbox_selector"
android:drawableStart="@drawable/checkbox_selector"
/>

如果是在RecyclerView中(例如:滚动等操作),则CheckBoxes在onBindViewHolder中不会被选中,直到下一次刷新。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接