テキスト フィールドを構成する

TextField を使用すると、ユーザーはテキストを入力、変更できます。このページでは、TextField の実装、TextField 入力のスタイル設定、その他の TextField オプション(キーボード オプションやユーザー入力の視覚的な変換など)の構成を行う方法について説明します。

TextField の実装を選択する

TextField の実装には次の 2 つのレベルがあります。

  1. TextField はマテリアル デザインの実装であり、マテリアル デザインのガイドラインに沿っているため、この実装を選択することをおすすめします。
  2. BasicTextField を使用すると、ユーザーはハードウェア キーボードまたはソフトウェア キーボードを使用してテキストを編集できますが、ヒントやプレースホルダなどの装飾は提供されません。

TextField(
    state = rememberTextFieldState(initialText = "Hello"),
    label = { Text("Label") }
)

「

OutlinedTextField(
    state = rememberTextFieldState(),
    label = { Text("Label") }
)

紫色の枠線とラベルが付いた編集可能なテキスト フィールド。

スタイルTextField

TextField と BasicTextField には、カスタマイズ用の共通パラメータが多数あります。TextField の完全なリストは、TextField のソースコードで確認できます。以下に、有用なパラメータの一部を示します(すべてを網羅しているわけではありません)。

  • textStyle
  • lineLimits

TextField(
    state = rememberTextFieldState("Hello\nWorld\nInvisible"),
    lineLimits = TextFieldLineLimits.MultiLine(maxHeightInLines = 2),
    placeholder = { Text("") },
    textStyle = TextStyle(color = Color.Blue, fontWeight = FontWeight.Bold),
    label = { Text("Enter text") },
    modifier = Modifier.padding(20.dp)
)

ラベルと 2 つの編集可能な行がある、複数行の TextField

デザインでマテリアル TextField または OutlinedTextField が必要な場合は、BasicTextField よりも TextField をおすすめします。ただし、マテリアル仕様の装飾を必要としないデザインを構築する場合は、BasicTextField を使用する必要があります。

行数の上限を構成する

TextField コンポーザブルは、単一の軸に沿ったスクロールをサポートしています。スクロール動作は lineLimits パラメータによって決まります。1 行に構成された TextField は水平方向にスクロールし、複数行の TextField は垂直方向にスクロールします。

TextFieldLineLimits を使用して、TextField に適した回線構成を選択します。

TextField(
    state = rememberTextFieldState(),
    lineLimits = TextFieldLineLimits.SingleLine
)

「

SingleLine 構成には次の特性があります。

  • テキストは折り返されず、改行もできません。
  • TextField の高さは常に固定されています。
  • テキストがオーバーフローすると、水平方向にスクロールします。

TextField(
    state = rememberTextFieldState("Hello\nWorld\nHello\nWorld"),
    lineLimits = TextFieldLineLimits.MultiLine(1, 4)
)

「

MultiLine 構成には次の特性があります。

  • minHeightInLines と maxHeightInLines の 2 つのパラメータを受け入れます。
  • テキスト フィールドの高さが minHeightInLines 以上である。
  • テキストがオーバーフローすると、折り返されます。
  • テキストにさらに行が必要な場合、フィールドは高さが maxHeightInLines になるまで拡大し、垂直方向にスクロールします。

Brush API を使用したスタイル入力

Brush API を使用すると、TextField でより高度なスタイル設定を行うことができます。次のセクションでは、Brush を使用して TextField 入力にカラー グラデーションを追加する方法について説明します。

Brush API を使用してテキストのスタイルを設定する方法については、Brush API を使用して高度なスタイル設定を有効にするをご覧ください。

TextStyle を使用してカラー グラデーションを実装する

TextField 内で入力時にカラー グラデーションを実装するには、選択したブラシを TextField の TextStyle として設定します。この例では、linearGradient を含む組み込みブラシを使用して、TextField にテキストが入力されるときに虹色のグラデーション効果を表示します。

val brush = remember {
    Brush.linearGradient(
        colors = listOf(Color.Red, Color.Yellow, Color.Green, Color.Blue, Color.Magenta)
    )
}
TextField(
    state = rememberTextFieldState(), textStyle = TextStyle(brush = brush)
)

テキスト フィールドにテキストが入力され、虹色のグラデーション効果が表示されている。
図 1. TextField のコンテンツに虹色のグラデーション効果を適用します。

テキスト フィールドの状態を管理する

TextField は、コンテンツと選択に TextFieldState という専用の状態ホルダー クラスを使用します。TextFieldState は、アーキテクチャのどこにでもホイスティングできるように設計されています。TextFieldState には、主に次の 2 つのプロパティがあります。

  • initialText: TextField の内容。
  • initialSelection: カーソルまたは選択範囲の位置を示します。

TextFieldState が onValueChange コールバックなどの他のアプローチと異なるのは、TextFieldState が入力フロー全体を完全にカプセル化している点です。これには、正しいバッキング データ構造の使用、フィルタとフォーマッタのインライン化、さまざまなソースからのすべての編集の同期が含まれます。

TextFieldState() を使用して、TextField で状態をホイスティングできます。このためには、rememberTextFieldState() 関数を使用することをおすすめします。rememberTextFieldState() は、コンポーザブルに TextFieldState インスタンスを作成し、状態オブジェクトが記憶されるようにして、組み込みの保存と復元の機能を提供します。

val usernameState = rememberTextFieldState()
TextField(
    state = usernameState,
    lineLimits = TextFieldLineLimits.SingleLine,
    placeholder = { Text("Enter Username") }
)

rememberTextFieldState には、空白のパラメータを指定することも、初期化時にテキストの値を表す初期値を渡すこともできます。後続の再コンポジションで別の値が渡されても、状態の値は更新されません。初期化後に状態を更新するには、TextFieldState で編集メソッドを呼び出します。

TextField(
    state = rememberTextFieldState(initialText = "Username"),
    lineLimits = TextFieldLineLimits.SingleLine,
)

テキスト フィールド内に「Username」というテキストが表示されている TextField。
図 2 TextField。初期テキストは「Username」です。

TextFieldBuffer でテキストを変更する

TextFieldBuffer は、StringBuilder と同様の機能を持つ、編集可能なテキスト コンテナとして機能します。テキスト コンテンツと選択に関する情報の両方を保持します。

TextFieldBuffer は、TextFieldState.edit、InputTransformation.transformInput、OutputTransformation.transformOutput などの関数のレシーバー スコープとしてよく使用されます。これらの関数では、必要に応じて TextFieldBuffer を読み取ったり更新したりできます。その後、これらの変更は TextFieldState に commit されるか、OutputTransformation の場合はレンダリング パイプラインに渡されます。

append、insert、replace、delete などの標準的な編集機能を使用して、バッファの内容を変更できます。選択状態を変更するには、selection: TextRange 変数を直接設定するか、placeCursorAtEnd や selectAll などのユーティリティ関数を使用します。選択範囲自体は TextRange で表されます。開始インデックスは含まれ、終了インデックスは含まれません。(3, 3) のように開始値と終了値が同じ TextRange は、文字が選択されていないカーソル位置を示します。

val phoneNumberState = rememberTextFieldState("1234567890")

TextField(
    state = phoneNumberState,
    keyboardOptions = KeyboardOptions(
        keyboardType = KeyboardType.Phone
    ),
    inputTransformation = InputTransformation.maxLength(10).then {
        if (!asCharSequence().isDigitsOnly()) {
            revertAllChanges()
        }
    },
    outputTransformation = OutputTransformation {
        if (length > 0) insert(0, "(")
        if (length > 4) insert(4, ")")
        if (length > 8) insert(8, "-")
    }
)

TextFieldState のテキストを編集する

状態変数を使用して状態を直接編集できるメソッドがいくつかあります。

  • edit: 状態の内容を編集できるようにし、TextFieldBuffer 関数を提供して、insert、replace、append などのメソッドを使用できるようにします。

    // Initial textFieldState text passed in is "I love Android"
    // textFieldState.text : I love Android
    // textFieldState.selection: TextRange(14, 14)
    textFieldState.edit { insert(14, "!") }
    // textFieldState.text : I love Android!
    // textFieldState.selection: TextRange(15, 15)
    textFieldState.edit { replace(7, 14, "Compose") }
    // textFieldState.text : I love Compose!
    // textFieldState.selection: TextRange(15, 15)
    textFieldState.edit { append("!!!") }
    // textFieldState.text : I love Compose!!!!
    // textFieldState.selection: TextRange(18, 18)
    textFieldState.edit { selectAll() }
    // textFieldState.text : I love Compose!!!!
    // textFieldState.selection: TextRange(0, 18)

  • setTextAndPlaceCursorAtEnd: 現在のテキストを消去し、指定されたテキストに置き換えて、カーソルを末尾に設定します。

    textFieldState.setTextAndPlaceCursorAtEnd("I really love Android")
    // textFieldState.text : I really love Android
    // textFieldState.selection : TextRange(21, 21)

  • clearText: すべてのテキストを消去します。

    textFieldState.clearText()
    // textFieldState.text :
    // textFieldState.selection : TextRange(0, 0)

他の TextFieldState 関数については、TextFieldState リファレンスをご覧ください。

ユーザー入力を変更する

以降のセクションでは、ユーザー入力を変更する方法について説明します。入力変換を使用すると、ユーザーが入力している間に TextField 入力をフィルタできます。一方、出力変換は、ユーザー入力を画面に表示する前にフォーマットします。

入力変換を使用してユーザー入力をフィルタする

入力変換を使用すると、ユーザーからの入力をフィルタできます。たとえば、TextField が米国の電話番号を受け取る場合は、10 桁のみを受け入れる必要があります。InputTransformation の結果は TextFieldState に保存されます。

一般的な InputTransformation ユースケース用の組み込みフィルタがあります。長さを制限するには、InputTransformation.maxLength() を呼び出します。

TextField(
    state = rememberTextFieldState(),
    lineLimits = TextFieldLineLimits.SingleLine,
    inputTransformation = InputTransformation.maxLength(10)
)

カスタム入力変換

InputTransformation は単一関数インターフェースです。カスタム InputTransformation を実装する場合は、TextFieldBuffer.transformInput をオーバーライドする必要があります。

class CustomInputTransformation : InputTransformation {
    override fun TextFieldBuffer.transformInput() {
    }
}

電話番号の場合は、TextField に数字のみを入力できるようにするカスタム入力変換を追加します。

class DigitOnlyInputTransformation : InputTransformation {
    override fun TextFieldBuffer.transformInput() {
        if (!asCharSequence().isDigitsOnly()) {
            revertAllChanges()
        }
    }
}

入力変換を連結する

テキスト入力に複数のフィルタを追加するには、then 拡張関数を使用して InputTransformation を連結します。フィルタは順番に実行されます。ベスト プラクティスとして、最も選択的なフィルタを最初に適用して、最終的に除外されるデータに対して不要な変換が行われないようにします。

TextField(
    state = rememberTextFieldState(),
    inputTransformation = InputTransformation.maxLength(6)
        .then(CustomInputTransformation()),
)

入力変換を追加すると、TextField 入力は最大 10 桁を受け入れます。

表示する前に入力をフォーマットする

OutputTransformation を使用すると、ユーザー入力を画面に表示する前にフォーマットできます。InputTransformation とは異なり、OutputTransformation で行われた書式設定は TextFieldState に保存されません。前述の電話番号の例を基に、適切な場所に括弧とダッシュを追加する必要があります。

米国の電話番号。かっこ、ダッシュ、対応するインデックスを使用して適切にフォーマットされています。
図 3. 適切な形式と対応するインデックスを持つ米国の電話番号。

OutputTransformation は、値ベースのテキスト フィールドの VisualTransformation を置き換えます。主な違いは、オフセット マッピングを計算しないことです。

OutputTransformation は単一の抽象メソッド インターフェースです。カスタム OutputTransformation を実装するには、transformOutput メソッドをオーバーライドする必要があります。

class CustomOutputTransformation : OutputTransformation {
    override fun TextFieldBuffer.transformOutput() {
    }
}

電話番号の書式を設定するには、インデックス 0 に開きかっこ、インデックス 4 に閉じかっこ、インデックス 8 にダッシュを OutputTransformation に追加します。

class PhoneNumberOutputTransformation : OutputTransformation {
    override fun TextFieldBuffer.transformOutput() {
        if (length > 0) insert(0, "(")
        if (length > 4) insert(4, ")")
        if (length > 8) insert(8, "-")
    }
}

次に、OutputTransformation を TextField に追加します。

TextField(
    state = rememberTextFieldState(),
    outputTransformation = PhoneNumberOutputTransformation()
)

変換の連携方法

次の図は、テキスト入力から変換、出力までのフローを示しています。

テキスト入力が変換を経てテキスト出力になる仕組みを視覚化したもの。
図 4. テキスト入力が変換を経てテキスト出力になる様子を示す図。
  1. 入力ソースから入力を受け取ります。
  2. 入力は InputTransformation でフィルタされ、TextFieldState に保存されます。
  3. 入力は OutputTransformation に渡されてフォーマットされます。
  4. 入力は TextField に表示されます。

キーボード オプションを設定する

TextField を使用すると、キーボード レイアウトなどのキーボード構成オプションを設定したり、キーボードでサポートされている場合は自動修正を有効にしたりできます。ソフトウェア キーボードが次に示すオプションに対応していない場合、一部のオプションは保証されない可能性があります。サポートされているキーボード オプションのリストは次のとおりです。

  • capitalization
  • autoCorrect
  • keyboardType
  • imeAction

KeyboardOptions クラスに、新しいブール値パラメータ showKeyboardOnFocus が追加されました。これは、TextFieldState と統合された TextField コンポーネントに特化して使用します。このオプションは、ユーザーの直接的な操作以外の方法(プログラムなど)で TextField がフォーカスを取得したときのソフトウェア キーボードの動作を制御します。

KeyboardOptions.showKeyboardOnFocus が true に設定されている場合、TextField が間接的にフォーカスを取得しても、ソフトウェア キーボードは自動的に表示されません。このような場合、ユーザーは TextField 自体を明示的にタップしてキーボードを表示する必要があります。

キーボード操作ロジックを定義する

Android のソフトウェア キーボードのアクション ボタンを使用すると、アプリ内でインタラクティブなレスポンスが可能になります。アクション ボタンの構成の詳細については、キーボード オプションを設定するをご覧ください。

ソフトウェア キーボードの操作ボタン(チェックマーク アイコン)が赤で囲まれている。
図 5. ソフトウェア キーボードの操作ボタン。

ユーザーがこの操作ボタンをタップしたときの動作を定義するには、onKeyboardAction パラメータを使用します。このパラメータは、KeyboardActionHandler という名前のオプションの関数型インターフェースを受け入れます。KeyboardActionHandler インターフェースには、単一のメソッド onKeyboardAction(performDefaultAction: () -> Unit) が含まれています。この onKeyboardAction メソッドの実装を提供することで、ユーザーがキーボードのアクション ボタンを押したときに実行されるカスタム ロジックを導入できます。

いくつかの標準的なキーボード操作タイプには、デフォルトの動作が組み込まれています。たとえば、アクション タイプとして ImeAction.Next または ImeAction.Previous を選択すると、デフォルトでは、それぞれ後続または先行する入力フィールドにフォーカスが移動します。同様に、ImeAction.Done に設定された操作ボタンは、通常、ソフトウェア キーボードを閉じます。これらのデフォルト機能は自動的に実行されるため、KeyboardActionHandler を指定する必要はありません。

これらのデフォルトのアクションに加えて、カスタム動作を実装することもできます。KeyboardActionHandler を指定すると、その onKeyboardAction メソッドが performDefaultAction 関数を受け取ります。カスタム ロジック内の任意の場所でこの performDefaultAction() 関数を呼び出すことで、現在の IME アクションに関連付けられている標準のデフォルトの動作をトリガーすることもできます。

TextField(
    state = textFieldViewModel.usernameState,
    keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
    onKeyboardAction = { performDefaultAction ->
        textFieldViewModel.validateUsername()
        performDefaultAction()
    }
)

このスニペットは、ユーザー名フィールドを備えた登録画面での一般的なユースケースを示しています。このフィールドでは、キーボード操作ボタンに ImeAction.Next が選択されています。この選択により、後続のパスワード フィールドにすばやくシームレスに移動できます。

この標準的なナビゲーションに加えて、ユーザーがパスワードを入力する際に、ユーザー名のバックグラウンド検証プロセスを開始する必要があります。ImeAction.Next に固有のデフォルトのフォーカス切り替え動作が、このカスタム検証ロジックとともに保持されるように、performDefaultAction() 関数が呼び出されます。performDefaultAction() を呼び出すと、基盤となるフォーカス管理システムが自動トリガーされ、フォーカスが次の適切な UI 要素に移動し、想定されるナビゲーション フローが維持されます。

安全なパスワード フィールドを作成する

SecureTextField は、パスワード フィールドを記述するための、状態ベースのテキスト フィールド上に構築されたコンポーザブルです。パスワード テキスト フィールドの作成には SecureTextField を使用することをおすすめします。デフォルトで文字入力が非表示になり、切り取りとコピーのアクションが無効になるためです。

SecureTextField には textObfuscationMode があり、ユーザーに文字入力がどのように表示されるかを制御します。textObfuscationMode には次のオプションがあります。

  • Hidden: すべての入力を非表示にします。パソコン プラットフォームでのデフォルトの動作。

    入力した文字が非表示になる、安全なテキスト フィールド。

  • Visible: すべての入力を表示します。

    入力した文字が表示されるセキュアなテキスト フィールド。

  • RevealLastTyped: 最後の文字を除くすべての入力を非表示にします。モバイル デバイスでのデフォルトの動作。

    最後に入力した文字のみが短時間表示されるセキュアなテキスト フィールド。

参考情報