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

TextField を使用すると、ユーザーはテキストを入力、変更できます。使用できるテキスト フィールドには、状態ベースのテキスト フィールドと値ベースのテキスト フィールドの 2 種類があります。コンテンツを表示するタイプを選択します。

TextField の状態を管理するためのより完全で信頼性の高いアプローチを提供するため、状態ベースのテキスト フィールドを使用することをおすすめします。次の表は、これらのタイプのテキスト フィールドの違いをまとめたもので、状態ベースのテキスト フィールドの主な利点を示しています。

機能

値ベースのテキスト フィールド

状態ベースのテキスト フィールド

州ベースの給付金

状態管理

onValueChange コールバックを使用してテキスト フィールドの状態を更新します。onValueChange によって報告された変更に基づいて、独自の状態の value を更新する必要があります。

TextFieldState オブジェクトを明示的に使用して、テキスト入力の状態(値、選択、コンポジション)を管理します。この状態は記憶して共有できます。

  • onValueChange コールバックが削除されたため、非同期動作を導入できなくなりました。
  • 状態は、再コンポーズ、構成、プロセスの終了後も保持されます。

ビジュアル変換

VisualTransformation を使用して、表示されるテキストの表示方法を変更します。通常、入力と出力のフォーマットを 1 つのステップで処理します。

InputTransformation を使用して、ユーザーの入力が状態に commit される前に変更し、OutputTransformation を使用して、基になる状態データを変更せずにテキスト フィールドの内容をフォーマットします。

  • OutputTransformation を使用して、元の未加工テキストと変換されたテキストの間のオフセット マッピングを指定する必要がなくなりました。

行数の上限

singleLine: Boolean, maxLines: Int と minLines: Int を受け入れて、行数を制御します。

lineLimits: TextFieldLineLimits を使用して、テキスト フィールドが占有できる行数の最小値と最大値を構成します。

  • TextFieldLineLimits 型の lineLimits パラメータを指定することで、行制限を構成する際のあいまいさを排除しました。

セキュア テキスト フィールド

なし

SecureTextField は、パスワード フィールドを記述するための、状態ベースのテキスト フィールド上に構築されたコンポーザブルです。

  • 内部でセキュリティを最適化できます。textObfuscationMode を使用した事前定義の UI が付属しています。

このページでは、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 を使用する必要があります。

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. 適切な形式と対応するインデックスを持つ米国の電話番号。

これは、値ベースの TextField で 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

参考情報