人们可以像这样简单地设置使用自定义drawable代替End Icon:
textInputLayout.endIconMode = TextInputLayout.END_ICON_CUSTOM
textInputLayout.endIconDrawable = progressDrawable
有问题的部分是获取可绘制的加载指示器。
错误选项 1
没有可用于加载指示器的公共可绘制资源。
有android.R.drawable.progress_medium_material,但它被标记为私有,无法在代码中解析。将资源及其所有依赖的私有资源总共复制到大约 6 个文件中(2 个可绘制对象 + 2 个动画师 + 2 个插值器)。这可以工作,但感觉很像一个黑客。
错误的选项 2
我们可以使用ProgressBar 来检索它的indeterminateDrawable。这种方法的问题在于可绘制对象与ProgressBar 密切相关。仅当 ProgressBar 可见时,指示器才会动画,着色一个视图也会着色另一个视图中的指示器,并且可能会出现其他奇怪的行为。
在类似情况下,我们可以使用Drawable.mutate() 来获取drawable 的新副本。不幸的是,indeterminateDrawable 已经发生了变异,因此 mutate() 什么都不做。
真正有效地将可绘制对象与ProgressBar 分离的是对indeterminateDrawable.constantState.newDrawable() 的调用。请参阅documentation 了解更多信息。
无论如何,这仍然感觉像是一个 hack。
好选项 3
虽然可绘制资源被标记为私有,但我们可以解析某些主题属性以获取系统的默认加载指示器可绘制。主题定义了引用ProgressBar 样式的progressBarStyle 属性。这种风格的内部是indeterminateDrawable 属性,它引用了主题可绘制对象。在代码中,我们可以像这样解析drawable:
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
}
太好了,现在我们有了一个无需 hack 即可绘制的原生加载指示器!
额外措施
动画
现在如果你把drawable插入这段代码
textInputLayout.endIconMode = TextInputLayout.END_ICON_CUSTOM
textInputLayout.endIconDrawable = progressDrawable
你会发现它没有显示任何东西。
实际上,它确实正确显示了可绘制对象,但真正的问题是它没有被动画化。只是碰巧在动画开始时,drawable 被折叠成一个不可见的点。
不幸的是,我们无法将可绘制对象转换为其真实类型AnimationScaleListDrawable,因为它位于com.android.internal.graphics.drawable 包中。
幸运的是,我们可以将其输入为Animatable 和start() 它:
(drawable as? Animatable)?.start()
颜色
当TextInputLayout 接收/失去焦点时,会发生另一个意外行为。在这种情况下,它将根据layout.setEndIconTintList() 定义的颜色为drawable 着色。如果您没有明确指定着色列表,它会将可绘制对象着色为?colorPrimary。但是在我们设置drawable的那一刻,它仍然被着色为?colorAccent,并且在看似随机的时刻它会改变颜色。
因此,我建议将layout.endIconTintList 和drawable.tintList 着色为相同的ColorStateList。如:
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。