#android #material-components-android #material-components #progress-indicator #android-gravity
Вопрос:
Я создал пользовательское представление, расширяющее кнопку MaterialButton, которая заменяет рисоваемый указатель на циклический указатель при загрузке.
Однако, когда я заменяю рисование индикатором 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"
Правильное положение Рисование располагается в начале текста.
Неверное положение Индикатор выполнения больше не отображается в начале текста.
Комментарии:
1. Похоже, это ошибка. Можете ли вы использовать compose в качестве обходного пути?
2. Я должен был бы разобраться в этом. У меня еще нет никакого опыта работы с композицией.
3. Как и предполагали другие, это похоже на ошибку в библиотеке компонентов материалов de, поэтому я сообщил о проблеме: github.com/material-components/material-components-android/…
Ответ №1:
Любопытно, что один рисунок работает, а другой-нет. Возможно, стоит сообщить об этом как об ошибке. В то же время я могу предложить обходной путь, который не требует много работы (IMO).
Если мы возьмем ваш пользовательский вид и удалим iconGravity
и установим гравитацию текста в начале и вертикальном центре:
gravity = Gravity.START or Gravity.CENTER_VERTICAL
мы можем объединить неопределенное и текст в левой части кнопки:
Теперь вопрос в том, как расположить пару по центру кнопки. Мы можем воспользоваться подсказкой из кода кнопки «Материал» о подходе к тому, сколько места требуется слева для центрирования рисунка и текста.
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)
}
}
}
Комментарии:
1. Это приемлемый обходной путь для этой ошибки. Мне нужно было немного подправить код, и это решило мою проблему.
Ответ №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 в существующий макет, просто добавьте xml-макет:
<androidx.compose.ui.platform.ComposeView
android:id="@ id/compose_view"
android:layout_width="150dp"
android:layout_height="wrap_content" />
и в вашем Фрагменте или деятельности:
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")
}
}
}
}
Здесь вы можете найти более подробную информацию о совместимости.
Комментарии:
1. Есть еще один ответ, который не требует от меня переноса этого кода для создания. Однако это пригодится, как только мы начнем переходить к составлению. Спасибо за ваши усилия.