Android iconGravity 不适用于带有 CircularProgressIndicator 的 MaterialButton
Android iconGravity not working for MaterialButton with CircularProgressIndicator
我创建了一个扩展 MaterialButton 的自定义视图,在加载时用 CircularProgressIndicator 替换可绘制对象。
但是,当我用 CircularProgressIndicator 替换可绘制对象时,iconGravity 不再起作用。我不明白我做错了什么。
我已经能够将 class 归结为:
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 位于文本的开头。
位置不正确进度指示器不再位于文本的开头。
免责声明:这似乎是 material 组件库的一个错误,这个答案不是答案中报告的代码的解决方案。
如果您可以使用 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_width="150dp"
android:layout_height="wrap_content" />
在您的片段中或 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 的更多详细信息。
奇怪的是,一个可绘制对象可以工作,而另一个不能。可能值得将其报告为错误。同时,我可以提供一个不需要大量工作的解决方法 (IMO)。
如果我们采用您的自定义视图并删除 iconGravity
并将文本的引力设置为开始和垂直居中:
gravity = Gravity.START or Gravity.CENTER_VERTICAL
我们可以在按钮的左侧将不确定和文本放在一起:
现在的问题是如何使对在按钮中居中。我们可以从 MaterialButton 的 code 中得到一些关于左侧需要多少 space 来使可绘制对象和文本居中的方法的提示。
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)
}
}
}
我创建了一个扩展 MaterialButton 的自定义视图,在加载时用 CircularProgressIndicator 替换可绘制对象。
但是,当我用 CircularProgressIndicator 替换可绘制对象时,iconGravity 不再起作用。我不明白我做错了什么。
我已经能够将 class 归结为:
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 位于文本的开头。
位置不正确进度指示器不再位于文本的开头。
免责声明:这似乎是 material 组件库的一个错误,这个答案不是答案中报告的代码的解决方案。
如果您可以使用 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_width="150dp"
android:layout_height="wrap_content" />
在您的片段中或 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 的更多详细信息。
奇怪的是,一个可绘制对象可以工作,而另一个不能。可能值得将其报告为错误。同时,我可以提供一个不需要大量工作的解决方法 (IMO)。
如果我们采用您的自定义视图并删除 iconGravity
并将文本的引力设置为开始和垂直居中:
gravity = Gravity.START or Gravity.CENTER_VERTICAL
我们可以在按钮的左侧将不确定和文本放在一起:
现在的问题是如何使对在按钮中居中。我们可以从 MaterialButton 的 code 中得到一些关于左侧需要多少 space 来使可绘制对象和文本居中的方法的提示。
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)
}
}
}