web-dev-qa-db-ja.com

TextInputLayoutの垂直方向のヒントとEditText

ヒントを表示するために TextInputLayout を使用していますが、垂直方向に中央揃えすることができません。私はいつもこれを手に入れます:

enter image description here

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>

そして私はこれを手に入れます:

enter image description here

「大きな」ヒントはまだ垂直方向に中央揃えされていません。 「小さい」ヒント(灰色の背景、上部、フィールドがフォーカスされている場合にのみ表示)は上部にいくらかのスペースを取り、 EditText をプッシュするため、これは中央より少し下です。

11
allo86

これは、現在のTextInputLayoutの実装では可能ではないようです。しかし、TextInputEditTextのパディングで遊んで、希望どおりの結果を得ることができます。

次のようなTextInputLayoutTextInputEditTextがあるとします。

<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>

enter image description here

enter image description here

ご覧のとおり、TextInputLayoutは、小さいバージョンのヒントを保持する上部領域と、大きなバージョンのヒントを保持する下部領域(および入力コンテンツ)で構成されています。ビューがフォーカスを失い、エディットテキストが空の場合、ヒントは青いスペース内に移動しています。一方、ビューがフォーカスを取得したり、エディットテキストにテキストが含まれている場合、ヒントは赤いスペースに移動しています。

だから私たちがしたいことは:

  • TextInputEditTextの下部に追加のパディングを追加します。内部にフォーカスとテキストがない場合、このパディングは赤い領域の高さに等しくなります。
  • TextInputEditTextにフォーカスまたはテキストがある場合は、このパディングを削除してください。

その結果、ビューは次のようになり、垂直方向に中央に大きなヒントが表示されます。 enter image description here

次のようにビューを取得するとします。

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)
    ...
}

上部の赤いスペースをピクセルで計算するために使用できる実装の例を次に示します。

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())
    }
}

次に、このメソッドを2か所で呼び出す必要があります。ビューが作成されたとき(実際には、ビューが完全に測定されるまで待つ必要があります)と、フォーカスが変更されたときです。

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())
}

1つの問題は、TextInputLayoutの高さが変化するため、すべてのビューが移動し、実際には中央に表示されないことです。高さを固定したTextInputLayout内に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>

コード:

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
}

これで問題が解決することを願っています。

6
JFrite

私はテーマを使用したときにこの問題に直面しました"Widget.MaterialComponents.TextInputLayout.FilledBox.Dense"およびパスワードの表示切り替えボタン。

そのため、この質問の回答に基づいて、カスタムクラスを作成することになりました。

前: - Before 後: After

カスタムクラス:

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>
4
Andrew

センターに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" />
1
Saurabh Vadhva

解決策を見つけました:

    <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" />

高さ90 dpは一例です。あなたがあなたのxmlを提供できれば、私はあなたのケースにそれを適応させることができます。 Android:gravity="center_vertical"を設定するだけで、TextInputLayoutの高さはwrap_contentになります。

それが役に立てば幸い :)

0
Rene Ferrari

多分少し遅れますが... @JFrite応答に基づいて...コードを改善するクラスを作成できます。

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)

これが誰かを助けることを願っています!