2015-07-02 7 views
29

Ich habe eine Aktivität mit Navigationsschublade und randlosen Fragment (mit Bild in der Oberseite, die hinter transluzenten Systemleiste auf Lollipop erscheinen muss). Während ich eine Übergangslösung hatte, bei der das Fragment aufgeblasen wurde, indem ich einfach <fragment> in Activities XML-Tag hatte, sah es gut aus.fitsSystemWindows Effekt für Fragmente über FragmentTransaction gegangen

Dann hatte ich <fragment> mit <FrameLayout> und führte Fragment Transaktionen zu ersetzen, und jetzt das Fragment hinter der Systemleiste nicht mehr angezeigt wird, trotz fitsSystemWindows zu true über alle erforderliche Hierarchie festgelegt ist.

Ich glaube, es könnte einen Unterschied zwischen <fragment> wird in Activity-Layout vs allein aufgebläht sein. Ich habe gegoogelt und ein paar Lösungen für KitKat gefunden, aber keiner von denen arbeitete für mich (Lollipop).

activity.xml

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" 
             xmlns:app="http://schemas.android.com/apk/res-auto" 
             android:id="@+id/drawer_layout" 
             android:layout_height="match_parent" 
             android:layout_width="match_parent" 
             android:fitsSystemWindows="true"> 

    <FrameLayout 
      android:id="@+id/fragment_host" 
      android:layout_width="match_parent" 
      android:layout_height="match_parent" 
      android:fitsSystemWindows="true"> 

    </FrameLayout> 

    <android.support.design.widget.NavigationView 
      android:id="@+id/nav_view" 
      android:layout_height="match_parent" 
      android:layout_width="wrap_content" 
      android:layout_gravity="start" 
      android:fitsSystemWindows="true"/> 

</android.support.v4.widget.DrawerLayout> 

fragment.xml

<android.support.design.widget.CoordinatorLayout 
     xmlns:android="http://schemas.android.com/apk/res/android" 
     xmlns:app="http://schemas.android.com/apk/res-auto" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:fitsSystemWindows="true"> 

    <android.support.design.widget.AppBarLayout 
      android:layout_width="match_parent" 
      android:layout_height="224dp" 
      android:fitsSystemWindows="true" 
      android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> 
... 

Es funktionierte, wenn activity.xml diese Art und Weise war:

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" 
             xmlns:app="http://schemas.android.com/apk/res-auto" 
             android:id="@+id/drawer_layout" 
             android:layout_height="match_parent" 
             android:layout_width="match_parent" 
             android:fitsSystemWindows="true"> 

    <fragment xmlns:android="http://schemas.android.com/apk/res/android" 
       xmlns:tools="http://schemas.android.com/tools" 
       android:id="@+id/fragment" 
       android:name="com.actinarium.random.ui.home.HomeCardsFragment" 
       tools:layout="@layout/fragment_home" 
       android:layout_width="match_parent" 
       android:layout_height="match_parent"/> 

    <android.support.design.widget.NavigationView 
      android:id="@+id/nav_view" 
      android:layout_height="match_parent" 
      android:layout_width="wrap_content" 
      android:layout_gravity="start" 
      android:fitsSystemWindows="true"/> 

</android.support.v4.widget.DrawerLayout> 

Antwort

55

Wenn Sie <fragment> verwenden, das Layout in Ihrem Fragmente des zurückgegeben wird onCreateView direkt anstelle von den <fragment> Tag angebracht (du nie sieht tatsächlich einen <fragment> Tag, wenn Sie in Ihrer View-Hierarchie aussehen.

daher im <fragment> Fall, haben Sie

DrawerLayout 
    CoordinatorLayout 
    AppBarLayout 
    ... 
    NavigationView 

Ähnlich wie cheesesquare funktioniert. Dies funktioniert, weil, wie in this blog post, DrawerLayout und CoordinatorLayout erklärt haben beide unterschiedliche Regeln, wie fitsSystemWindows gilt für sie - sie beide es verwenden, ihr Kind Ansichten Einsatz, aber auch Anruf dispatchApplyWindowInsets() auf jedes Kind, so dass sie Zugang zum fitsSystemWindows="true" Eigenschaft .

Dies ist ein Unterschied zu dem Standardverhalten mit Layouts wie FrameLayout wo, wenn Sie verwenden fitsSystemWindows="true" verbraucht sind, alle Einsätze, blind Polsterung Anwendung ohne Kind Blick zu informieren (das ist die ‚Tiefe zuerst‘ Teil der Blog-Post).

Also, wenn Sie den <fragment>-Tag mit einem FrameLayout und FragmentTransactions ersetzen, Ihre Ansicht Hierarchie wird:

DrawerLayout 
    FrameLayout 
    CoordinatorLayout 
     AppBarLayout 
     ... 
    NavigationView 

als die Ansicht des Fragments in die FrameLayout eingeführt wird. Diese Ansicht weiß nichts über die Übergabe von an untergeordnete Ansichten, sodass Ihre CoordinatorLayout diese Flagge nie sehen oder ihr benutzerdefiniertes Verhalten ausführen kann.

Das Problem zu beheben ist eigentlich ziemlich einfach: ersetzen Sie Ihre FrameLayout durch eine andere CoordinatorLayout. Dadurch wird sichergestellt, dass die fitsSystemWindows="true" von dem Fragment auf die neu aufgeblasene CoordinatorLayout weitergeleitet wird.

Alternative und gleichwertige Lösungen wären eine benutzerdefinierte Unterklasse von FrameLayout zu machen und außer Kraft setzen onApplyWindowInsets() für jedes Kind zu versenden (in Ihrem Fall nur die einem) oder verwenden Sie die ViewCompat.setOnApplyWindowInsetsListener() Methode von dort den Anruf in Code und Versand abfangen (keine Unterklasse erforderlich). Weniger Code ist normalerweise am einfachsten zu pflegen, daher würde ich nicht empfehlen, diese Routen über die CoordinatorLayout Lösung zu gehen, es sei denn, Sie fühlen sich stark darüber.

+5

Dies ist die echte Lösung. Vielen Dank! Mit dieser Information habe ich eine Unterklasse von FrameLayout geschrieben, die WindowIssets an ihre Childs weiterleitet, um einen ähnlichen Effekt wie CoordinatorLayout zu erreichen, wie Sie es vorgeschlagen haben. Für alle Interessierten: https://gist.github.com/Pkmmte/d8e983fb9772d2c91688 – Pkmmte

+0

Danke Ian! Ich bin gerade hierher gekommen, um diese Frage mit dem Hinweis auf Ihren gestrigen Beitrag zu beantworten, aber Sie haben dies bereits getan (: – Actine

+1

@Pkmmte, ich kann es immer noch nicht mit FT arbeiten! Gibt es ein Beispielprojekt mit 'WindowInsetsFrameLayout'? Und 2. Frage - ist es kompatibel zu ICS? – WindRider

1

Ich habe dieses letzte Jahr erstellt, um dieses Problem zu lösen: https://gist.github.com/cbeyls/ab6903e103475bd4d51b

Edit: sicher sein, dass Sie verstehen, was passtSystemWindows zuerst. Wenn Sie es auf eine View setzen, heißt das im Wesentlichen: "Setzen Sie diese View und alle ihre untergeordneten Elemente unter die Statusleiste und über die Navigationsleiste". Es macht keinen Sinn, dieses Attribut auf den obersten Container zu setzen.

+0

Ich versuchte dies, es funktionierte nicht für Lollipop. Auch die IDE sagt 'super.fitSystemWindows' ist veraltet seit API 20 – Actine

+0

Danke für das Feedback, das letzte Mal, als ich es in meiner App unter Lollipop versuchte es funktionierte, aber ich werde wieder testen und schauen, ob ich eine neue Implementierung finden kann, die doesn ' t verwende eine veraltete Methode. – BladeCoder

+0

Vielleicht ist das Problem bei mir mit 'CoordinatorLayout' aus AppCompat-Design-Bibliothek? Es verwirrt mit all diesen Layout-Dingen unter der Haube. Ich habe versucht, Ihre Lösung wieder und jetzt sehe ich zwei Statusleisten, wenn meine Symbolleiste zusammengebrochen ist -_- – Actine

3

OK, nachdem mehrere Leute darauf hinwiesen, dass fitsSystemWindows anders funktioniert, und es sollte nicht bei jeder Ansicht in der Hierarchie verwendet werden, experimentierte ich weiter und entfernte die Eigenschaft aus verschiedenen Ansichten.

ich den erwarteten Zustand bekam nach fitsSystemWindows von jedem Knoten in activity.xml Entfernen = \

+0

Also im Grunde wussten Sie nicht, was es ist, und Sie wussten nicht, dass Sie es nicht brauchen? – BladeCoder

+6

Ja :(Ich gebe zu, ich folgte blind dem Cheesesquare-Beispiel, es hatte dieses Attribut überall in seinen Layouts. Wird nicht noch einmal passieren. Ich dachte dummerweise, dass ich mit der Design-Bibliothek eine schöne App erstellen könnte ohne tief in die verworrenen Layouting-Interna von Android zu graben.Gosch, ich habe einmal einen benutzerdefinierten Ausdrucksprachinterpreter implementiert, und er war weniger verworren als die Benutzeroberfläche von Android – Actine

+1

Ich bekomme keine Lösung.Sie haben 'fitsSystemWindows' aus' activity.xml entfernt 'Und verließ es für das 'CoordinatorLayout' wie im Cheesesquare-Beispiel? Aber dann, ist Ihre Navigationsleiste hinter der Statusleiste? – Pin

3

Mein Problem war ähnlich wie Ihres: Ich habe eine Bottom Bar Navigation, die die Inhaltsfragmente ersetzt. Jetzt wollen einige der Fragmente über die Statusleiste (mit CoordinatorLayout, AppBarLayout), andere nicht (mit ConstraintLayout, Toolbar) zeichnen.

ConstraintLayout 
    FrameLayout 
    [the ViewGroup of your choice] 
    BottomNavigationView 

Der Vorschlag ianhanniballake andere CoordinatorLayout Schicht hinzuzufügen nicht das, was ich will, so habe ich eine benutzerdefinierte FrameLayout, die die Einsätze Griffe (wie er vorgeschlagen), und nach einiger Zeit kam ich auf diese Lösung, die wirklich nicht viel Code:

activity_main.xml

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:app="http://schemas.android.com/apk/res-auto" 
    android:id="@+id/content" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"> 

    <com.example.app.WindowInsetsFrameLayout 
     android:id="@+id/fragment_container" 
     android:layout_width="0dp" 
     android:layout_height="0dp" 
     app:layout_constraintBottom_toTopOf="@+id/bottom_navigation" 
     app:layout_constraintEnd_toEndOf="parent" 
     app:layout_constraintStart_toStartOf="parent" 
     app:layout_constraintTop_toTopOf="parent" /> 

    <BottomNavigationView 
     android:id="@+id/bottom_navigation" 
     android:layout_width="0dp" 
     android:layout_height="wrap_content" 
     app:layout_constraintBottom_toBottomOf="parent" 
     app:layout_constraintEnd_toEndOf="parent" 
     app:layout_constraintStart_toStartOf="parent" /> 

</android.support.constraint.ConstraintLayout> 

WindowInsetsFrameLayout.java

/** 
* FrameLayout which takes care of applying the window insets to child views. 
*/ 
public class WindowInsetsFrameLayout extends FrameLayout { 

    public WindowInsetsFrameLayout(Context context) { 
     this(context, null); 
    } 

    public WindowInsetsFrameLayout(Context context, AttributeSet attrs) { 
     this(context, attrs, 0); 
    } 

    public WindowInsetsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 

     // Look for replaced fragments and apply the insets again. 
     setOnHierarchyChangeListener(new OnHierarchyChangeListener() { 
      @Override 
      public void onChildViewAdded(View parent, View child) { 
       requestApplyInsets(); 
      } 

      @Override 
      public void onChildViewRemoved(View parent, View child) { 

      } 
     }); 
    } 

} 
+0

perfekt für mich. Vielen Dank! –