TextInputLayout 提示重叠问题

TextInputLayout hint overlap issue

我正在使用 Android 设计库中的 TextInputLayout 在 EditText 上显示标签。

问题是当我开始 activity 时,EditText 提示(标签)文本 与实际文本重叠 (一秒钟),然后才 returns 到它自己的位置(在 EditText 的顶部)。

为了说明这个问题,我录制了一个简短的示例视频:https://youtu.be/gy0CzcYggxU

这是我的 activity.xml:

<LinearLayout
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_margin="16dp"
  android:orientation="vertical">

<android.support.design.widget.TextInputLayout
    android:id="@+id/firstNameTextInputLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="8dp">

  <EditText
      android:id="@+id/firstNameEditText"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:hint="@string/first_name"
      android:inputType="textCapWords"
      android:textColor="@color/textPrimary"
      android:textColorHint="@color/textSecondary"
      android:textSize="16sp"
      android:theme="@style/CustomEditText"/>
</android.support.design.widget.TextInputLayout>


<android.support.design.widget.TextInputLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="24dp">

  <EditText
      android:id="@+id/lastNameEditText"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:hint="@string/last_name"
      android:inputType="textCapWords"
      android:textColor="@color/textPrimary"
      android:textColorHint="@color/textSecondary"
      android:textSize="16sp"
      android:theme="@style/CustomEditText"/>
</android.support.design.widget.TextInputLayout>

我想出了一个便宜的解决方法来解决这个问题和另一个问题。

子类化 TextInputLayout
查看 addView()
的代码 如果您在文本视图中设置了文本,当它膨胀时,它会将提示设置为折叠并阻止动画。此代码执行临时设置文本的解决方法,直到在安装过程中设置状态。作为奖励,有代码可确保绘制提示,以防万一只有一次布局传递。

    public class TextInputLayout extends android.support.design.widget.TextInputLayout {

    public TextInputLayout(Context context) {
        super(context);
    }

    public TextInputLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onLayout(final boolean changed, final int left, final int top, final int right, final int bottom) {
        if (ViewCompat.isLaidOut(this)) {
            super.onLayout(changed, left, top, right, bottom);
        } else {
            // Workaround for this terrible logic where onLayout gets called before the view is flagged as laid out.
            // The normal TextInputLayout is depending on isLaidOut when onLayout is called and failing the check which prevents initial drawing
            // If there are multiple layout passes this doesn't get broken
            post(new Runnable() {
                @SuppressLint("WrongCall")
                @Override
                public void run() {
                    TextInputLayout.super.onLayout(changed, left, top, right, bottom);
                }
            });
        }
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        if (child instanceof EditText) {
            EditText editText = (EditText) child;
            if (StringUtils.isEmpty(editText.getText().toString())) {
                editText.setText("  "); // Set filler text so the initial state of the floating title is to be collapsed
                super.addView(child, index, params);
                editText.setText(""); // Set back to blank to cause the hint to animate in just in case the user sets text
                // This prevents the hint from being drawn over text that is set programmatically before the state is determined
                return;
            }
        }
        super.addView(child, index, params);
    }
}

您可以以编程方式设置提示,稍有延迟。这不是一个理想的解决方案,但至少看起来比重叠提示好。

new Handler().postDelayed(
   new Runnable() {
      @Override
      public void run () {
         textInputLayout.setHint("My hint");
      }
   }, 100
);

我认为这可能会在编译时得到修复 'com.android.support:design:23.0.1'

丹尼尔·奥乔亚 (Daniel Ochoa) 在评论中指出了一个对我有用的变通方法 - 使用一些文本内容设置 EditText 的初始状态(一个空字符串应该可以做到)。这将强制提示的初始状态为 up。

<android.support.design.widget.TextInputLayout
    android:id="@+id/firstNameTextInputLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="8dp">

  <EditText
      android:id="@+id/firstNameEditText"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:hint="@string/first_name"
      android:inputType="textCapWords"
      android:textColor="@color/textPrimary"
      android:textColorHint="@color/textSecondary"
      android:textSize="16sp"
      android:theme="@style/CustomEditText"
      android:text=" "/>
</android.support.design.widget.TextInputLayout>

对我有用的解决方法是像这样更新 activity:

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    textInputLayout.setHintAnimationEnabled(false);
    textInput.setText("sample");
    textInputLayout.setHintAnimationEnabled(true);
    ...
}

终于找到问题的充分解释:

Well it turns out that there was a performance optimization added to the framework in Android 4.0 which allowed your view hierarchy only one single draw pass before the Activity animation was started. Once the Activity animation has ended, your view hierarchy is drawn every ~16ms as you expect.

阅读更多:https://medium.com/@chrisbanes

TLDR: 这是平台限制,此行为会发生在旧版本(Marshmallow 及更低版本)上。

Nougat 动画将 运行 符合预期,没有延迟。