在TextInputLayout中垂直居中提示和EditText

all*_*o86 11 android android-edittext android-textinputlayout android-gravity

我正在使用TextInputLayout来显示提示,但我无法将其垂直居中.我总是得到这个:

在此输入图像描述

EditText/TextInputEditText中没有文本时,我想垂直居中.我尝试过基本的想法(重力,layout_gravity等).到目前为止,唯一的方法是添加一些"魔术"填充,但我想以更清洁的方式做到这一点.我正在考虑测量顶部提示标签高度,并在它不可见时将其添加为底部边距,并在可见时删除相同的边距,但我还不太了解TextInputLayout源代码.有谁知道怎么做?

编辑:

我试过这个建议的答案:

<android.support.design.widget.TextInputLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="20dp"
    android:background="@color/grey_strong">

    <android.support.design.widget.TextInputEditText
        android:layout_width="match_parent"
        android:layout_height="90dp"
        android:background="@color/red_light"
        android:gravity="center_vertical"
        android:hint="Test"/>

</android.support.design.widget.TextInputLayout>
Run Code Online (Sandbox Code Playgroud)

我得到了这个:

在此输入图像描述

"大"提示仍然没有垂直居中.它略低于中心,因为"小"提示(在灰色背景中,在顶部,仅在场聚焦时可见)在顶部占据一些空间并推动EditText.

小智 8

只需在 editText 或其后代中添加 paddingVertical

  • 最简单的方法! (2认同)

And*_*rew 7

当我使用主题"Widget.MaterialComponents.TextInputLayout.FilledBox.Dense"和密码可见性切换按钮时,我遇到了这个问题。

所以我最终根据这个问题的答案创建了自定义类。

前: 前 后: 后

自定义类:

package com.mycompany

import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewTreeObserver
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import com.mycompany.R

class CustomTextInputEditText : TextInputEditText {
    //region Constructors
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
    //endregion

    //region LifeCycle
    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        textInputEditText.setOnFocusChangeListener { _, hasFocus ->
            updateHintPosition(hasFocus, !textInputEditText.text.isNullOrEmpty())
        }
        textInputEditText.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
            override fun onPreDraw(): Boolean {
                if ((textInputLayout?.height ?: 0) > 0) {
                    textInputLayout?.viewTreeObserver?.removeOnPreDrawListener(this)
                    updateHintPosition(textInputEditText.hasFocus(), !textInputEditText.text.isNullOrEmpty())
                    return false
                }
                return true
            }
        })
    }
    //endregion

    //region Center hint
    private var paddingBottomBackup:Int? = null
    private var passwordToggleButtonPaddingBottomBackup:Float? = null
    private val textInputEditText: TextInputEditText
        get() {
            return this
        }
    private val textInputLayout:TextInputLayout?
        get(){
            return if (parent is TextInputLayout) (parent as? TextInputLayout) else (parent?.parent as? TextInputLayout)
        }
    private val passwordToggleButton:View?
        get() {
            return (parent as? View)?.findViewById(R.id.text_input_password_toggle)
        }

    private fun updateHintPosition(hasFocus: Boolean, hasText: Boolean) {
        if (paddingBottomBackup == null)
            paddingBottomBackup = paddingBottom

        if (hasFocus || hasText)
            textInputEditText.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottomBackup!!)
        else
            textInputEditText.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottomBackup!! + getTextInputLayoutTopSpace())

        val button = passwordToggleButton
        if (button != null){
            if (passwordToggleButtonPaddingBottomBackup == null)
                passwordToggleButtonPaddingBottomBackup = button.translationY

            if (hasFocus || hasText)
                button.translationY =  - getTextInputLayoutTopSpace().toFloat() * 0.50f
            else
                button.translationY = passwordToggleButtonPaddingBottomBackup!!
        }
    }

    private fun getTextInputLayoutTopSpace(): Int {
        var currentView: View = textInputEditText
        var space = 0
        do {
            space += currentView.top
            currentView = currentView.parent as View
        } while (currentView !is TextInputLayout)
        return space
    }
    //endregion

    //region Internal classes
    data class Padding(val l: Int, val t: Int, val r: Int, val b: Int)
    //endregion
}
Run Code Online (Sandbox Code Playgroud)

用法:

        <com.google.android.material.textfield.TextInputLayout
            style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.Dense"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:hint="Password"
            app:passwordToggleEnabled="true">

            <com.mycompany.CustomTextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:inputType="textPassword" />

        </com.google.android.material.textfield.TextInputLayout>
Run Code Online (Sandbox Code Playgroud)


小智 6

使用的当前实现似乎无法实现TextInputLayout。但是您可以通过玩游戏的填充来实现您想要的TextInputEditText

假设您有TextInputLayout一个TextInputEditText像这样的:

<android.support.design.widget.TextInputLayout
    android:id="@+id/text_input_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#FAA"
    android:hint="Text hint">

    <android.support.design.widget.TextInputEditText
        android:id="@+id/text_input_edit_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#AAF" />

</android.support.design.widget.TextInputLayout>
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

在此处输入图片说明

如您所见,TextInputLayout它由一个顶部区域和一个底部区域组成,顶部区域用于保存小版本的提示,底部区域用于保存大版本的提示(以及输入内容)。当视图失去焦点且编辑文本为空时,提示在蓝色空间内移动。另一方面,当视图获得焦点或编辑文本中包含一些文本时,提示将移至红色空间。

所以我们要做的是:

  • TextInputEditText当里面没有焦点和文本时,在其底部添加一个额外的填充,该填充等于红色区域的高度;
  • TextInputEditText里面有焦点或文本时,请删除此填充。

结果,视图将看起来像这样,大提示垂直居中: 在此处输入图片说明

假设您按以下方式检索视图:

private lateinit var textInputLayout: TextInputLayout
private lateinit var textInputEditText: TextInputEditText

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    ...
    textInputLayout = view.findViewById(R.id.text_input_layout)
    textInputEditText = view.findViewById(R.id.text_input_edit_text)
    ...
}
Run Code Online (Sandbox Code Playgroud)

这是可用于计算顶部红色空间(以像素为单位)的实现示例。

private fun getTextInputLayoutTopSpace(): Int {
    var currentView: View = textInputEditText
    var space = 0
    do {
        space += currentView.top
        currentView = currentView.parent as View
    } while (currentView.id != textInputLayout.id)
    return space
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以像这样更新填充:

private fun updateHintPosition(hasFocus: Boolean, hasText: Boolean) {
    if (hasFocus || hasText) {
        textInputEditText.setPadding(0, 0, 0, 0)
    } else {
        textInputEditText.setPadding(0, 0, 0, getTextInputLayoutTopSpace())
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您必须在两个位置调用此方法:创建视图时(实际上,我们需要等待视图被完全度量)以及焦点更改时。

textInputLayout.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
    override fun onPreDraw(): Boolean {
        if (textInputLayout.height > 0) {
            textInputLayout.viewTreeObserver.removeOnPreDrawListener(this)
            updateHintPosition(textInputEditText.hasFocus(), !textInputEditText.text.isNullOrEmpty())
            return false
        }
        return true
    }
})

textInputEditText.setOnFocusChangeListener { _, hasFocus ->
    updateHintPosition(hasFocus, !textInputEditText.text.isNullOrEmpty())
}
Run Code Online (Sandbox Code Playgroud)

一个问题是的高度在TextInputLayout变化,因此所有视图都在移动,并且看起来并不居中。您可以通过将TextInputLayout内部的a 放置FrameLayout为固定高度并将其垂直居中来解决此问题。

最终,您可以为所有事物设置动画。TransitionManager更改填充时,您只需要使用支持库的。

您可以在以下链接中查看最终结果https : //streamable.com/la9uk

完整的代码如下所示:

布局:

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="60dp"> <-- Adapt the height for your needs -->

    <android.support.design.widget.TextInputLayout
        android:id="@+id/text_input_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:background="#FAA"
        android:hint="Text hint">

        <android.support.design.widget.TextInputEditText
            android:id="@+id/text_input_edit_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#AAF" />

    </android.support.design.widget.TextInputLayout>

</FrameLayout>
Run Code Online (Sandbox Code Playgroud)

代码:

private lateinit var textInputLayout: TextInputLayout
private lateinit var textInputEditText: TextInputEditText

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    super.onCreateView(inflater, container, savedInstanceState)
    val view = inflater.inflate(R.layout.your_layout, container, false)

    textInputLayout = view.findViewById(R.id.text_input_layout)
    textInputEditText = view.findViewById(R.id.text_input_edit_text)

    textInputLayout.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
        override fun onPreDraw(): Boolean {
            // Wait for the first draw to be sure the view is completely measured
            if (textInputLayout.height > 0) {
                textInputLayout.viewTreeObserver.removeOnPreDrawListener(this)
                updateHintPosition(textInputEditText.hasFocus(), !textInputEditText.text.isNullOrEmpty(), false)
                return false
            }
            return true
        }
    })

    textInputEditText.setOnFocusChangeListener { _, hasFocus ->
        updateHintPosition(hasFocus, !textInputEditText.text.isNullOrEmpty(), true)
    }

    return view
}

private fun updateHintPosition(hasFocus: Boolean, hasText: Boolean, animate: Boolean) {
    if (animate) {
        TransitionManager.beginDelayedTransition(textInputLayout)
    }
    if (hasFocus || hasText) {
        textInputEditText.setPadding(0, 0, 0, 0)
    } else {
        textInputEditText.setPadding(0, 0, 0, getTextInputLayoutTopSpace())
    }
}

private fun getTextInputLayoutTopSpace(): Int {
    var currentView: View = textInputEditText
    var space = 0
    do {
        space += currentView.top
        currentView = currentView.parent as View
    } while (currentView.id != textInputLayout.id)
    return space
}
Run Code Online (Sandbox Code Playgroud)

我希望这能解决您的问题。