在 TextInputLayout 中垂直居中提示和 EditText
Center hint and EditText vertically in TextInputLayout
我正在使用 TextInputLayout 来显示提示,但我无法将其垂直居中。我总是这样:
当 EditText / TextInputEditText 中没有文本时,我想将提示垂直居中。我已经尝试了基本的想法(重力,layout_gravity,等等)。到目前为止,唯一的方法是添加一些 "magic" 填充,但我想以更简洁的方式进行。我正在考虑测量顶部提示标签的高度并在它不可见时将其添加为底部边距,并在它可见时删除相同的边距,但我还不太了解 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>
我明白了:
"big" 提示仍然没有垂直居中。它有点低于中心,因为 "small" 提示(在灰色背景中,在顶部,仅当字段聚焦时可见)在顶部占据一些 space 并推动 EditText .
找到解决方案:
<android.support.design.widget.TextInputLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<android.support.design.widget.TextInputEditText
android:layout_width="200dp"
android:layout_height="90dp"
android:hint="Test"
android:gravity="center_vertical" />
90dp 作为高度只是一个例子。如果您可以提供 xml,我可以根据您的情况进行调整。你只需要设置 android:gravity="center_vertical"
并且 TextInputLayout
应该有 wrap_content
作为高度
希望对您有所帮助:)
请使用此代码
如果你想在中心显示 EditText 提示
<android.support.design.widget.TextInputLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="90dp"
android:hint="Test"
android:gravity="center" />
如果要在垂直居中和左对齐中显示 EditText 提示
<android.support.design.widget.TextInputLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="90dp"
android:hint="Test"
android:gravity="center_vertical" />
或
<android.support.design.widget.TextInputLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="90dp"
android:hint="Test"
android:gravity="center|left" />
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>
如您所见,TextInputLayout
由顶部区域组成,用于保存小版本的提示和底部区域,用于保存大版本的提示(以及输入内容)。当视图失去焦点并且编辑文本为空时,提示在蓝色 space 内移动。另一方面,当视图获得焦点或编辑文本中有一些文本时,提示将移至红色 space.
所以我们要做的是:
- 当
TextInputEditText
里面没有焦点和文字时,在底部添加一个额外的padding,这个padding等于红色区域的高度;
- 当
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)
...
}
这是一个实现示例,您可以使用它来计算顶部红色 space(以像素为单位)。
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
}
然后你可以像这样更新填充:
private fun updateHintPosition(hasFocus: Boolean, hasText: Boolean) {
if (hasFocus || hasText) {
textInputEditText.setPadding(0, 0, 0, 0)
} else {
textInputEditText.setPadding(0, 0, 0, getTextInputLayoutTopSpace())
}
}
现在你必须在两个地方调用这个方法:创建视图时(实际上我们需要等待视图完全测量)和焦点改变时。
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())
}
一个问题是 TextInputLayout
的高度在变化,所以所有的视图都在移动,看起来并不真正居中。您可以通过将 TextInputLayout
放在具有固定高度的 FrameLayout
中并将其垂直居中来解决此问题。
你终于可以让所有的东西都动起来了。更改填充时,您只需使用支持库的 TransitionManager
。
你可以在这个link中看到最终结果: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>
代码:
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
}
希望这能解决您的问题。
我在使用主题 "Widget.MaterialComponents.TextInputLayout.FilledBox.Dense"
和密码可见性切换按钮时遇到了这个问题。
所以我最终根据这个问题的答案创建了自定义 class。
之前:
后:
自定义 class:
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
}
用法:
<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>
可能有点晚了但是...基于@JFrite 的回复...您可以创建一个 class 来改进代码:
class TextInputCenterHelper constructor(val textInputLayout: TextInputLayout, val textInputEditText: TextInputEditText){
init {
textInputLayout.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
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)
}
}
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
}
并使用它:
TextInputCenterHelper(your_text_input_layout, your_text_input_edit_text)
希望对大家有所帮助!
问题已于 2019 年 4 月 5 日解决。这是提交:
https://github.com/material-components/material-components-android/commit/4476564820ff7a12f94ffa7fc8d9e10221b18eb1
您可以使用已解决该错误的最新版本(2020 年 7 月 23 日)。查看变更日志(“TextInputLayout”部分):
https://github.com/material-components/material-components-android/releases/tag/1.3.0-alpha02
只需更新 gradle 中的库:
implementation 'com.google.android.material:material:1.3.0-alpha02'
它对我有用。
我发现此处发布的解决方案存在一些问题,因此再添加一个覆盖基础更好的解决方案:
public class CenteredTextInputEditText extends TextInputEditText {
private Integer paddingBottomBackup = null;
// region Constructors
public CenteredTextInputEditText(Context context) {
super(context);
}
public CenteredTextInputEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CenteredTextInputEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
// endregion
// region LifeCycle
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (getOnFocusChangeListener() == null) {
setOnFocusChangeListener((v, hasFocus) -> updateHintPosition(hasFocus));
}
getViewTreeObserver()
.addOnPreDrawListener(
new OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if (getHeight() > 0) {
getViewTreeObserver().removeOnPreDrawListener(this);
updateHintPosition(hasFocus());
return false;
}
return true;
}
});
}
// endregion
// region Center hint
private void updateHintPosition(boolean hasFocus) {
boolean hasText = getText() != null && !Strings.isNullOrEmpty(getText().toString());
if (paddingBottomBackup == null) {
paddingBottomBackup = getPaddingBottom();
}
int bottomPadding = paddingBottomBackup;
if (!hasFocus && !hasText) {
bottomPadding += getTextInputTopSpace();
}
setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), bottomPadding);
if (hasFocus) {
KeyboardUtils.openKeyboardFrom(this);
}
}
private int getTextInputTopSpace() {
View currentView = this;
int space = 0;
do {
space += currentView.getTop();
currentView = (View) currentView.getParent();
} while (!(currentView instanceof TextInputLayout));
return space;
}
// endregion
public void addAdditionalFocusListener(Consumer<Boolean> consumer) {
setOnFocusChangeListener(
new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
consumer.accept(hasFocus);
updateHintPosition(hasFocus);
}
});
}
}
我发现居中 TextInput 布局的最简单方法是在我的 XML 中启用 SetHelperEnabled 或通过编程方式将这些属性插入 TextInputLayout
app:helperTextEnabled="true"
app:helperText=" "
您还可以通过将 paddingTop
添加到内部 EditText
来进一步调整额外的填充
<androidx.cardview.widget.CardView
android:id="@+id/cardView2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="32dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView2"
app:layout_constraintTop_toBottomOf="@+id/textViewSeekModText">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="409dp"
android:layout_height="wrap_content"
android:background="#FDFDFD"
app:helperText=" "
app:helperTextEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:hint="hint" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.cardview.widget.CardView>
只需在 editText 或其后代中添加 paddingVertical
我知道这不是最好的解决方案,但它确实有效。您只需向“TextInputEditText”添加填充:
android:paddingTop="18dp"
android:paddingBottom="18dp"
完整示例:
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/inputLoginEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:layout_marginTop="5dp"
android:backgroundTint="@color/light_blue_background"
android:textColor="@color/blue_button"
app:hintEnabled="false"
android:layout_gravity="center"
app:boxStrokeWidth="0dp"
app:boxStrokeWidthFocused="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textLabelEmail"
app:passwordToggleEnabled="false">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etLoginEmail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:backgroundTint="@color/light_blue_background"
android:hint="@string/hint_email"
android:inputType="textEmailAddress"
android:paddingTop="18dp"
android:paddingBottom="18dp"
android:maxLines="1"
android:textColor="@color/blue_button" />
</com.google.android.material.textfield.TextInputLayout>
它对我来说很完美。
下面是我们可以实现的方法
要使提示居中,请使用app:boxBackgroundMode="filled"
如果要删除编辑文本底线,请使用
app:boxStrokeWidth="0dp"
app:boxStrokeWidthFocused="0dp"
app:boxStrokeColor="@color/white"
app:boxBackgroundColor="@color/white"
完整代码
<com.google.android.material.textfield.TextInputLayout
app:boxBackgroundMode="filled"
app:boxStrokeWidth="0dp"
app:boxStrokeWidthFocused="0dp"
app:boxStrokeColor="@color/white"
app:boxBackgroundColor="@color/white"
android:id="@+id/layout_main"
android:paddingVertical="@dimen/dp_6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dp_4"
android:hint="@string/your_hint"
android:paddingHorizontal="@dimen/dp_16"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/ed_txt"
android:inputType="textNoSuggestions"
android:layout_width="match_parent"
android:layout_height="?listPreferredItemHeightSmall"
android:gravity="bottom"
android:paddingVertical="@dimen/dp_8" />
</com.google.android.material.textfield.TextInputLayout>
我正在使用 TextInputLayout 来显示提示,但我无法将其垂直居中。我总是这样:
当 EditText / TextInputEditText 中没有文本时,我想将提示垂直居中。我已经尝试了基本的想法(重力,layout_gravity,等等)。到目前为止,唯一的方法是添加一些 "magic" 填充,但我想以更简洁的方式进行。我正在考虑测量顶部提示标签的高度并在它不可见时将其添加为底部边距,并在它可见时删除相同的边距,但我还不太了解 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>
我明白了:
"big" 提示仍然没有垂直居中。它有点低于中心,因为 "small" 提示(在灰色背景中,在顶部,仅当字段聚焦时可见)在顶部占据一些 space 并推动 EditText .
找到解决方案:
<android.support.design.widget.TextInputLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<android.support.design.widget.TextInputEditText
android:layout_width="200dp"
android:layout_height="90dp"
android:hint="Test"
android:gravity="center_vertical" />
90dp 作为高度只是一个例子。如果您可以提供 xml,我可以根据您的情况进行调整。你只需要设置 android:gravity="center_vertical"
并且 TextInputLayout
应该有 wrap_content
作为高度
希望对您有所帮助:)
请使用此代码 如果你想在中心显示 EditText 提示
<android.support.design.widget.TextInputLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="90dp"
android:hint="Test"
android:gravity="center" />
如果要在垂直居中和左对齐中显示 EditText 提示
<android.support.design.widget.TextInputLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="90dp"
android:hint="Test"
android:gravity="center_vertical" />
或
<android.support.design.widget.TextInputLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android">
<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="90dp"
android:hint="Test"
android:gravity="center|left" />
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>
如您所见,TextInputLayout
由顶部区域组成,用于保存小版本的提示和底部区域,用于保存大版本的提示(以及输入内容)。当视图失去焦点并且编辑文本为空时,提示在蓝色 space 内移动。另一方面,当视图获得焦点或编辑文本中有一些文本时,提示将移至红色 space.
所以我们要做的是:
- 当
TextInputEditText
里面没有焦点和文字时,在底部添加一个额外的padding,这个padding等于红色区域的高度; - 当
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)
...
}
这是一个实现示例,您可以使用它来计算顶部红色 space(以像素为单位)。
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
}
然后你可以像这样更新填充:
private fun updateHintPosition(hasFocus: Boolean, hasText: Boolean) {
if (hasFocus || hasText) {
textInputEditText.setPadding(0, 0, 0, 0)
} else {
textInputEditText.setPadding(0, 0, 0, getTextInputLayoutTopSpace())
}
}
现在你必须在两个地方调用这个方法:创建视图时(实际上我们需要等待视图完全测量)和焦点改变时。
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())
}
一个问题是 TextInputLayout
的高度在变化,所以所有的视图都在移动,看起来并不真正居中。您可以通过将 TextInputLayout
放在具有固定高度的 FrameLayout
中并将其垂直居中来解决此问题。
你终于可以让所有的东西都动起来了。更改填充时,您只需使用支持库的 TransitionManager
。
你可以在这个link中看到最终结果: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>
代码:
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
}
希望这能解决您的问题。
我在使用主题 "Widget.MaterialComponents.TextInputLayout.FilledBox.Dense"
和密码可见性切换按钮时遇到了这个问题。
所以我最终根据这个问题的答案创建了自定义 class。
之前:
自定义 class:
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
}
用法:
<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>
可能有点晚了但是...基于@JFrite 的回复...您可以创建一个 class 来改进代码:
class TextInputCenterHelper constructor(val textInputLayout: TextInputLayout, val textInputEditText: TextInputEditText){
init {
textInputLayout.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
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)
}
}
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
}
并使用它:
TextInputCenterHelper(your_text_input_layout, your_text_input_edit_text)
希望对大家有所帮助!
问题已于 2019 年 4 月 5 日解决。这是提交: https://github.com/material-components/material-components-android/commit/4476564820ff7a12f94ffa7fc8d9e10221b18eb1
您可以使用已解决该错误的最新版本(2020 年 7 月 23 日)。查看变更日志(“TextInputLayout”部分): https://github.com/material-components/material-components-android/releases/tag/1.3.0-alpha02
只需更新 gradle 中的库:
implementation 'com.google.android.material:material:1.3.0-alpha02'
它对我有用。
我发现此处发布的解决方案存在一些问题,因此再添加一个覆盖基础更好的解决方案:
public class CenteredTextInputEditText extends TextInputEditText {
private Integer paddingBottomBackup = null;
// region Constructors
public CenteredTextInputEditText(Context context) {
super(context);
}
public CenteredTextInputEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CenteredTextInputEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
// endregion
// region LifeCycle
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (getOnFocusChangeListener() == null) {
setOnFocusChangeListener((v, hasFocus) -> updateHintPosition(hasFocus));
}
getViewTreeObserver()
.addOnPreDrawListener(
new OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if (getHeight() > 0) {
getViewTreeObserver().removeOnPreDrawListener(this);
updateHintPosition(hasFocus());
return false;
}
return true;
}
});
}
// endregion
// region Center hint
private void updateHintPosition(boolean hasFocus) {
boolean hasText = getText() != null && !Strings.isNullOrEmpty(getText().toString());
if (paddingBottomBackup == null) {
paddingBottomBackup = getPaddingBottom();
}
int bottomPadding = paddingBottomBackup;
if (!hasFocus && !hasText) {
bottomPadding += getTextInputTopSpace();
}
setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), bottomPadding);
if (hasFocus) {
KeyboardUtils.openKeyboardFrom(this);
}
}
private int getTextInputTopSpace() {
View currentView = this;
int space = 0;
do {
space += currentView.getTop();
currentView = (View) currentView.getParent();
} while (!(currentView instanceof TextInputLayout));
return space;
}
// endregion
public void addAdditionalFocusListener(Consumer<Boolean> consumer) {
setOnFocusChangeListener(
new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
consumer.accept(hasFocus);
updateHintPosition(hasFocus);
}
});
}
}
我发现居中 TextInput 布局的最简单方法是在我的 XML 中启用 SetHelperEnabled 或通过编程方式将这些属性插入 TextInputLayout
app:helperTextEnabled="true"
app:helperText=" "
您还可以通过将 paddingTop
添加到内部 EditText
<androidx.cardview.widget.CardView
android:id="@+id/cardView2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="32dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/textView2"
app:layout_constraintTop_toBottomOf="@+id/textViewSeekModText">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="409dp"
android:layout_height="wrap_content"
android:background="#FDFDFD"
app:helperText=" "
app:helperTextEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
android:hint="hint" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.cardview.widget.CardView>
只需在 editText 或其后代中添加 paddingVertical
我知道这不是最好的解决方案,但它确实有效。您只需向“TextInputEditText”添加填充:
android:paddingTop="18dp"
android:paddingBottom="18dp"
完整示例:
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/inputLoginEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:layout_marginTop="5dp"
android:backgroundTint="@color/light_blue_background"
android:textColor="@color/blue_button"
app:hintEnabled="false"
android:layout_gravity="center"
app:boxStrokeWidth="0dp"
app:boxStrokeWidthFocused="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textLabelEmail"
app:passwordToggleEnabled="false">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etLoginEmail"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:backgroundTint="@color/light_blue_background"
android:hint="@string/hint_email"
android:inputType="textEmailAddress"
android:paddingTop="18dp"
android:paddingBottom="18dp"
android:maxLines="1"
android:textColor="@color/blue_button" />
</com.google.android.material.textfield.TextInputLayout>
它对我来说很完美。
下面是我们可以实现的方法
要使提示居中,请使用app:boxBackgroundMode="filled"
如果要删除编辑文本底线,请使用
app:boxStrokeWidth="0dp"
app:boxStrokeWidthFocused="0dp"
app:boxStrokeColor="@color/white"
app:boxBackgroundColor="@color/white"
完整代码
<com.google.android.material.textfield.TextInputLayout
app:boxBackgroundMode="filled"
app:boxStrokeWidth="0dp"
app:boxStrokeWidthFocused="0dp"
app:boxStrokeColor="@color/white"
app:boxBackgroundColor="@color/white"
android:id="@+id/layout_main"
android:paddingVertical="@dimen/dp_6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dp_4"
android:hint="@string/your_hint"
android:paddingHorizontal="@dimen/dp_16"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/ed_txt"
android:inputType="textNoSuggestions"
android:layout_width="match_parent"
android:layout_height="?listPreferredItemHeightSmall"
android:gravity="bottom"
android:paddingVertical="@dimen/dp_8" />
</com.google.android.material.textfield.TextInputLayout>