Android iconGravity 不适用于具有 CircularProgressIndicator 的 MaterialButton

Posted

技术标签:

【中文标题】Android iconGravity 不适用于具有 CircularProgressIndicator 的 MaterialButton【英文标题】:Android iconGravity not working for MaterialButton with CircularProgressIndicator 【发布时间】:2021-11-01 01:25:44 【问题描述】:

我创建了一个扩展 MaterialButton 的自定义视图,它在加载时将可绘制对象替换为 CircularProgressIndicator。

但是,当我用 CircularProgressIndicator 替换可绘制对象时,iconGravity 不再起作用。我看不出我做错了什么。

我已经能够将课程归结为:

class LoadingButton constructor(context: Context) : MaterialButton(context) 

    private val progressIndicatorDrawable by lazy 
        val progressIndicatorSpec = CircularProgressIndicatorSpec(context, null, 0, R.style.Widget_MaterialComponents_CircularProgressIndicator_ExtraSmall)
        progressIndicatorSpec.indicatorColors = intArrayOf(getColor(context, R.color.white))
        IndeterminateDrawable.createCircularDrawable(context, progressIndicatorSpec).apply 
            setVisible(true, true)
        
    

    init 
        // the following drawable is aligned to the start of the view (incorrect).
        icon = progressIndicatorDrawable
        // the following drawable is aligned to the start of the text (correct).
        // icon = DrawableUtil.getDrawable(context, R.drawable.icon_delete)
        text = "Delete"
        iconGravity = ICON_GRAVITY_TEXT_START
    

我正在使用这个依赖项

// I've also tried 1.5.0-alpha02
implementation "com.google.android.material:material:1.3.0"

正确的位置 drawable 位于文本的开头。

位置不正确 进度指示器不再位于文本的开头。

【问题讨论】:

这似乎是一个错误。您可以使用 compose 作为解决方法吗? 我必须调查一下。我还没有任何撰写的经验。 就像其他人建议的那样,这似乎是材料组件库中的一个错误,所以我报告了一个问题:github.com/material-components/material-components-android/… 【参考方案1】:

奇怪的是,一个drawable 可以工作,而另一个却不能。可能值得将其报告为错误。同时,我可以提供一种不涉及大量工作 (IMO) 的解决方法。

如果我们采用您的自定义视图并删除 iconGravity 并将文本的重心设置为开始和垂直中心:

gravity = Gravity.START or Gravity.CENTER_VERTICAL

我们可以在按钮的左侧将不确定和文本放在一起:

现在的问题是如何让这对在按钮中居中。我们可以从 MaterialButton 的 code 中获得提示,了解左侧需要多少空间才能使可绘制对象和文本居中。

int newIconLeft =
    (buttonWidth
        - getTextWidth()
        - ViewCompat.getPaddingEnd(this)
        - localIconSize
        - iconPadding
        - ViewCompat.getPaddingStart(this))
        / 2;

把它们放在一起:

这里是演示代码。如果您的实际按钮比您的问题中描述的更复杂,则可能需要进行一些调整。

class LoadingButton @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : MaterialButton(context, attrs) 

    // true - use indeterminate; false = use delete icon
    private val useIndeterminate = true

    private val progressIndicatorDrawable by lazy 
        val progressIndicatorSpec = CircularProgressIndicatorSpec(
            context,
            null,
            0,
            R.style.Widget_MaterialComponents_CircularProgressIndicator_ExtraSmall
        )
        progressIndicatorSpec.indicatorColors = intArrayOf(getColor(context, R.color.white))
        IndeterminateDrawable.createCircularDrawable(context, progressIndicatorSpec).apply 
            setVisible(true, true)
        
    

    init 
        if (useIndeterminate) 
            icon = progressIndicatorDrawable
            gravity = Gravity.START or Gravity.CENTER_VERTICAL
         else 
            icon = ContextCompat.getDrawable(context, R.drawable.icon_delete)
            iconGravity = ICON_GRAVITY_TEXT_START
            gravity = Gravity.CENTER
        
        text = "Delete"
    

    // This is homw much we need to shift the contents of the button to the right in order to
    // center it.
    private var xShift = 0f
    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) 
        super.onLayout(changed, left, top, right, bottom)
        if (useIndeterminate) 
            xShift = (width
                    - icon.intrinsicWidth
                    - paint.measureText(text.toString())
                    - iconPadding
                    - ViewCompat.getPaddingStart(this)
                    - ViewCompat.getPaddingEnd(this)) / 2f
        
    

    override fun onDraw(canvas: Canvas?) 
        // Shift right before drawing.
        canvas?.withTranslation(xShift) 
            super.onDraw(canvas)
        
    

【讨论】:

这是一个可接受的解决此错误的方法。我需要稍微调整一下代码,它解决了我的问题。【参考方案2】:

免责声明:这似乎是材料组件库的错误,此答案不是答案中报告的代码的解决方案。

如果您可以使用 Compose 作为解决方法,只需应用以下内容:

Button (
    modifier = Modifier.width(150.dp),
    onClick =  /* Do something! */ 
)
    CircularProgressIndicator(color = White,
        modifier = Modifier
            .size(24.dp)
            .align(Alignment.CenterVertically))
    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
    Text("Delete")

要将 Compose UI 内容合并到现有布局中,只需添加 xml 布局:

<androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_view"
        android:layout_
        android:layout_ />

在你的 Fragment 或 Activity 中:

    val view: ComposeView = findViewById(R.id.compose_view)
    view.apply 
        setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
        setContent 
            MaterialTheme 
                /* The code above */
                Button (
                    onClick =  /* Do something! */ )
                    CircularProgressIndicator(color = White, modifier = Modifier.size(24.dp).align(
                        Alignment.CenterVertically))
                    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
                    Text("Delete")
                
            
        
    

您可以在此处找到有关Interoperability 的更多详细信息。

【讨论】:

还有另一个答案不需要我迁移此代码来撰写。但是,一旦我们开始迁移到 compose,这将派上用场。感谢您的努力。

以上是关于Android iconGravity 不适用于具有 CircularProgressIndicator 的 MaterialButton的主要内容,如果未能解决你的问题,请参考以下文章

Android列表视图不适用于片段

Android '无法添加窗口 -- 令牌 null 不适用于应用程序' 异常

FFMPEG 命令不适用于 Android 10

GCM 不适用于 Android 4.1 设备

调试不适用于特定的 android 设备

Android 模拟位置不适用于谷歌地图