The child views are overlapping because there is nothing set to prevent the overlap. The basic layout behavior of CoordinatorLayout
comes from FrameLayout
, which is intended to provide a block of the screen (i.e. a frame) for its child. With multiple children, they are stacked in front of each other in source order, back to front, so the last child is in front. This is why the fragment @id/main_map
and LinearLayout
overlap the toolbar @id/toolbar
and floating action button @id/fab
.
CoordinatorLayout
s own, more advanced layout mechanism is to animate child layout based on user interaction using Behavior
s (more on this later). If you aren't making use of animated layouts, a CoordinatorLayout
is not the most appropriate layout; the likes of a ConstraintLayout
or LinearLayout
would probably be better matches. This is potentially the simplest solution: change the layout. The following sections cover what to do if you want coordinated behaviors.
Behaviors
As previously mentioned, the main reason to use a CoordinatorLayout
is to animate the layout of children, done by giving views Behavior
s. A Behavior
will update the layout of the owning view when some dependent view changes. Some widgets have built-in behavior, such as FloatingActionButton
s and AppBarLayout
. Others can be assigned via the layout_behavior
property, setting it to the class name of the behavior to apply. For example, AppBarLayout
provides a behavior that will offset a scrolling view relative to an AppBarLayout
. This behavior is most often used with a collapsing toolbar. For example:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.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">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_height="192dp"
android:layout_width="match_parent">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:toolbarId="@+id/toolbar"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
app:contentScrim="?attr/colorPrimary"
>
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="top|fill_horizontal"
app:layout_insetEdge="top"
android:fitsSystemWindows="true"
>
...
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/..."
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
...
Absent app:layout_behavior
set to the appropriate behavior, @id/main
will overlap the AppBarLayout. For some views, you'll also have to set layout_width
and layout_height
to prevent overlap.
Note that if you create a new "Basic Activity" project, it will create something similar to the above.
Source Order
If you want widgets to overlap but want a different widget in front of another, change the source order so any widget in front comes after the widget it's supposed to eclipse.
Gravity
If the widgets are small enough, you can potentially pack them along different edges. Use gravity to anchor a child to an edge (e.g. center|bottom
, center|start
), corner (e.g. top|end
, bottom|start
) or center of a CoordinatorLayout
. This is what you do with a FloatingActionButton
:
<android.support.design.widget.FloatingActionButton
android:layout_gravity = "bottom|end"
[...]
>
You can also do this with, for example, a toolbar. You can't do this with Button
s, as the gravity attribute will affect the button's contents, rather than its position. If you want to position a Button
, wrap it in (e.g.) a FrameLayout
or Toolbar
. Make sure the wrapping view is not set to (explicitly or implicitly) fill the parent, such as with FrameLayout
s, else the wrapping view will expand to fill the entire frame, defeating the purpose of setting its gravity.
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|end"
...
/>
<Button
...
Layout based on gravity comes from FrameLayout
.
Dodging
Once you've positioned something on an edge, you can shift another child away from the positioned views using dodgeInsetEdges
. You must also set insetEdge
on the positioned children, so the offset for the dodging view can be calculated. Note that this only shifts the view; it does not resize it, so if you try to dodge opposite edges, they may cancel out.
<fragment
app:layout_dodgeInsetEdges="top"
...
/>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|end"
app:layout_insetEdge="top"
...
/>
While you can try inset dodging to prevent overlay, its intended purpose is to move views out of the way when another view (such as a SnackBar
) enters along an edge; inset dodging will animate the other views, sliding them out of the way as the new view comes in.
Keylines
You can horizontally position child views using keylines. Note that if you do this, you can only vertically position the view using vertical padding & margins, relative to the CoordinatorLayout
top.
Anchors
You can position a view along the edge of another view by using anchors. Set layout_anchor
of the anchored view to the ID of the view to anchor to, and which part of the view to anchor to with layout_anchorGravity
.
<fragment
android:id="@+id/main_map"
...
/>
<Button
app:layout_anchor="@id/main_map"
app:layout_anchorGravity="top|end"
...
/>