如何在 Java 中更改 TextInputLayout 或 AutoCompleteTextView 的波纹颜色?

How to change TextInputLayout or AutoCompleteTextView's ripple color in Java?

考虑以下带有 AutoCompleteTextView 的 TextInputLayout:

<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/input"
    style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="Value"
    android:paddingHorizontal="16dp"
    android:paddingTop="16dp"
    >

    <AutoCompleteTextView
        android:id="@+id/inputEditor"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="none"
        />

</com.google.android.material.textfield.TextInputLayout>

当用户单击 AutoCompleteTextView 时,波纹将扩展并填充 AutoCompleteTextView:

如何更改 Java/Kotlin 中波纹的颜色?

要在代码中执行此操作,您可以使用允许添加样式的小部件的构造方法之一,但这些方法取决于特定的 api 级别 [21 到 24,具体取决于所使用的内容]

widget constructors

journaldev 应用样式中的“colorControlHighlight”

  <!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="colorControlHighlight">@color/design_default_color_background</item>
</style>

事实证明,更改 AutoCompleteTextView 的颜色比预期的要棘手。

有没有publicAPI?

TextInputLayout 允许通过 Text.setBoxBackgroundColor(color) 更改背景颜色。但是,此方法仅适用于填充样式,不适用于轮廓样式。无论如何我都试过了;结果看起来太糟糕了我不会在这里粘贴可憎的东西来保护你的眼睛免受永久性伤害。

material 设计库将自动膨胀 MaterialAutoCompleteTextView 代替 AutoCompleteTextView。我可以调用 MaterialAutoCompleteTextView.setSupportBackgroundTintList(tint) 来给背景着色。文档将我重定向为调用 ViewCompat.setBackgroundTintList(view, tint)

view.setSupportBackgroundTintList(ColorStateList.valueOf(Color.GREEN))
ViewCompat.setBackgroundTintList(view, ColorStateList.valueOf(Color.GREEN))

结果:

两种方法我都试过了。遗憾的是,此 API 仅在波纹进行时应用色调。它还会导致背景在提示下绘制。

由于 public API 不起作用,让我们探索一些 hacks 解决方法。

克隆背景

我使用调试器来准确查看 AutoCompleteTextView 用于其背景的内容。原来它是一个 2 层 LayerDrawable,第一层是 RippleDrawable,第二层是 CutoutDrawable.

2层LayerDrawable:

第一层 - RippleDrawable:

第 2 层 - CutoutDrawable:

我只需要改变 RippleDrawable 的颜色。所以我 mutate() LayerDrawable 并以 RippleDrawable 为目标。

      (view.background as? LayerDrawable)
          ?.let { it.mutate() as? LayerDrawable }
          ?.let {
            for (i in 0 until it.numberOfLayers) {
              val d = it.getDrawable(i)
              if (d is RippleDrawable) {
                d.setColor(ColorStateList.valueOf(Color.GREEN))
              }
            }
            view.background = it
          }

结果:

我认为我们走在正确的轨道上;提示文本现在已着色,但 CutoutDrawable 仍会丢失其所有状态并且无法正确剪切提示区域。

直接变异背景

由于 CutoutDrawable 不会覆盖 Drawable.inflate(Resources, XmlPullParser, AttributeSet),我认为 AutoCompleteTextView 的背景总是在 Java 中创建,而不是像 R.drawable.* 中的传统 Drawable 资源那样共享 XML Drawable。所以删除 .mutate() 调用应该是安全的。

      (view.background as? LayerDrawable)
          ?.let {
            for (i in 0 until it.numberOfLayers) {
              val d = it.getDrawable(i)
              if (d is RippleDrawable) {
                d.setColor(ColorStateList.valueOf(Color.GREEN))
              }
            }
          }

结果:

瞧! AutoCompleteTextView 的波纹现在可以更改为 Java/Kotlin.

中的任何颜色