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

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

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

機能

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

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

州ベースの給付

状態管理

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

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

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

ビジュアル変換

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

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

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

行数上限

singleLine: Boolean, maxLines: IntminLines: Int を受け取り、行数を制御します。

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

  • TextFieldLineLimits タイプの lineLimits パラメータを指定することで、行数の上限を構成する際の曖昧さを解消しました。

保護されたテキスト フィールド

なし

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

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

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

TextField の実装を選択する

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

  1. TextField はマテリアル デザインの実装であり、次のようなマテリアル デザイン ガイドラインに沿っているため、この実装を選択することをおすすめします。
    • デフォルトのスタイル設定は塗りつぶしです。
    • OutlinedTextField は、枠線付きのスタイル設定バージョンです。
  2. BasicTextField を使用すると、ユーザーはハードウェア キーボードまたはソフトウェア キーボードを使用してテキストを編集できます。ただし、ヒントやプレースホルダのような装飾は用意されていません。

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

「

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

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

スタイルTextField

TextFieldBasicTextField は、カスタマイズ用のパラメータを多数共有しています。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 による入力のスタイル設定

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

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

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

TextField 内で入力するときに色付きのグラデーションを実装するには、TextFieldTextStyle として任意のブラシを設定します。この例では、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)
)

buildAnnotatedString と SpanStyle を linearGradient とともに使用して、テキストの一部のみをカスタマイズする。
図 1. TextField コンテンツのレインボー グラデーション エフェクト。

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

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

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

TextFieldStateonValueChange コールバックなどの他のアプローチと異なるのは、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,
)

テキスト フィールド内に「ユーザー名」というテキストが表示された TextField。
図 2. TextField(初期テキストは「ユーザー名」)

TextFieldBuffer でテキストを変更する

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

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

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

val phoneNumberState = rememberTextFieldState()

LaunchedEffect(phoneNumberState) {
    phoneNumberState.edit { // TextFieldBuffer scope
        append("123456789")
    }
}

TextField(
    state = phoneNumberState,
    inputTransformation = InputTransformation { // TextFieldBuffer scope
        if (asCharSequence().isDigitsOnly()) {
            revertAllChanges()
        }
    },
    outputTransformation = OutputTransformation {
        if (length > 0) insert(0, "(")
        if (length > 4) insert(4, ")")
        if (length > 8) insert(8, "-")
    }
)

TextFieldState のテキストを編集する

状態変数を介して状態を直接編集する方法はいくつかあります。

  • edit: 状態の内容を編集でき、TextFieldBuffer 関数を提供するため、insertreplaceappend などのメソッドを使用できます。

    val usernameState = rememberTextFieldState("I love Android")
    // textFieldState.text : I love Android
    // textFieldState.selection: TextRange(14, 14)
    usernameState.edit { insert(14, "!") }
    // textFieldState.text : I love Android!
    // textFieldState.selection: TextRange(15, 15)
    usernameState.edit { replace(7, 14, "Compose") }
    // textFieldState.text : I love Compose!
    // textFieldState.selection: TextRange(15, 15)
    usernameState.edit { append("!!!") }
    // textFieldState.text : I love Compose!!!!
    // textFieldState.selection: TextRange(18, 18)
    usernameState.edit { selectAll() }
    // textFieldState.text : I love Compose!!!!
    // textFieldState.selection: TextRange(0, 18)

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

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

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

    usernameState.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 (!TextUtils.isDigitsOnly(asCharSequence())) {
            revertAllChanges()
        }
    }
}

入力変換を連結する

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

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

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

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

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

かっこ、ダッシュ、対応するインデックスを使用して正しく形式設定された米国の電話番号。
図 3. 正しい形式の米国の電話番号と対応するインデックス。

これは、値ベースの TextFieldVisualTransformation を処理する更新された方法です。主な違いは、オフセット マッピングを計算する必要がないことです。

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

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

電話番号をフォーマットするには、OutputTransformation にインデックス 0 に左括弧、インデックス 4 に右括弧、インデックス 8 にダッシュを追加します。

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

次に、OutputTransformationTextField に追加します。

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

変換の連携の仕組み

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

テキスト入力が変換されてテキスト出力になるまでのプロセスを可視化したもの。
図 4. テキスト入力が変換されてテキスト出力になるまでの流れを示す図。
  1. 入力ソースから入力が受信されます。
  2. 入力は InputTransformation でフィルタされ、TextFieldState に保存されます。
  3. 入力は OutputTransformation を介して渡され、フォーマットされます。
  4. 入力は TextField に表示されます。

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

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

  • capitalization
  • autoCorrect
  • keyboardType
  • imeAction

参考情報