Wie lösche ich den TextField-Fokus beim Schließen der Tastatur und verhindere, dass zwei Rückdrücke erforderlich sind, um die App in Jetpack Compose zu beenden?

Lesezeit: 6 Minuten

Benutzer-Avatar
Pylyp Dukhov

Ich verwende BasicTextField.

Wenn ich mit der Bearbeitung beginne, wird die Schaltfläche „Zurück“ zur Schaltfläche „Tastatur ausblenden“ (Pfeil nach unten).

Beim ersten Drücken der Zurück-Taste wird die Tastatur ausgeblendet, aber der Fokus liegt immer noch auf dem Textfeld. Beide onFocusChanged und BackPressHandler Handler werden nicht angerufen.

Zweites Drücken der Zurück-Taste löscht den Fokus: onFocusChanged heißt und BackPressHandler ist nicht.

BackHandler {
    println("BackPressHandler")
}
val valueState = remember { mutableStateOf(TextFieldValue(text = "")) }
BasicTextField(
    value = valueState.value,
    onValueChange = {
        valueState.value = it
    },
    modifier = Modifier
        .fillMaxWidth()
        .onFocusChanged {
            println("isFocused ${it.isFocused}")
        }
)

Beim dritten Mal funktioniert BackHandler einwandfrei. Ich habe es nur zum Testen verwendet, ich sollte es hier nicht brauchen, es wurde erwartet, dass der Fokus nach dem ersten Antippen der Zurück-Taste verloren geht

  • Laut den Release Notes ist dies in 1.1.0 behoben jetc.dev/issues/077.html

    – mmm111mm

    24. August 2021 um 6:01 Uhr

  • @mmm111mmm aus dem Gespräch rein Thema verfassen Ich verstehe, dass nach dem Fix Second Back die App geschlossen wird, aber das Schließen der Tastatur wird den Fokus immer noch nicht löschen, was immer noch nicht das ist, was ich in meiner App erwarte

    – Pylyp Dukhov

    24. August 2021 um 6:05 Uhr

Benutzer-Avatar
Pylyp Dukhov

Da ist ein Thema verfassen mit fokussiertem Textfeld verhindert, dass die Zurück-Schaltfläche die App schließt, wenn die Tastatur ausgeblendet ist. Es ist als behoben markiert, wird aber in einer zukünftigen Version enthalten sein, nicht in 1.0

Aber so wie ich es verstehe, ist die Tatsache, dass das Textfeld den Fokus nicht verliert, nachdem die Tastatur geschlossen wurde, ein beabsichtigtes Verhalten auf Android (wegen einer möglichen angeschlossenen Tastatur? Ich habe den Grund nicht verstanden). Und so funktioniert es auch im alten Android-Layout

Es scheint mir seltsam, also kam ich mit dem folgenden Modifikator, der den Fokus aufgibt, wenn die Tastatur verschwindet:

fun Modifier.clearFocusOnKeyboardDismiss(): Modifier = composed {
    var isFocused by remember { mutableStateOf(false) }
    var keyboardAppearedSinceLastFocused by remember { mutableStateOf(false) }
    if (isFocused) {
        val imeIsVisible = LocalWindowInsets.current.ime.isVisible
        val focusManager = LocalFocusManager.current
        LaunchedEffect(imeIsVisible) {
            if (imeIsVisible) {
                keyboardAppearedSinceLastFocused = true
            } else if (keyboardAppearedSinceLastFocused) {
                focusManager.clearFocus()
            }
        }
    }
    onFocusEvent {
        if (isFocused != it.isFocused) {
            isFocused = it.isFocused
            if (isFocused) {
                keyboardAppearedSinceLastFocused = false
            }
        }
    }
}

ps Sie müssen installieren Begleiter-Einsätze Abhängigkeit für LocalWindowInsets.current.ime

Pss Seit Komponieren 1.2.0-alpha03, Begleiter-Einsätze wurde meist eingezogen Stiftung komponierenKasse Migrationsleitfaden für mehr Details. LocalWindowInsets.current.ime sollte durch ersetzt werden WindowInsets.ime.


Verwendungszweck:

BasicTextField(
    value = valueState.value,
    onValueChange = {
        valueState.value = it
    },
    modifier = Modifier
        .clearFocusOnKeyboardDismiss()
)

  • Eigentlich bekomme ich das nicht zum Laufen LocalWindowInsets.current.ime.isVisible gibt immer false zurück, wenn ich die Soft-Tastatur öffne und schließe. Zumindest im Emulator.

    – mmm111mm

    19. August 2021 um 11:22 Uhr

  • @mmm111mmm bist du gefolgt WindowCompat.setDecorFitsSystemWindows(window, false) Notiz von accompanist insets?

    – Pylyp Dukhov

    19. August 2021 um 11:27 Uhr

  • Vielen Dank. Und ich fügte hinzu ProvideWindowInsets. Jetzt ist mein Problem, meinen Bildschirm zu gestalten, jetzt werden die Statusleiste und die untere Navigationsleiste nicht berücksichtigt …

    – mmm111mm

    19. August 2021 um 11:42 Uhr

  • @mmm111mmm, wenn Sie nicht vorhaben, mit ihnen zu interagieren, fügen Sie einfach hinzu systemBarsPadding für die ganze app, so etwas wie diese. Andernfalls müssen Sie diese Polsterung bei Bedarf zu jedem einzelnen Bildschirm hinzufügen

    – Pylyp Dukhov

    19. August 2021 um 11:46 Uhr


  • Ja, das funktioniert, aber wenn ich jetzt die Tastatur mit einem am unteren Bildschirmrand angehängten Textfeld öffne, wird das Textfeld jetzt von der Tastatur verdeckt, wie ich es verwende android:windowSoftInputMode="adjustResize. Dies ist die einzige funktionierende Lösung, die ich finden kann. Aber ich denke, es ist ein Problem mit Compose, und das ist jetzt zu komplex: Entfernen Sie Fenstereinschübe, damit Sie das Öffnen und Schließen einer Tastatur erkennen können, um den Fokus eines Textfelds aufzuheben, und Sie müssen auch Ihre App ohne Einschübe entwerfen.

    – mmm111mm

    19. August 2021 um 11:58 Uhr

Danke an alle Antworten hier. Nachdem Sie sich auf die Antworten hier bezogen haben, finden Sie hier eine Lösung ohne Verwendung einer Bibliothek

1. Erstellen Sie eine Erweiterung in View, um festzustellen, ob die Tastatur geöffnet ist oder nicht

fun View.isKeyboardOpen(): Boolean {
    val rect = Rect()
    getWindowVisibleDisplayFrame(rect);
    val screenHeight = rootView.height
    val keypadHeight = screenHeight - rect.bottom;
    return keypadHeight > screenHeight * 0.15
}

2. Erstellen Sie einen beobachtbaren Zustand, um festzustellen, ob eine Tastatur geöffnet ist oder nicht

Dadurch werden globale Layout-Updates auf LocalView überwacht, bei denen wir bei jedem Ereignis den Status zum Öffnen/Schließen der Tastatur prüfen.

@Composable
fun rememberIsKeyboardOpen(): State<Boolean> {
    val view = LocalView.current

    return produceState(initialValue = view.isKeyboardOpen()) {
        val viewTreeObserver = view.viewTreeObserver
        val listener = OnGlobalLayoutListener { value = view.isKeyboardOpen() }
        viewTreeObserver.addOnGlobalLayoutListener(listener)

        awaitDispose { viewTreeObserver.removeOnGlobalLayoutListener(listener)  }
    }
}

3. Modifikator erstellen

Dieser Modifikator sorgt dafür, dass der Fokus auf sichtbare/unsichtbare Tastaturereignisse gelöscht wird.

fun Modifier.clearFocusOnKeyboardDismiss(): Modifier = composed {

    var isFocused by remember { mutableStateOf(false) }
    var keyboardAppearedSinceLastFocused by remember { mutableStateOf(false) }

    if (isFocused) {
        val isKeyboardOpen by rememberIsKeyboardOpen()

        val focusManager = LocalFocusManager.current
        LaunchedEffect(isKeyboardOpen) {
            if (isKeyboardOpen) {
                keyboardAppearedSinceLastFocused = true
            } else if (keyboardAppearedSinceLastFocused) {
                focusManager.clearFocus()
            }
        }
    }
    onFocusEvent {
        if (isFocused != it.isFocused) {
            isFocused = it.isFocused
            if (isFocused) {
                keyboardAppearedSinceLastFocused = false
            }
        }
    }
}

4. Verwenden Sie es

Verwenden Sie es schließlich mit TextField zusammensetzbar

BasicTextField(Modifier.clearFocusOnKeyboardDismiss())

Benutzer-Avatar
mmm111mm

Ich habe eine wohl einfachere Lösung mit dem Baumbeobachter von Android gefunden.

Sie müssen keine andere Bibliothek verwenden oder die Einsätze aus Ihrem Layout entfernen.

Es löscht den Fokus beim Verfassen jedes Mal, wenn die Tastatur ausgeblendet ist.

Hoffentlich wird dies wann nicht mehr nötig sein diese es ist veröffentlicht worden.

class MainActivity : ComponentActivity() {

  var kbClosed: () -> Unit = {}
  var kbClosed: Boolean = false

  override fun onCreate(state: Bundle?) {
    super.onCreate(state)
    setContent {
      val focusManager = LocalFocusManager.current
      kbClosed = {
        focusManager.clearFocus()
      }
      MyComponent()
    }
    setupKeyboardDetection(findViewById<View>(android.R.id.content))
  }

  fun setupKeyboardDetection(contentView: View) {
    contentView.viewTreeObserver.addOnGlobalLayoutListener {
      val r = Rect()
      contentView.getWindowVisibleDisplayFrame(r)
      val screenHeight = contentView.rootView.height
      val keypadHeight = screenHeight - r.bottom
      if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
        kbClosed = false
        // kb opened
      } else if(!kbClosed) {
        kbClosed = true
        kbClosed()
      }
    }
  }
}

Fügen Sie in einer Klasse, die von Application erbt, den folgenden Code hinzu, um zu erkennen, wann die Hauptaktivität erstellt wird, und fügen Sie den Code hinzu, der erkennt, wann die Tastatur angezeigt oder ausgeblendet wird:

import android.app.Activity
import android.app.Application
import android.content.res.Resources
import android.graphics.Rect
import android.os.Bundle
import android.util.DisplayMetrics
import androidx.compose.runtime.mutableStateOf

class App : Application() {

    private val activityLifecycleTracker: AppLifecycleTracker = AppLifecycleTracker()

    override fun onCreate() {
        super.onCreate()
        registerActivityLifecycleCallbacks(activityLifecycleTracker)
    }

    companion object {
        val onKeyboardClosed = mutableStateOf(false)
    }

    /**
     * Callbacks for handling the lifecycle of activities.
     */
    class AppLifecycleTracker : ActivityLifecycleCallbacks {

        override fun onActivityCreated(activity: Activity, p1: Bundle?) {
            val displayMetrics: DisplayMetrics by lazy { Resources.getSystem().displayMetrics }
            val screenRectPx = displayMetrics.run { Rect(0, 0, widthPixels, heightPixels) }

            // Detect when the keyboard closes.
            activity.window.decorView.viewTreeObserver.addOnGlobalLayoutListener {
                val r = Rect()
                activity.window.decorView.getWindowVisibleDisplayFrame(r)
                val heightDiff: Int = screenRectPx.height() - (r.bottom - r.top)

                onKeyboardClosed.value = (heightDiff <= 100)
            }
        }

        override fun onActivityStarted(activity: Activity) {
        }

        override fun onActivityResumed(activity: Activity) {
        }

        override fun onActivityPaused(p0: Activity) {
        }

        override fun onActivityStopped(activity: Activity) {
        }

        override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) {
        }

        override fun onActivityDestroyed(p0: Activity) {
        }
    }
}

Fügen Sie die folgende Modifikatorerweiterung hinzu:

@Stable
fun Modifier.clearFocusOnKeyboardClose(focusManager: FocusManager): Modifier {
    if (App.onKeyboardClosed.value) {
        focusManager.clearFocus()
    }

    return this
}

Fügen Sie in Ihrem Composable einen Verweis auf den FocusManager hinzu und fügen Sie den Modifikator zu Ihrem TextField hinzu:

@Composable
fun MyComposable() {
   val focusManager = LocalFocusManager.current
   
    OutlinedTextField(
                     modifier = Modifier.clearFocusOnKeyboardClose(focusManager = focusManager)
    )
}

Das TextField löscht seinen Fokus, wenn die Tastatur geschlossen wird.

1014120cookie-checkWie lösche ich den TextField-Fokus beim Schließen der Tastatur und verhindere, dass zwei Rückdrücke erforderlich sind, um die App in Jetpack Compose zu beenden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy