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.
Dies geschieht auch, wenn CoordinatorLayout
selbst nicht so weit ausgefahren ist:
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:
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):
Wie zuvor erstreckt sich die CoordinatorView
selbst nur bis zur Oberkante der BottomNavigationView
.
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>
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.
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"
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.
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.
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.