Android-Insets: Umgang mit Ängsten und Vorbereitung auf Android Q.

Android Q ist die zehnte Version von Android mit API-Level 29. Eine der Hauptideen der neuen Version ist das Konzept von Rand zu Rand, wenn Anwendungen den gesamten Bildschirm von unten nach oben einnehmen. Dies bedeutet, dass die Statusleiste und die Navigationsleiste transparent sein müssen. Wenn sie jedoch transparent sind, gibt es keine Systembenutzeroberfläche - sie überlappt die interaktiven Komponenten der Anwendung. Dieses Problem wird mit Einsätzen gelöst.

Mobile Entwickler vermeiden Insets, sie verursachen Angst in ihnen. In Android Q ist es jedoch nicht möglich, Insets zu umgehen - Sie müssen sie studieren und anwenden. Insets sind in der Tat nicht kompliziert: Sie zeigen, welche Bildschirmelemente sich mit der Systemschnittstelle überschneiden, und schlagen vor, wie das Element so verschoben werden soll, dass es nicht mit der Systembenutzeroberfläche in Konflikt steht. Konstantin Tskhovrebov wird erzählen, wie Insets funktionieren und wie sie nützlich sind.


Konstantin Tskhovrebov (Terrakok) arbeitet in Redmadrobot . Ist seit 10 Jahren mit Android beschäftigt und hat viel Erfahrung in verschiedenen Projekten gesammelt, in denen es keinen Platz für Insets gab. Sie haben es immer geschafft, irgendwie herumzukommen. Konstantin wird über eine lange Geschichte der Vermeidung des Insets-Problems, über das Studium und den Kampf gegen Android berichten. Er wird typische Aufgaben aus seiner Erfahrung betrachten, bei denen Einfügungen angewendet werden könnten, und zeigen, wie man aufhört, Angst vor der Tastatur zu haben, ihre Größe erkennt und auf das Erscheinungsbild reagiert.

Hinweis. Der Artikel basiert auf einem Bericht von Konstantin auf der Saint AppsConf 2019 . In dem Bericht wurden Materialien aus mehreren Artikeln zu Einsätzen verwendet. Link zu diesen Materialien am Ende.

Typische Aufgaben


Farbstatusleiste. In vielen Projekten zeichnet der Designer eine Farbstatusleiste. Es wurde in Mode, als Android 5 mit dem neuen Material Design kam.



Wie male ich die Statusleiste? Elementar - hinzufügen colorPrimaryDarkund die Farbe wird ersetzt.

 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    ...
    <item name="colorPrimaryDark">@color/colorAccent</item>
    ...
</style>

Für Android über der fünften Version (API ab 21) können Sie im Thema spezielle Parameter festlegen:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    ...
    <item name="android:statusBarColor">@color/colorAccent</item>
    ...
</style>

Mehrfarbige Statusleiste . Manchmal zeichnen Designer auf verschiedenen Bildschirmen die Statusleiste in verschiedenen Farben.



Es ist in Ordnung, der einfachste Weg, mit verschiedenen Themen in verschiedenen Aktivitäten zu arbeiten .

Der interessantere Weg ist, die Farben direkt zur Laufzeit zu ändern .

override fun onCreateView(...): View {
    requireActivity().window.statusBarColor = requireContext().getColor(R.color.colorPrimary)
    ...
}

Der Parameter wird durch ein spezielles Flag geändert. Die Hauptsache ist jedoch, nicht zu vergessen, die Farbe wieder zu ändern, wenn der Benutzer den Bildschirm wieder verlässt.

Transparente Statusleiste. Das ist schwieriger. In den meisten Fällen ist Transparenz mit Karten verbunden, da Transparenz auf Karten am besten sichtbar ist. In diesem Fall setzen wir wie bisher einen speziellen Parameter:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">    
    ...
    <item name="android:windowTranslucentStatus">true</item>
    ...
</style>

Hier gibt es natürlich einen bekannten Trick - höher einzurücken, sonst wird die Statusleiste über das Symbol gelegt und es wird hässlich.


Aber auf anderen Bildschirmen bricht alles.



Wie löse ich das Problem? Die erste Methode, die mir in den Sinn kommt, sind verschiedene Aktivitäten: Wir haben verschiedene Themen, verschiedene Parameter, sie funktionieren unterschiedlich.

Arbeiten Sie mit der Tastatur. Wir vermeiden Einfügungen nicht nur in der Statusleiste, sondern auch bei der Arbeit mit der Tastatur.



Niemand mag die Option auf der linken Seite, aber um sie in eine Option auf der rechten Seite zu verwandeln, gibt es eine einfache Lösung.

<activity
    ...
    android:windowSoftInputMode="adjustResize">
    ...
</activity>

Die Größe der Aktivität kann jetzt geändert werden, wenn die Tastatur angezeigt wird. Es funktioniert einfach und streng. Aber vergessen Sie nicht einen anderen Trick - wickeln Sie alles ein ScrollView. Plötzlich nimmt die Tastatur den gesamten Bildschirm ein und es wird ein kleiner Streifen darüber sein?

Es gibt Zeiten, in denen wir das Layout ändern möchten, wenn die Tastatur angezeigt wird. Ordnen Sie beispielsweise die Schaltflächen anders an oder verbergen Sie das Logo.



Wir haben so viele Krücken mit einer Tastatur und einer transparenten Statusleiste trainiert, dass es jetzt schwer ist, uns aufzuhalten. Gehen Sie zu StackOverflow und kopieren Sie den schönen Code.

boolean isOpened = false;

public void setListenerToRootView() {
    final View activityRootView = getWindow().getDecorView().findViewById(android.R.id.content);
    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
    
            int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();      
            if (heightDiff > 100) { // 99% of the time the height diff will be due to a keyboard.       
                Toast.makeText(getApplicationContext(), "Gotcha!!! softKeyboardup", 0).show();

                if (isOpened == false) {
                   //Do two things, make the view top visible and the editText smaller
                }
                isOpened = true;
            } else if (isOpened == true) {
                Toast.makeText(getApplicationContext(), "softkeyborad Down!!!", 0).show();
                isOpened = false;
            }    
        }
    });
}

Hier wird sogar die Wahrscheinlichkeit des Auftretens der Tastatur berechnet. Der Code funktioniert, in einem der Projekte haben wir ihn sogar verwendet, aber für eine lange Zeit.

Viele Krücken in den Beispielen sind mit der Verwendung verschiedener Aktivitäten verbunden. Aber sie sind nicht nur schlecht, weil sie Krücken sind, sondern auch aus anderen Gründen: dem Problem des „Kaltstarts“, der Asynchronität. Ich habe die Probleme ausführlicher im Artikel „ Führerschein für ein Auto oder warum Anwendungen eine Einzelaktivität sein sollten “ beschrieben. Aus der Dokumentation von Google geht außerdem hervor, dass der empfohlene Ansatz eine Einzelaktivitätsanwendung ist.

Was haben wir vor Android 10 gemacht?


Wir (bei Redmadrobot) entwickeln Anwendungen von ziemlich hoher Qualität, haben jedoch Einfügungen lange vermieden. Wie haben wir es geschafft, sie ohne große Krücken und in einer Aktivität zu vermeiden?

Hinweis. Screenshots und Code aus meinem Lieblingsprojekt GitFox .

Stellen Sie sich den Anwendungsbildschirm vor. Als wir unsere Anwendungen entwickelten, hätten wir nie gedacht, dass es darunter eine transparente Navigationsleiste geben könnte. Gibt es unten einen schwarzen Balken? Also, Benutzer sind daran gewöhnt.



Oben setzen wir zunächst den Parameter, dass die Statusleiste schwarz mit Transparenz ist. Wie sieht es in Bezug auf Layout und Code aus?



In der Abbildung Abstraktion: Der rote Block ist die Aktivität der Anwendung, der blaue Block ist ein Fragment mit einem Navigationsbot (mit Registerkarten), und darin werden grüne Fragmente mit Inhalt gewechselt. Es ist ersichtlich, dass sich die Symbolleiste nicht unter der Statusleiste befindet. Wie haben wir das erreicht?

Android hat eine knifflige Flagge fitSystemWindow. Wenn Sie den Wert auf "true" setzen, fügt der Container sich selbst eine Auffüllung hinzu, sodass nichts in ihm unter die Statusleiste fällt. Ich glaube, diese Flagge ist die offizielle Krücke von Google für diejenigen, die Angst vor Einsätzen haben. Verwenden Sie alles funktioniert relativ gut ohne Einsätze.

Das Flag FitSystemWindow=”true”fügt dem von Ihnen angegebenen Container eine Auffüllung hinzu. Die Hierarchie ist jedoch wichtig: Wenn einer der Elternteile dieses Flag auf „true“ setzt, wird seine Verteilung nicht berücksichtigt, da der Container die Einrückung bereits angewendet hat.

Die Flagge funktioniert, aber ein anderes Problem tritt auf. Stellen Sie sich einen Bildschirm mit zwei Registerkarten vor. Beim Umschalten wird eine Transaktion gestartet, die ein replaceFragment untereinander verursacht, und alles bricht zusammen.



Für das zweite Fragment ist ebenfalls ein Flag gesetzt FitSystemWindow=”true”, und dies sollte nicht passieren. Aber was ist passiert, warum? Die Antwort ist, dass dies eine Krücke ist und manchmal nicht funktioniert.

Wir haben jedoch eine Lösung für den Stapelüberlauf gefunden : Verwenden Sie root nicht für Fragmente FrameLayout, sondern CoordinatorLayout. Es wurde für andere Zwecke erstellt, funktioniert aber hier.

Warum funktioniert es?


Mal sehen, in was in der Quelle passiert CoordinatorLayout.

@Override
public void onAttachedToWindow() {   
    super.onAttachedToWindow();
    ...    
    if (mLastInsets == null && ViewCompat.getFitsSystemWindows(this)) {         
        // We're set to fitSystemWindows but we haven't had any insets yet...
        // We should request a new dispatch of window insets
        ViewCompat.requestApplyInsets(this);
    }     
    ...
}

Wir sehen einen schönen Kommentar, dass hier Einsätze benötigt werden, aber nicht. Wir müssen sie erneut fragen, wenn wir ein Fenster anfordern. Wir haben herausgefunden, dass Insets irgendwie im Inneren funktionieren, aber wir wollen nicht mit ihnen arbeiten und gehen Coordinator.

Im Frühjahr 2019 haben wir die Anwendung entwickelt. Zu diesem Zeitpunkt ist Google I / O gerade vorbei. Wir haben noch nicht alles herausgefunden und haben weiterhin an Vorurteilen festgehalten. Wir haben jedoch festgestellt, dass das Wechseln der Registerkarten unten etwas langsam ist, da wir ein komplexes Layout haben, das mit einer Benutzeroberfläche geladen ist. Wir finden einen einfachen Weg, um dies zu lösen - wechseln Sie replacezu, show/hidedamit Sie das Layout nicht jedes Mal neu erstellen.



Wir ändern uns und wieder funktioniert nichts - alles ist kaputt! Anscheinend ist es einfach nicht möglich, Krücken zu streuen. Sie müssen verstehen, warum sie funktioniert haben. Wir studieren den Code und es stellt sich heraus, dass jede ViewGroup auch mit Insets arbeiten kann.


@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {    
    insets = super.dispatchApplyWindowInsets(insets);     
    if (!insets.isConsumed()) {         
        final int count = getChildCount();        
        for (int i = 0; i < count; i++) {
            insets = getChildAt(i).dispatchApplyWindowInsets(insets);             
            if (insets.isConsumed()) {          
                break;
            }
        }
    }
    return insets;
}

Es gibt eine solche Logik: Wenn mindestens jemand bereits Insets verarbeitet hat, werden diese von allen nachfolgenden Ansichten in der ViewGroup nicht empfangen. Was bedeutet das für uns? Lassen Sie mich Ihnen ein Beispiel für unser FrameLayout zeigen, das die Tabs wechselt.



Im Inneren befindet sich das erste Fragment, für das das Flag gesetzt ist fitSystemWindow=”true”. Dies bedeutet, dass das erste Fragment Einfügungen verarbeitet. Danach nennen wir das HIDEerste Fragment und das SHOWzweite. Der erste bleibt jedoch im Layout - seine Ansicht bleibt nur versteckt.

Der Container geht durch seine Ansicht: Er nimmt das erste Fragment, gibt ihm Einfügungen und daraus fitSystemWindow=”true”- er hat sie genommen und verarbeitet. Perfekt sah FrameLayout so aus, als ob die Einfügungen verarbeitet wurden, und gab sie nicht an das zweite Fragment weiter. Alles funktioniert wie es sollte. Aber das passt nicht zu uns, was sollen wir tun?

Wir schreiben unsere eigene ViewGroup


Wir kamen aus Java und dort OOP in seiner ganzen Pracht, also beschlossen wir zu erben. Wir haben unsere eigene ViewGroup geschrieben, aus der wir die Methode neu definiert haben dispatchApplyWindowInsets. Es funktioniert so, wie wir es brauchen: Es gibt den Kindern immer die Einsätze zurück, die gekommen sind, unabhängig davon, ob wir sie verarbeitet haben oder nicht.

class WindowInsetFrameLayout @JvmOverloads constructor(
    context: Context,      
    attrs: AttributeSet? = null,     
    defStyleRes: Int = 0
) : FrameLayout(context, attrs, defStyleRes) {

    override fun dispatchApplyWindowInsets(insets: WindowInsets): WindowInsets { 
        for (child in (0 until childCount)) {
            getChildAt(child).dispatchApplyWindowInsets(insets)
        }
        return insets.consumeSystemWindowInsets()
    }
}

Custom ViewGroup funktioniert, auch das Projekt ist alles, was wir brauchen. Aber er erreicht immer noch keine allgemeine Lösung. Als wir herausfanden, was sie uns auf Google IO mitgeteilt haben, stellten wir fest, dass wir nicht mehr so ​​handeln können.

Android 10


Android 10 zeigte uns zwei wichtige UI-Konzepte, deren Einhaltung unbedingt empfohlen wird: Edge-to-Edge- und Gesten-Navigation.

Kante an Kante. Dieses Konzept legt nahe, dass der Inhalt der Anwendung den gesamten möglichen Platz auf dem Bildschirm einnehmen sollte . Für uns als Entwickler bedeutet dies, dass Anwendungen unter den Systemfenstern Statusleiste und Navigationsleiste platziert werden sollten .



Bisher konnten wir dies irgendwie ignorieren oder nur unter der Statusleiste platziert werden.

Die Listen sollten nicht nur zum letzten Element, sondern auch weiter gescrollt werden, um nicht unter der Navigationsleiste zu bleiben.


Gestische Navigation. Dieses zweite wichtige Konzept ist die Gesten-Navigation . Sie können die Anwendung mit Ihren Fingern vom Bildschirmrand aus steuern. Der Gestenmodus ist nicht Standard, alles sieht anders aus, aber jetzt können Sie nicht zwischen zwei verschiedenen Navigationsleisten wechseln.

In diesem Moment haben wir festgestellt, dass nicht alles so einfach ist. Es wird nicht möglich sein, Einfügungen weiter zu vermeiden, wenn wir Qualitätsanwendungen entwickeln wollen. Es ist Zeit, die Dokumentation zu studieren und zu verstehen, was Einschübe sind.

Einschübe Was müssen Sie über sie wissen?


Insets wurden erstellt, um Entwickler zu erschrecken.
Sie haben dies perfekt gemacht, seit es in Android 5 erschien.

Natürlich ist nicht alles so beängstigend. Das Konzept der Einschübe ist einfach: Sie melden eine Systemoberflächenüberlagerung auf dem Anwendungsbildschirm. Dies kann eine Navigationsleiste, eine Statusleiste oder eine Tastatur sein. Die Tastatur ist auch eine normale Systembenutzeroberfläche, die über der Anwendung liegt. Es ist nicht notwendig, es mit Krücken zu verarbeiten, sondern nur mit Einsätzen.

Das Ziel von Insets ist die Lösung von Konflikten . Befindet sich beispielsweise ein anderes Element über unserer Schaltfläche, können wir die Schaltfläche verschieben, damit der Benutzer die Anwendung weiterhin verwenden kann. Verwenden Sie für Android 10 und andere Versionen

, um mit dem Einschub umzugehen . Es gibt 5 verschiedene Arten von Insets in Android 10WindowlnsetsWindowInsetsCompat

und ein "Bonus", der nicht Einschub genannt wird, sondern anders. Wir werden uns mit allen Typen befassen, da die meisten nur eines wissen - Systemfenster-Einfügungen.

Systemfenstereinfügungen


Eingeführt in Android 5.0 (API 21). Durch die Methode erhalten getSystemWindowInsets().

Dies ist die Hauptart von Insets, mit denen Sie lernen müssen, wie man arbeitet, da der Rest auf die gleiche Weise funktioniert. Sie werden benötigt, um zuerst die Statusleiste und dann die Navigationsleiste und die Tastatur zu verarbeiten. Sie lösen beispielsweise ein Problem, wenn sich die Navigationsleiste über der Anwendung befindet. Wie im Bild: Die Schaltfläche blieb unter der Navigationsleiste, der Benutzer kann nicht darauf klicken und ist sehr unglücklich.



Gewindeschneidbare Elementeinsätze


Erscheint nur in Android 10. Erhalten durch die Methode getTappableElementInsets().

Diese Einfügungen sind nur für die Handhabung verschiedener Navigationsleistenmodi nützlich . Wie Chris Bane selbst sagt , können Sie diese Art von Insets vergessen und nur Systemfenster-Insets umgehen. Wenn Sie jedoch möchten, dass die Anwendung 100% cool und nicht zu 99,9% cool ist, sollten Sie sie verwenden.

Sehen Sie das Bild an.



Oben zeigt die purpurrote Farbe Systemfenster-Einfügungen an, die in verschiedenen Modi der Navigationsleiste angezeigt werden. Es ist zu sehen, dass sie rechts und links der Navigationsleiste entsprechen.

Erinnern Sie sich daran, wie die Gesten-Navigation funktioniert: Im richtigen Modus klicken wir nie auf ein neues Bedienfeld, sondern ziehen unsere Finger immer von unten nach oben. Dies bedeutet, dass die Schaltflächen nicht entfernt werden können. Wir können weiterhin unsere FAB-Taste (Floating Action Button) drücken, niemand wird sich einmischen. Daher TappableElementInsetswird es leer, da FAB zum Bewegen nicht erforderlich ist. Aber wenn wir uns etwas höher bewegen, ist das okay.

Der Unterschied tritt nur bei der Gesten-Navigation und der transparenten Navigationsleiste (Farbanpassung) auf. Dies ist keine sehr angenehme Situation.



Alles wird funktionieren, aber es sieht unangenehm aus. Der Benutzer kann durch die Nähe der Elemente verwirrt sein. Es kann erklärt werden, dass einer für Gesten und der andere für das Drücken ist, aber immer noch nicht schön. Erhöhen Sie daher entweder den FAB höher oder lassen Sie ihn rechts.

Systemgesteneinsätze


Erscheint nur in Android 10. Erhalten durch die Methode getSystemGestureInsets().

Diese Einfügungen beziehen sich auf die Gesten-Navigation. Ursprünglich wurde angenommen, dass die Systembenutzeroberfläche über der Anwendung gezeichnet wird. Systemgesteneinfügungen besagen jedoch, dass nicht die Benutzeroberfläche gezeichnet wird, sondern das System selbst die Gesten verarbeitet. Sie beschreiben, wo das System standardmäßig Gesten verarbeitet.

Die Bereiche dieser Einschübe sind ungefähr diejenigen, die im Diagramm gelb markiert sind.



Aber ich möchte Sie warnen - sie werden nicht immer so sein. Wir werden nie wissen, welche neuen Bildschirme Samsung und andere Hersteller entwickeln werden. Es gibt bereits Geräte, bei denen sich der Bildschirm nach allen Seiten befindet. Vielleicht sind die Einsätze überhaupt nicht dort, wo wir sie erwarten. Daher müssen Sie mit ihnen wie mit einer gewissen Abstraktion arbeiten: Es gibt solche Einfügungen in Systemgesten-Einfügungen, in denen das System selbst Gesten verarbeitet.

Diese Einschübe können überschrieben werden. Sie entwickeln beispielsweise einen Fotoeditor. An einigen Stellen möchten Sie selbst mit Gesten umgehen, auch wenn sie sich in der Nähe des Bildschirmrandes befinden. Geben Sie dem System an, dass Sie den Punkt auf dem Bildschirm in der Ecke des Fotos selbst verarbeiten möchten. Es kann neu definiert werden, indem man dem System sagt: "Ich werde das Quadrat um den Punkt immer selbst verarbeiten."

Obligatorische Systemgesteneinsätze


Wird nur in Android 10 angezeigt. Dies ist ein Untertyp von Systemgesten-Insets, die jedoch von der Anwendung nicht überschrieben werden können.

Wir verwenden die Methode getMandatorySystemGestureInsets(), um den Bereich zu bestimmen, in dem sie nicht funktionieren. Dies wurde absichtlich gemacht, so dass es unmöglich war, eine „hoffnungslose“ Anwendung zu entwickeln: Definieren Sie die Navigationsgeste von unten nach oben neu, sodass Sie die Anwendung auf dem Hauptbildschirm beenden können. Hier verarbeitet das System immer die Gesten selbst .


Es muss sich nicht unbedingt auf der Unterseite des Geräts befinden und muss nicht unbedingt diese Größe haben. Arbeite damit als Abstraktion.

Dies waren relativ neue Arten von Einsätzen. Aber es gibt solche, die schon vor Android 5 erschienen und anders genannt wurden.

Stabile Einsätze


Eingeführt mit Android 5.0 (API 21). Durch die Methode erhalten getStableInsets().

Selbst erfahrene Entwickler können nicht immer sagen, warum sie gebraucht werden. Ich werde Ihnen ein Geheimnis verraten: Diese Einfügungen sind nur für Vollbildanwendungen nützlich: Videoplayer, Spiele. Im Wiedergabemodus ist beispielsweise jede Systembenutzeroberfläche ausgeblendet, einschließlich der Statusleiste, die über den Bildschirmrand hinausgeht. Es lohnt sich jedoch, den Bildschirm zu berühren, da die Statusleiste oben angezeigt wird.

Wenn Sie eine BACK-Schaltfläche oben auf dem Bildschirm platzieren, während die Systemfenster-Einfügungen korrekt verarbeitet werden, wird mit jeder Registerkarte eine Benutzeroberfläche von oben auf dem Bildschirm angezeigt, und die Schaltfläche springt nach unten. Wenn Sie den Bildschirm eine Weile nicht berühren, verschwindet die Statusleiste und die Schaltfläche springt hoch, weil die Systemfenster-Einfügungen verschwunden sind.

In solchen Fällen sind stabile Einfügungen genau richtig. Sie sagen, dass jetzt kein Element in Ihrer Anwendung gezeichnet ist, aber es kann. Mit diesen Einsätzen können Sie beispielsweise den Wert der Statusleiste in diesem Modus im Voraus ermitteln und die Schaltfläche an der gewünschten Stelle positionieren.

Hinweis. Die Methode wurde getStableInsets()nur mit API 21 angezeigt. Bisher gab es mehrere Methoden für jede Seite des Bildschirms. 

Wir haben 5 Arten von Einsätzen untersucht, aber es gibt noch einen, der nicht direkt für Einsätze gilt. Es hilft beim Umgang mit Pony und Ausschnitten.

Pony und Ausschnitte


Bildschirme sind nicht mehr quadratisch. Sie sind länglich, mit einem oder mehreren Ausschnitten für die Kamera und Pony auf allen Seiten.



Bis zu 28 APIs konnten wir nicht verarbeiten. Ich musste durch Einfügungen erraten, was dort geschah. Aber mit der 28 API und darüber hinaus (vom vorherigen Android) erschien die Klasse offiziell DisplayCutout. Es befindet sich in denselben Einsätzen, aus denen Sie alle anderen Typen erhalten können. Mit der Klasse können Sie die Position und Größe von Artefakten ermitteln.

Zusätzlich zu den Standortinformationen wird dem Entwickler eine Reihe von y-Flags bereitgestellt WindowManager.LayoutParams. Mit ihnen können Sie verschiedene Verhaltensweisen in Ausschnitte einbeziehen. Ihre Inhalte werden möglicherweise um sie herum angezeigt oder nicht: im Quer- und Hochformat auf unterschiedliche Weise.

Flaggenwindow.attributes.layoutInDisplayCutoutMode =

  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT. Im Hochformat gibt es und im Querformat standardmäßig einen schwarzen Balken.
  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER. Überhaupt nicht - ein schwarzer Streifen.
  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES - ist es immer.

Die Anzeige kann gesteuert werden, aber wir arbeiten mit ihnen genauso wie mit allen anderen Insets.

insets.displayCutout
    .boundingRects
    .forEach { rect -> ... }

Rückruf mit Insets mit einem Array kommt zu uns displayCutout. Dann können wir es durchgehen und alle Ausschnitte und Pony verarbeiten, die in der Anwendung enthalten sind. Weitere Informationen zum Umgang mit ihnen finden Sie im Artikel .

Verstanden mit 6 Arten von Einsätzen. Lassen Sie uns nun darüber sprechen, wie es funktioniert.

Wie es funktioniert


Einfügungen werden vor allem dann benötigt, wenn etwas über die Anwendung gezeichnet wird, z. B. die Navigationsleiste.



Ohne Einschübe verfügt die Anwendung nicht über eine transparente Navigationsleiste und Statusleiste. Seien Sie nicht überrascht, dass Sie nicht kommen SystemWindowInsets, dies passiert.

Einsätze verteilen


Die gesamte UI-Hierarchie sieht aus wie ein Baum mit einer Ansicht am Ende. Knoten sind normalerweise eine ViewGroup. Sie erben auch von View, sodass Einfügungen auf besondere Weise zu ihnen kommen dispatchApplyWindowInsets. Ausgehend von der Stammansicht sendet das System Einfügungen im gesamten Baum. Mal sehen, wie View in diesem Fall funktioniert.



Das System ruft die Methode auf, dispatchApplyWindowInsetsin die die Insets kommen. Zurück in dieser Ansicht sollte etwas zurückgegeben werden. Was tun, um mit diesem Verhalten umzugehen?

Der Einfachheit halber betrachten wir nur WindowInsets.

Umgang mit Einsätzen


Zunächst scheint eine Neudefinition der Methode dispatchApplyWindowInsetsund Implementierung innerhalb ihrer eigenen Logik logisch : Einfügungen kamen, wir haben alles innerhalb implementiert.

Wenn Sie die View-Klasse öffnen und sich das Javadoc ansehen, das über dieser Methode geschrieben wurde, sehen Sie: "Überschreiben Sie diese Methode nicht!"
Wir behandeln Insets durch Delegation oder Vererbung.
Delegierung verwenden . Google bot die Möglichkeit, einen eigenen Delegierten zu entlarven, der für den Umgang mit Insets verantwortlich ist. Sie können es über die Methode installieren setOnApplyWindowInsetsListener(listener). Wir setzen einen Rückruf, der diese Einfügungen behandelt.

Wenn dies aus irgendeinem Grund nicht zu uns passt, können Sie von View erben und eine andere Methode überschreiben onApplyWindowInsets(WindowInsets insets) . Wir ersetzen unsere eigenen Einfügungen.

Google hat nicht zwischen Delegierung und Vererbung gewählt, sondern alles zusammen machen dürfen. Dies ist großartig, da wir nicht alle Symbolleisten oder ListView neu definieren müssen, um Einfügungen zu verarbeiten. Wir können jede vorgefertigte Ansicht, auch eine Bibliotheksansicht, verwenden und dort den Delegaten festlegen, der Einfügungen ohne Neudefinition verarbeitet.

Was sollen wir zurückgeben?

Warum etwas zurückgeben?


Dazu müssen Sie verstehen, wie die Verteilung von Insets, d. H. ViewGroup, funktioniert. Was macht sie in sich? Schauen wir uns das Standardverhalten anhand des zuvor gezeigten Beispiels genauer an.

Oben und unten befanden sich Systemeinfügungen, beispielsweise jeweils 100 Pixel. Die ViewGroup sendet sie an die erste Ansicht, die sie hat. Diese Ansicht hat sie irgendwie behandelt und gibt Einfügungen zurück: oben wird sie auf 0 verringert, unten wird sie verlassen. Oben fügte sie Polsterung oder Rand hinzu, aber unten berührte sie nicht und berichtete dies. Als Nächstes übergibt die ViewGroup Einfügungen an die zweite Ansicht.



Die nächste Ansicht verarbeitet und rendert Einfügungen. Jetzt sieht die ViewGroup, dass die Insets sowohl oben als auch unten verarbeitet werden - es ist nichts mehr übrig, alle Parameter sind Null.

Die dritte Ansicht weiß nicht einmal, dass es einige Einfügungen gab und etwas passierte. ViewGroup gibt Insets an denjenigen zurück, der sie freigelegt hat.

Als wir anfingen, uns mit diesem Thema zu befassen, haben wir die Idee für uns selbst aufgeschrieben - geben Sie immer die gleichen Einfügungen zurück, die gekommen sind. Wir wollten eine solche Situation vermeiden, in der einige View nicht einmal erkannten, dass es Einfügungen gab. Die Idee sah logisch aus. Aber es stellte sich heraus, dass nein. Google hat Insets nicht umsonst Verhalten hinzugefügt, wie im Beispiel.

Dies ist erforderlich, damit Einfügungen immer die gesamte Hierarchie der Ansicht erreichen und Sie immer mit ihnen arbeiten können. In diesem Fall gibt es keine Situation, wenn wir zwei Fragmente wechseln, eines verarbeitet und das zweite noch nicht empfangen hat. Im Übungsabschnitt werden wir auf diesen Punkt zurückkommen.

Trainieren


Theorie ist fertig. Mal sehen, wie es im Code aussieht, denn theoretisch ist immer alles glatt. Wir werden AndroidX verwenden.

Hinweis. In GitFox ist bereits alles im Code implementiert, volle Unterstützung für alle Extras des neuen Android. view.setOnApplyWindowInsetsListener { v, insets -> ... }Verwenden Sie stattdessen immer die Version von AndroidX , um Android-Versionen nicht zu überprüfen und nicht nach dem erforderlichen Insets-Typ zu suchen ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets -> ... }.

Es gibt einen Bildschirm mit einer dunklen Navigationsleiste. Es überlappt kein Element oben. Alles ist so, wie wir es gewohnt sind.


Aber es stellt sich heraus, dass wir es jetzt transparent machen müssen. Wie?

Schalten Sie die Transparenz ein


Am einfachsten ist es, im Betreff anzugeben, dass die Statusleiste und die Navigationsleiste transparent sind.

<style name="AppTheme" parent="Theme.MaterialComponents.Light">
    <item name="android:windowTranslucentStatus">true</item>
    <item name="android:windowTranslucentNavigation">true</item>
</style>

In diesem Moment beginnt sich alles von oben und von unten zu überlappen.

Interessanterweise gibt es zwei Flags und es gibt immer drei Optionen. Wenn Sie Transparenz für die Navigationsleiste angeben, wird die Statusleiste von selbst transparent - eine solche Einschränkung. Sie können nicht die erste Zeile schreiben, aber ich liebe immer Klarheit, also schreibe ich 2 Zeilen, damit die Follower verstehen können, was passiert, und nicht darin graben.

Wenn Sie diese Methode wählen, werden die Navigationsleiste und die Statusleiste mit Transparenz schwarz. Wenn die Anwendung weiß ist, können Sie Farben nur aus dem Code hinzufügen. Wie kann man das machen?

Schalten Sie die Transparenz mit Farbe ein


Es ist gut, dass wir eine Anwendung für eine einzelne Aktivität haben, also setzen wir zwei Flags in eine Aktivität.

rootView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or  
    View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION

<style name="AppTheme" parent="Theme.MaterialComponents.Light">
    <!-- Set the navigation bar to 50% translucent white -->
    <item name="android:navigationBarColor">#80FFFFFF</item> 
</style>

Es gibt dort viele Flags, aber es sind diese beiden, die dazu beitragen, Transparenz in der Navigationsleiste und der Statusleiste zu schaffen. Die Farbe kann über das Thema festgelegt werden.

Dies ist das seltsame Verhalten von Android: etwas durch das Thema und etwas durch die Flaggen. Wir können jedoch Parameter sowohl im Code als auch im Betreff angeben. Dies ist nicht so schlecht für Android, nur bei älteren Android-Versionen wird das im Thema angegebene Flag ignoriert. navigationBarColorEs wird hervorgehoben, dass dies unter Android 5 nicht der Fall ist, aber alles wird zusammenkommen. In GitFox habe ich anhand des Codes die Farbe der Navigationsleiste festgelegt.

Wir haben Betrug durchgeführt - angezeigt, dass die Navigationsleiste weiß mit Transparenz ist. Jetzt sieht die Anwendung so aus.


Was könnte einfacher sein als die Handhabung von Einsätzen?


In der Tat elementar.

ViewCompat
    .setOnApplyWindowInsetsListener(bottomNav) { view, insets ->  
        view.updatePadding(bottom = insets.systemWindowInsetBottom) 
        insets
    }

Wir nehmen die Methode setOnApplyWindowInsetsListenerund geben sie weiter bottomNav. Wir sagen, wenn die Einsätze kommen, installieren Sie die untere Polsterung, die hereingekommen ist systemWindowInsetBottom. Wir wollen es darunter haben, aber alle unsere anklickbaren Elemente sind oben. Wir geben vollständig eingefügte Elemente zurück, sodass alle anderen Ansichten in unserer Hierarchie diese ebenfalls erhalten.

Alles sieht gut aus.


Aber es gibt einen Haken. Wenn wir im Layout unten in der Navigationsleiste eine Art Polsterung angegeben haben, wurde diese hier gelöscht - setzen Sie die updatePaddingEinfügungen gleich. Unser Layout sieht nicht so aus, wie wir es gerne hätten.

Um die Werte aus dem Layout zu speichern, müssen Sie zuerst speichern, was sich in der unteren Auffüllung befindet. Wenn später Einfügungen eintreffen, fügen Sie die resultierenden Werte hinzu und legen Sie sie fest.

val bottomNavBottomPadding = bottomNav.paddingBottom
ViewCompat
    .setOnApplyWindowInsetsListener(bottomNav) { view, insets ->                 
        view.updatePadding(
        bottom = bottomNavBottomPadding + insets.systemWindowInsetBottom
    )
    Insets
}

Es ist unpraktisch, so zu schreiben: Überall im Code, wo Sie Einfügungen verwenden, müssen Sie den Wert aus dem Layout speichern und dann zur Benutzeroberfläche hinzufügen. Aber es gibt Kotlin, und das ist wunderbar - Sie können Ihre eigene Erweiterung schreiben, die all dies für uns erledigt.

Kotlin hinzufügen!


Wir erinnern uns initialPaddingund geben es dem Handler, wenn neue Insets (zusammen mit ihnen) kommen. Dies wird ihnen helfen, sich irgendwie zu addieren oder eine Art Logik von oben aufzubauen.

fun View.doOnApplyWindowInsets(block: (View, WindowInsetsCompat, Rect) -> WindowInsetsCompat) {    

    val initialPadding = recordInitialPaddingForView(this)

    ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets ->        
        block(v, insets, initialPadding)
    }
    
    requestApplyInsetsWhenAttached()
}

private fun recordInitialPaddingForView(view: View) =
   Rect(view.paddingLeft, view.paddingTop, view.paddingRight, view.paddingBottom)

  .

bottomNav.doOnApplyWindowInsets { view, insets, padding ->   
    view.updatePadding(
        bottom = padding.bottom + insets.systemWindowInsetBottom
    ) 
    insets
}

Wir müssen das Lambda neu definieren. Es hat nicht nur Einsätze, sondern auch Polster. Wir können sie nicht nur von unten, sondern auch von oben hinzufügen, wenn es sich um eine Symbolleiste handelt, die die Statusleiste verarbeitet.

Etwas vergessen!


Es wird eine unverständliche Methode aufgerufen.

fun View.doOnApplyWindowInsets(block: (View, WindowInsetsCompat, Rect) -> WindowInsetsCompat) {

    val initialPadding = recordInitialPaddingForView(this)
     
    ViewCompat.setOnApplyWindowInsetsListener(this) { v, insets ->        
        block(v, insets, initialPadding)
    }
      
    requestApplyInsetsWhenAttached() 
}

private fun recordInitialPaddingForView(view: View) =
      Rect(view.paddingLeft, view.paddingTop, view.paddingRight, view.paddingBottom)

Dies liegt an der Tatsache, dass Sie nicht nur hoffen können, dass das System Ihnen Einfügungen sendet. Wenn wir eine Ansicht aus dem Code erstellen oder InsetsListeneretwas später installieren, übergibt das System selbst möglicherweise nicht den letzten Wert.

Wir müssen überprüfen, ob die Ansicht auf dem Bildschirm angezeigt wird. Wir haben dem System mitgeteilt: "Die Einfügungen sind eingetroffen, wir möchten sie verarbeiten." Wir stellen den Listener ein und müssen eine Anfrage stellen requestsApplyInsets. "

fun View.requestApplyInsetsWhenAttached() {    
    if (isAttachedToWindow) {        
        requestApplyInsets()
    } else {
        addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {            
            override fun onViewAttachedToWindow(v: View) {
                v.removeOnAttachStateChangeListener(this)
                v.requestApplyInsets()           
            }

            override fun onViewDetachedFromWindow(v: View) = Unit
        })
    }
}

Wenn wir einen Ansichtscode erstellt und noch nicht an unser Layout angehängt haben, müssen wir den Zeitpunkt abonnieren, zu dem wir dies tun. Erst dann fordern wir Einschübe an.

Aber andererseits gibt es Kotlin: Sie haben eine einfache Erweiterung geschrieben und wir müssen nicht mehr darüber nachdenken.

RecyclerView verfeinern


Lassen Sie uns nun über die Fertigstellung der RecyclerView sprechen. Das letzte Element fällt unter die Navigationsleiste und bleibt darunter - hässlich. Es ist auch unpraktisch, darauf zu klicken. Wenn dies kein neues Panel ist, sondern das alte, ein großes, dann kann im Allgemeinen das gesamte Element darunter verschwinden. Was zu tun ist?



Die erste Idee besteht darin, unten ein Element hinzuzufügen und die Höhe so einzustellen, dass sie in die Einfügungen passt. Wenn wir jedoch eine Anwendung mit Hunderten von Listen haben, müssen wir irgendwie jede Liste für Einschübe abonnieren, dort ein neues Element hinzufügen und die Höhe festlegen. Darüber hinaus ist RecyclerView asynchron, es ist nicht bekannt, wann es funktionieren wird. Es gibt viele Probleme.

Aber es gibt noch einen anderen offiziellen Hack. Überraschenderweise funktioniert es effizient.

<androidx.recyclerview.widget.RecyclerView 
        android:id="@+id/recyclerView"        
    android:layout_width="match_parent"    
    android:layout_height="match_parent"             
    android:clipToPadding="false" />

recyclerView.doOnApplyWindowInsets { view, insets, padding ->    
    view.updatePadding(
        bottom = padding.bottom + insets.systemWindowInsetBottom
    )
}

Es gibt eine solche Flagge im Layout clipToPadding. Standardmäßig ist es immer wahr. Dies bedeutet, dass Sie keine Elemente zeichnen müssen, die dort angezeigt werden, wo die Polsterung freigelegt ist. Aber wenn gesetzt clipToPadding="false", können Sie zeichnen.

Wenn Sie nun das Auffüllen nach unten setzen, funktioniert dies in RecyclerView folgendermaßen: Das Auffüllen wird nach unten gesetzt, und das Element wird darüber gezeichnet, bis wir einen Bildlauf durchführen. Sobald das Ende erreicht ist, scrollt RecyclerView die Elemente an die Position, die wir benötigen.

Durch Setzen eines solchen Flags können wir mit RecyclerView wie mit einer normalen Ansicht arbeiten. Denken Sie nicht, dass es Elemente gibt, die scrollen - stellen Sie einfach die Auffüllung unten ein, wie zum Beispiel in der unteren Navigationsleiste.

Jetzt haben wir immer Einfügungen zurückgegeben, als ob sie nicht verarbeitet worden wären. Die ganzen Einsätze kamen zu uns, wir machten etwas mit ihnen, stellten die Polster ein und gaben alle ganzen Einsätze wieder zurück. Dies ist erforderlich, damit jede ViewGroup diese Einfügungen immer an alle Views weitergibt. Es klappt.

Anwendungsfehler


In vielen Anwendungen bei Google Play, die bereits Einschübe verarbeitet haben, ist mir ein kleiner Fehler aufgefallen. Jetzt erzähle ich von ihm.

Im Keller befindet sich ein Bildschirm mit Navigation. Auf der rechten Seite befindet sich derselbe Bildschirm, auf dem die Hierarchie angezeigt wird. Das grüne Fragment zeigt den Inhalt auf dem Bildschirm an und enthält eine RecyclerView.



Wer hat hier mit den Einsätzen umgegangen? Symbolleiste: Er hat oben eine Auffüllung angebracht, damit sein Inhalt unter die Statusleiste verschoben wird. Dementsprechend wurden in der unteren Navigationsleiste Einfügungen von unten angebracht und über der Navigationsleiste angehoben. RecyclerView behandelt Insets jedoch in keiner Weise, es fällt nicht unter sie, es muss keine Insets verarbeiten - alles wird korrekt ausgeführt.



Hier stellt sich jedoch heraus, dass wir das grüne RecyclerView-Fragment an einer anderen Stelle verwenden möchten, an der die untere Navigationsleiste nicht mehr vorhanden ist. Zu diesem Zeitpunkt beginnt RecyclerView bereits mit der Verarbeitung von Einfügungen von unten. Wir müssen Polster darauf anwenden, um unter der Navigationsleiste richtig zu scrollen. Daher fügen wir in RecyclerView auch die Verarbeitung von Einfügungen hinzu.



Wir kehren zu unserem Bildschirm zurück, auf dem alle Einfügungen verarbeiten. Erinnerst du dich, niemand berichtet, dass er sie verarbeitet hat?



Wir sehen diese Situation: RecyclerView verarbeitete Einfügungen von unten, obwohl sie die Navigationsleiste nicht erreichen - unten wurde ein Leerzeichen angezeigt. Ich habe dies in Anwendungen bemerkt und war ziemlich groß und beliebt.



Wenn dies der Fall ist, erinnern wir uns, dass wir alle Einfügungen zur Verarbeitung zurückgeben. Also (es stellt sich heraus!) Es muss gemeldet werden, dass die Einfügungen verarbeitet werden: Die Navigationsleiste sollte melden, dass die Einfügungen verarbeitet wurden. Sie sollten nicht zu RecyclerView gelangen.



Installieren Sie dazu die untere Navigationsleiste InsetsListener. Subtrahieren Sie, bottom + insetsdass sie nicht verarbeitet werden, und geben Sie 0 zurück.

doOnApplyWindowInsets { view, insets, initialPadding ->    
    view.updatePadding(
        bottom = initialPadding.bottom + insets.systemWindowInsetBottom
    )
    insets.replaceSystemWindowInsets(
        Rect(
            insets.systemWindowInsetLeft,            
            insets.systemWindowInsetTop,            
            insets.systemWindowInsetRight,
            0
        )
    )
}

Wir geben neue Einfügungen zurück, in denen alle alten Parameter gleich, aber bottom + insetsgleich 0 sind. Es scheint, dass alles in Ordnung ist. Wir fangen an und wieder Unsinn - RecyclerView behandelt aus irgendeinem Grund immer noch Einfügungen.



Ich habe nicht sofort verstanden, dass das Problem darin besteht, dass der Container für diese drei Ansichten LinearLayout ist. Darin befindet sich eine Symbolleiste, ein Snippet mit einer RecyclerView und am unteren Rand der unteren Navigationsleiste. LinearLayout nimmt seine Kinder in Ordnung und wendet Einfügungen auf sie an.

Es stellt sich heraus, dass die untere Navigationsleiste die letzte ist. Er sagte jemandem, dass er alle Einschübe bearbeitet habe, aber es sei zu spät. Alle Einschübe wurden von oben nach unten verarbeitet, RecyclerView hat sie bereits erhalten, und dies rettet uns nicht.



Was zu tun ist? LinearLayout funktioniert nicht so, wie ich es möchte, es überträgt sie von oben nach unten, und ich muss zuerst das untere erhalten.

Alle neu definieren


Der Java-Entwickler hat in mir gespielt - alles muss neu definiert werden ! Nun, jetzt dispatchApplyWindowInsetsneu definieren, setzen yLinearLayout, was immer von unten nach oben geht. Er sendet zuerst die unteren Navigationsleisten und dann alle anderen.

@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {     
    insets = super.dispatchApplyWindowInsets(insets);     
    if (!insets.isConsumed()) {        
        final int count = getChildCount();
        for (int i = 0; i < count; i++) {
            insets = getChildAt(i).dispatchApplyWindowInsets(insets);
            if (insets.isConsumed()) {
                break;
            }
        }
    }
    return insets;
}

Aber ich blieb rechtzeitig stehen und erinnerte mich an den Kommentar, dass diese Methode nicht neu definiert werden musste.

Etwas höher ist hier die Rettung: ViewGroup sucht nach einem Delegaten, der die Einfügungen verarbeitet, ruft dann die Supermethode in View auf und enthält eine eigene Verarbeitung. In diesem Code erhalten wir Einfügungen, und wenn sie noch nicht verarbeitet wurden, beginnen wir mit der Standardlogik für unsere Kinder.

Ich habe eine einfache Erweiterung geschrieben. In InsetsListenereiner Ansicht können Sie angeben, an wen diese Einfügungen übergeben werden.

fun View.addSystemBottomPadding(    
    targetView: View = this
) {
    doOnApplyWindowInsets { _, insets, initialPadding ->           
        targetView.updatePadding(
            bottom = initialPadding.bottom + insets.systemWindowInsetBottom
        )
        insets.replaceSystemWindowInsets(
            Rect(
                insets.systemWindowInsetLeft,
                insets.systemWindowInsetTop,     
                insets.systemWindowInsetRight,
                0
            )
        )
    }
}

Hier entspricht es targetViewstandardmäßig derselben Ansicht, auf die wir uns beziehen addSystemBottomPadding. Wir können es neu definieren.

An meinem LinearLayout habe ich einen solchen Handler aufgehängt und vorbeigegangen als targetView- dies ist meine untere Navigationsleiste.

  • Zuerst wird er die unteren Navigationsleisten einfügen. 
  • Das wird Einfügungen verarbeiten, wird Null unten zurückgeben.
  • Außerdem werden sie standardmäßig von oben nach unten verschoben: Symbolleiste, Fragment mit RecyclerView.
  • Dann wird er vielleicht wieder Einfügungen unterer Navigationsleiste senden. Aber das ist nicht mehr wichtig, alles wird so gut funktionieren.

Ich habe genau das erreicht, was ich wollte: Alle Einsätze werden in der Reihenfolge verarbeitet, in der sie benötigt werden.



Wichtig


Ein paar wichtige Dinge zu beachten.

Die Tastatur ist die Systemoberfläche über Ihrer Anwendung. Keine Notwendigkeit, sie auf besondere Weise zu behandeln. Wenn Sie sich die Google-Tastatur ansehen, handelt es sich nicht nur um ein Layout mit Schaltflächen, auf die Sie klicken können. Es gibt Hunderte von Modi für diese Tastatur: Suche nach Gifs und Memes, Spracheingabe, Größenänderung von 10 Pixel Höhe auf Bildschirmgröße. Denken Sie nicht an die Tastatur, sondern abonnieren Sie Insets.

Navigation Drawer unterstützt die Gesten-Navigation immer noch nicht. Bei Google IO versprachen sie, dass alles sofort funktionieren würde. In Android 10 unterstützt Navigation Drawer dies jedoch immer noch nicht. Wenn Sie auf Android 10 aktualisieren und die Navigation mit Gesten aktivieren, fällt die Navigationsleiste ab. Jetzt müssen Sie auf das Hamburger-Menü klicken, um es anzuzeigen, oder eine Kombination von zufälligen Umständen ermöglicht es Ihnen, es zu dehnen.

In der Pre-Alpha-Version von Android funktioniert Navigation Drawer, aber ich habe mich nicht getraut, ein Update durchzuführen - dies ist Pre-Alpha. Selbst wenn Sie die neueste Version von GitFox aus dem Repository installieren, befindet sich dort eine Navigationsleiste, die jedoch nicht abgerufen werden kann. Sobald der offizielle Support herauskommt, werde ich aktualisieren und alles wird gut funktionieren.

Checkliste zur Vorbereitung auf Android 10


Legen Sie die Transparenz der Navigationsleiste und der Statusleiste zu Beginn des Projekts fest . Google empfiehlt dringend, eine transparente Navigationsleiste beizubehalten. Für uns ist dies ein wichtiger praktischer Teil. Wenn das Projekt funktioniert, Sie es jedoch nicht aktiviert haben, wählen Sie die Zeit für die Unterstützung von Android 10. Aktivieren Sie sie zuerst im Betreff, z. B. durchscheinend, und korrigieren Sie das Layout, in dem es beschädigt wurde.

Fügen Sie Kotlin Erweiterungen hinzu - mit ihnen ist es einfacher.

Fügen Sie der gesamten Symbolleiste über dem Abstand hinzu. Symbolleisten befinden sich immer oben, und genau das müssen Sie tun.

Fügen Sie für alle RecyclerViews eine Auffüllung von unten und die Möglichkeit zum Scrollen hinzu clipToPadding="false".

Denken Sie an alle Schaltflächen an den Bildschirmrändern (FAB) . FABs werden höchstwahrscheinlich nicht dort sein, wo Sie es erwarten.

Überschreiben oder erstellen Sie keine eigenen Implementierungen für alle LinearLayout- und ähnliche Fälle. Machen Sie den Trick wie in GitFox oder nehmen Sie meine vorgefertigte Erweiterung, um zu helfen.

Überprüfen Sie die Gesten an den Bildschirmrändern auf benutzerdefinierte Ansicht . Navigation Drawer unterstützt dies nicht. Es ist jedoch nicht so schwierig, es mit Ihren Händen zu unterstützen und Einfügungen für Gesten auf dem Bildschirm mit Navigation Drawer neu zu definieren. Vielleicht haben Sie Bildbearbeitungsprogramme, in denen Gesten funktionieren.

Das Scrollen in ViewPager funktioniert nicht vom Rand aus, sondern nur von der Mitte nach rechts und links . Google sagt, das sei normal. Wenn Sie den ViewPager vom Rand ziehen, wird dies als Drücken der Taste "Zurück" empfunden.

Schließen Sie in ein neues Projekt sofort die gesamte Transparenz ein. Sie können Designern mitteilen, dass Sie keine Navigationsleiste und keine Statusleiste haben. Das ganze Quadrat ist Ihr Anwendungsinhalt. Und die Entwickler werden bereits herausfinden, wo und was sie ansprechen müssen.

Nützliche Links:

  • @rmr_spb in einem Telegramm - Aufzeichnungen der internen Mitaps von Redmadrobot SPb.
  • Der gesamte Quellcode in diesem Artikel und noch mehr in GitFox . Es gibt einen separaten Android 10-Zweig, in dem Sie sehen können, wie ich zu all dem gekommen bin und wie ich das neue Android unterstützt habe.
  • Chris Banes Insetter Library . Es enthält 5-6 Erweiterungen, die ich gezeigt habe. Wenden Sie sich zur Verwendung in Ihrem Projekt an die Bibliothek. Wahrscheinlich wird sie auf Android Jetpack verschoben. Ich habe sie in meinem Projekt entwickelt und sie scheinen mir besser geworden zu sein.
  • Chris Banes Artikel .
  • FunCorn, , .
  • «Why would I want to fitsSystemWindows?» .

AppConf, youtube- . , - . AppsConf , . , , stay tuned!

All Articles