- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
使用 TextInputLayout来自 Material Design library我们可以使用各种 end icon modes用于密码编辑、文本清除和自定义模式。此外,如果我们使用任何 Widget.MaterialComponents.TextInputLayout.*.ExposedDropdownMenu
样式,它会自动应用显示打开和关闭 V 形的特殊结束图标模式。
各种图标模式的例子:
考虑到结束图标的各种用例,我们决定在 InputTextLayout
中使用加载指示器,使其看起来像这样:
应该如何着手实现?
最佳答案
可以像这样简单地设置使用自定义可绘制对象来代替结束图标:
textInputLayout.endIconMode = TextInputLayout.END_ICON_CUSTOM
textInputLayout.endIconDrawable = progressDrawable
有问题的部分是获取可绘制的加载指示器。
没有可用于加载指示器的公共(public)可绘制资源。
有 android.R.drawable.progress_medium_material
但它被标记为私有(private)并且无法在代码中解析。将资源及其所有依赖的私有(private)资源复制到大约 6 个文件中(2 个可绘制对象 + 2 个动画师 + 2 个插值器)。这可能行得通,但感觉很像黑客。
我们可以使用 ProgressBar
来检索它的 indeterminateDrawable
。这种方法的问题在于可绘制对象与 ProgressBar
紧密相关。指示器仅在 ProgressBar
可见时才会显示动画,对一个 View 进行着色也会对另一个 View 中的指示器进行着色,并且可能会出现其他奇怪的行为。
在类似的情况下,我们可以使用 Drawable.mutate()
来获取可绘制对象的新副本。不幸的是,indeterminateDrawable
已经发生了变化,因此 mutate()
什么都不做。
真正使可绘制对象与 ProgressBar
分离的是对 indeterminateDrawable.constantState.newDrawable()
的调用。参见 documentation了解更多信息。
无论如何,这仍然感觉像是一个 hack。
虽然 drawable 资源被标记为私有(private),但我们可以解析某些主题属性以获取系统默认的加载指示器 drawable。该主题定义了 progressBarStyle
属性,该属性引用了 ProgressBar
的样式。此样式内部是引用主题可绘制对象的 indeterminateDrawable
属性。在代码中,我们可以像这样解析可绘制对象:
fun Context.getProgressBarDrawable(): Drawable {
val value = TypedValue()
theme.resolveAttribute(android.R.attr.progressBarStyleSmall, value, false)
val progressBarStyle = value.data
val attributes = intArrayOf(android.R.attr.indeterminateDrawable)
val array = obtainStyledAttributes(progressBarStyle, attributes)
val drawable = array.getDrawableOrThrow(0)
array.recycle()
return drawable
}
太好了,现在我们有了一个无需修改的原生加载指示器!
现在,如果您将可绘制对象插入此代码
textInputLayout.endIconMode = TextInputLayout.END_ICON_CUSTOM
textInputLayout.endIconDrawable = progressDrawable
你会发现它没有显示任何东西。
实际上,它确实正确显示了可绘制对象,但真正的问题是它没有被动画化。碰巧在动画开始时,drawable 折叠成一个不可见的点。
不幸的是,我们无法将 drawable 转换为它的真实类型 AnimationScaleListDrawable
,因为它在 com.android.internal.graphics.drawable
包中。对我们来说幸运的是,我们可以将其键入为 Animatable
和 start()
:
(drawable as? Animatable)?.start()
当 TextInputLayout
获得/失去焦点时,会发生另一种意外行为。在这种情况下,它将根据 layout.setEndIconTintList()
定义的颜色为可绘制对象着色。如果您没有明确指定色调列表,它会将可绘制对象着色为 ?colorPrimary
。但是在我们设置 drawable 的那一刻,它仍然着色为 ?colorAccent
并且在看似随机的时刻它会改变颜色。
出于这个原因,我建议使用相同的 ColorStateList
为 layout.endIconTintList
和 drawable.tintList
着色。如:
fun Context.fetchPrimaryColor(): Int {
val array = obtainStyledAttributes(intArrayOf(android.R.attr.colorPrimary))
val color = array.getColorOrThrow(0)
array.recycle()
return color
}
...
val states = ColorStateList(arrayOf(intArrayOf()), intArrayOf(fetchPrimaryColor()))
layout.setEndIconTintList(states)
drawable.setTintList(states)
最终我们得到这样的结果:
分别使用 android.R.attr.progressBarStyle
(中)和 android.R.attr.progressBarStyleSmall
。
关于android - 带有加载指示器的 TextInputLayout,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57661168/
我是一名优秀的程序员,十分优秀!