wake-up-neo.com

Das untergeordnete CoordinatorLayout-Element überlappt BottomNavigationView

Ich versuche eine CoordinatorLayout mit einer BottomNavigationView, einer AppBarLayout und einer ViewPager zu verwenden. Hier ist mein Layout:

<?xml version="1.0" encoding="utf-8"?>
<Android.support.design.widget.CoordinatorLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:fitsSystemWindows="true"
    tools:context=".MainActivity">

    <Android.support.design.widget.AppBarLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:fitsSystemWindows="true"
        Android:theme="@style/AppTheme.AppBarOverlay">

        <Android.support.v7.widget.Toolbar
            Android:id="@+id/toolbar"
            Android:layout_width="match_parent"
            Android:layout_height="?attr/actionBarSize"
            app:layout_scrollFlags="enterAlways|scroll"
            app:popupTheme="@style/AppTheme.PopupOverlay"/>

    </Android.support.design.widget.AppBarLayout>

    <Android.support.v4.view.ViewPager
        Android:id="@+id/pager"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

    <Android.support.design.widget.BottomNavigationView
        Android:id="@+id/navigation"
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:layout_gravity="bottom"
        Android:background="?android:attr/windowBackground"
        app:itemIconTint="?colorPrimaryDark"
        app:itemTextColor="?colorPrimaryDark"
        app:menu="@menu/navigation"/>
</Android.support.design.widget.CoordinatorLayout>

Das Problem ist, dass CoordinatorLayout die ViewPager so weit wie möglich nach unten ausdehnt, sodass die Unterseite durch die BottomNavigationView verdeckt wird.

bounds of ViewPager

Dies geschieht auch, wenn CoordinatorLayout selbst nicht so weit ausgefahren ist:

bounds of CoordinatorLayout

Ich habe versucht, app:layout_insetEdge="bottom" zu BottomNavigationView und app:layout_dodgeInsetEdges="bottom" zu ViewPager hinzuzufügen, aber das hat ein anderes Problem: Es verschiebt die Unterseite von ViewPager nach oben, behält jedoch die gleiche Höhe, sodass die Oberseite jetzt abgehackt ist:

shifted ViewPager bounds

Ich habe zwei andere Experimente ausprobiert. Zuerst versuchte ich, die BottomNavigationView aus der CoordinatorLayout zu entfernen und sie zu Geschwistern unter einer vertikalen LinearLayout zu machen. Zweitens füge ich ViewPager und BottomNavigationView unter einem LinearLayout zusammen, in der Hoffnung, dass sie richtig angeordnet werden. Keiner von ihnen half: Im ersten Fall hat die CoordinatorLayout die ViewPager immer noch in Bezug auf den gesamten Bildschirm angepasst, entweder einen Teil hinter der BottomNavigationView versteckt oder die Oberseite abgehackt. Im zweiten Fall muss der Benutzer einen Bildlauf durchführen, um BottomNavigationView anzuzeigen.

Wie bekomme ich das Layout richtig?

P.S. Wenn ich das von @Anoop S S vorgeschlagene Layout ausprobierte (wobei CoordinatorLayout und BottomNavigationView als Geschwister unter einem RelativeLayout abgelegt wurden), erhalte ich Folgendes (wobei sich ViewPager immer noch hinter BottomNavigationView erstreckt):

result of Anoop's layout

Wie zuvor erstreckt sich die CoordinatorView selbst nur bis zur Oberkante der BottomNavigationView.

7
Ted Hopp

Grundsätzlich müssen Sie ein Relativelayout als übergeordnetes Element erstellen und BottomNavigationView und CoordinatorLayout als untergeordnetes Element festlegen. Richten Sie dann BottomNavigationView unten aus und setzen Sie CoordinatorLayout darüber. Bitte versuche den untenstehenden Code. Es könnte einige Attributfehler haben, weil ich es hier selbst geschrieben habe. Und Entschuldigung für die versaute Einrückung.

<?xml version="1.0" encoding="utf-8"?>

    <RelativeLayout
        xmlns:Android="http://schemas.Android.com/apk/res/Android"
        xmlns:app="http://schemas.Android.com/apk/res-auto"
        xmlns:tools="http://schemas.Android.com/tools"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        tools:context=".MainActivity">

    <Android.support.design.widget.CoordinatorLayout
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:layout_above="@+id/navigation"
        >

        <Android.support.design.widget.AppBarLayout
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:fitsSystemWindows="true"
            Android:theme="@style/AppTheme.AppBarOverlay">

            <Android.support.v7.widget.Toolbar
                Android:id="@+id/toolbar"
                Android:layout_width="match_parent"
                Android:layout_height="?attr/actionBarSize"
                app:layout_scrollFlags="enterAlways|scroll"
                app:popupTheme="@style/AppTheme.PopupOverlay"/>

        </Android.support.design.widget.AppBarLayout>

        <Android.support.v4.view.ViewPager
            Android:id="@+id/pager"
            Android:layout_width="match_parent"
            Android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

    </Android.support.design.widget.CoordinatorLayout>

<Android.support.design.widget.BottomNavigationView
            Android:id="@+id/navigation"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:layout_alignParentBottom="true"
            Android:background="?android:attr/windowBackground"
            app:itemIconTint="?colorPrimaryDark"
            app:itemTextColor="?colorPrimaryDark"
            app:menu="@menu/navigation"/>

    </RelativeLayout>
2
Anoop S S

Ich hatte ein ähnliches Problem mit einem Layout, das sehr nahe an OPs lag, und einem ViewPager mit 3 Seiten, aber nur die Seiten 2 und 3, die von appbar_scrolling_view_behavior betroffen sein sollten.

Nachdem ich mich stundenlang damit beschäftigt hatte, mögliche Lösungen zu erkunden (layout_dodgeInsetEdges, Window-Insets, der Versuch, die gemessene Größe der ViewPager-Seite zu ändern, Android: clipChildren, fitSystemWindows, ...), fand ich schließlich eine einfache Lösung.

Wie Vin Norman erläutert hat, wird ViewPager, das BottomNavigation überlappt, vollständig durch appbar_scrolling_view_behavior verursacht, das für ViewPager festgelegt wurde. AppBarLayout macht nur den gleichgeordneten Bruder mit appbar_scrolling_view_behavior im Vollbildmodus. So funktioniert das. 

Wenn Sie dieses Verhalten nur für bestimmte ViewPager-Seiten benötigen, gibt es eine einfache Lösung, die Sie für den OnPageChangeListener des ViewPagers anwenden können, um das Verhalten dynamisch zu ändern und die erforderliche Auffüllung hinzuzufügen/zu entfernen:

public class MyOnPageChangeListener extends  ViewPager.SimpleOnPageChangeListener {

        @Override
        public void onPageSelected(int position) {

           ...

           CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) _viewPager.getLayoutParams();

        if(position == 0) {
            params.setBehavior(null);
            params.setMargins(params.leftMargin, _appBarLayoutViewPagerMarginTopPx, 
                    params.rightMargin, _appBarLayoutViewPagerMarginBottomPx);
        } else {
            params.setBehavior(_appBarLayoutViewPagerBehavior);
            params.setMargins(params.leftMargin, 0, params.rightMargin, 0);
        }

        _viewPager.requestLayout();

        }

}

Bei der Position 0 (die Seite, auf der der ViewPager genau unter der Symbolleiste und über der BottomNavigationView erweitert werden soll), wird das Verhalten entfernt und die obere und untere Auffüllung (_appBarLayoutViewPagerMarginTopPx) und _appBarLayoutViewPagerMarginBottomPx wird hinzugefügt in Pixel für R.attr.actionbarSize und die Höhe für die NavigationBottomView (normalerweise sind beide 56dp)

Für alle anderen Seiten, die appbar_scrolling_view_behavior benötigen, stellen wir das zugehörige Bildlaufverhalten (zuvor in _appBarLayoutViewPagerBehavior gespeichert) wieder her und entfernen die Auffüllung oben und unten.

Ich habe diese Lösung getestet und funktioniert problemlos.

1
b0b

Ich habe mir einen anderen Ansatz ausgedacht (allerdings noch nicht getestet):

Ich unterstellte AppBarLayout.ScrollingViewBehavior, um den unteren Rand der Inhaltsansicht basierend auf der Höhe der BottomNavigationView (falls vorhanden) anzupassen. Auf diese Weise sollte es (hoffentlich) zukunftssicher sein, wenn sich die Höhe der BottomNavigationView aus irgendeinem Grund ändert.

Die Unterklasse (Kotlin):

class ScrollingViewWithBottomNavigationBehavior(context: Context, attrs: AttributeSet) : AppBarLayout.ScrollingViewBehavior(context, attrs) {
    // We add a bottom margin to avoid the bottom navigation bar
    private var bottomMargin = 0

    override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
        return super.layoutDependsOn(parent, child, dependency) || dependency is BottomNavigationView
    }

    override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
        val result = super.onDependentViewChanged(parent, child, dependency)

        if(dependency is BottomNavigationView && dependency.height != bottomMargin) {
            bottomMargin = dependency.height
            val layout = child.layoutParams as CoordinatorLayout.LayoutParams
            layout.bottomMargin = bottomMargin
            child.requestLayout()
            return true
        } else {
            return result
        }
    }
}

Und dann in das Layout XML:

app:layout_behavior=".ScrollingViewWithBottomNavigationBehavior"

anstatt

app:layout_behavior="@string/appbar_scrolling_view_behavior"
1
James

Dies wird durch app:layout_behavior="@string/appbar_scrolling_view_behavior" in Ihrem ViewPager verursacht. Wenn Sie diese Zeile entfernen, sehen Sie jetzt, dass sie in den CoordinatorLayout-Container passt (leider gehört dies dazu, dass Sie sich jetzt unter der Symbolleiste befinden).

Ich fand es hilfreich, CoordinatorLayout als ein FrameLayout zu behandeln, mit ein paar zusätzlichen Tricks. Das obige Attribut app: layout_behavior ist erforderlich, damit die Symbolleiste ein- und ausblättern kann. In Wirklichkeit führt das Layout dazu aus, dass die Ansicht mit der reduzierenden Symbolleiste (in Ihrem Fall Ihrem ViewPager) verknüpft ist Höhe der Werkzeugleiste größer als die Grenzen. Wenn Sie nach oben scrollen, wird die Ansicht innerhalb der Grenzen nach unten und die Symbolleiste nach oben verschoben. Nach unten scrollen, umgekehrt.

Nun zur BottomNavigationView! Wenn Sie, wie ich, die BottomNavigationView die ganze Zeit sichtbar haben möchten, verschieben Sie sie außerhalb des CoordinatorLayout, wie Anoop sagte. Verwenden Sie CoordinatorLayout nur für Dinge, die koordiniert werden müssen, alles andere außerhalb. Ich habe zufällig ein ConstraintLayout für meine übergeordnete Ansicht verwendet (Sie könnten jedoch RelativeLayout oder was auch immer für Sie funktioniert) verwenden. Mit ConstraintLayout würde es für Sie so aussehen:

 <Android.support.constraint.ConstraintLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:app="http://schemas.Android.com/apk/res-auto"
xmlns:tools="http://schemas.Android.com/tools"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:fitsSystemWindows="true">


<Android.support.design.widget.CoordinatorLayout
    Android:layout_width="0dp"
    Android:layout_height="0dp"
    app:layout_constraintBottom_toTopOf="@id/navigation"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    tools:context=".MainActivity">

    <Android.support.design.widget.AppBarLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:fitsSystemWindows="true"
        Android:theme="@style/AppTheme.AppBarOverlay">

        <Android.support.v7.widget.Toolbar
            Android:id="@+id/toolbar"
            Android:layout_width="match_parent"
            Android:layout_height="?attr/actionBarSize"
            app:layout_scrollFlags="enterAlways|scroll"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </Android.support.design.widget.AppBarLayout>

    <Android.support.v4.view.ViewPager
        Android:id="@+id/pager"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

</Android.support.design.widget.CoordinatorLayout>

<Android.support.design.widget.BottomNavigationView
    Android:id="@+id/navigation"
    Android:layout_width="0dp"
    Android:layout_height="wrap_content"
    Android:background="?android:attr/windowBackground"
    app:itemIconTint="?colorPrimaryDark"
    app:itemTextColor="?colorPrimaryDark"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:menu="@menu/navigation" />

 </Android.support.constraint.ConstraintLayout>

In der Android Studio-Designansicht wird der ViewPager immer noch größer angezeigt als der Container (wahrscheinlich sieht er immer noch hinter dem Bottom Nav aus). Aber das ist in Ordnung, wenn Sie den Inhalt des ViewPagers bis zum Ende durchgehen, wird dies angezeigt (d. H. Steht nicht hinter der unteren Navigation). Diese Eigenart in der Entwurfsansicht ist genau so, wie das CoordinatorLayout die Symbolleiste ein-/ausblenden lässt, wie bereits erwähnt.

0
Vin Norman

Falls noch jemand nach einer Lösung für dieses Problem sucht:

Ursache des Problems ist, dass CoordinatorLayout die Größe von AppBarLayout nicht korrekt berechnet, da es eine Symbolleiste mit app:layout_scrollFlags="enterAlways|scroll" Einstellung. Die Symbolleiste wird beim Scrollen ausgeblendet, sodass der gesamte verfügbare Speicherplatz für ViewPager verfügbar ist. In Wirklichkeit wird diese Symbolleiste jedoch so angezeigt, dass ViewPager hinter NavigationBar nach unten verschoben wird.

Der einfachste Weg, dies zu lösen, ist das Hinzufügen von Android:minHeight="?attr/actionBarSize" (oder welche Symbolleistenhöhe Sie auch verwenden) an AppBarLayout. Auf diese Weise weiß CoordinatorLayout genau, wie viel Speicherplatz für ViewPager erforderlich ist.

0
MarkoR

Wenn es für jemanden noch wichtig ist:

Versuchen Sie in der Antwort von Anoop SS, die RelativeLayout durch LinearLayout zu ersetzen. Setzen Sie auch layout_height von CoordinatorLayout auf 0dp und setzen Sie layout_weight auf 1.

Ich hatte fast das gleiche Problem ... nur dass ich eine statische AdView anstelle der BottomNavigationView am unteren Rand haben wollte. Anoop SS-Vorschläge ausprobiert, bekam ich zunächst dasselbe Verhalten wie OP: ViewPager hinter der AdView erweitert. Aber dann habe ich getan, was ich vorgeschlagen hatte und alles hat gut funktioniert.

Android-Layouts verhalten sich auf eine seltsame Weise oder es kann sein, dass es an guter Dokumentation fehlt oder an Wissen mangelt ... aber das Erstellen eines Layouts ist die meiste Zeit zu verdammt lästig.

0