I need to implement the below layout for my RecyclerView
Items (The picture represents two rows):
This is my XML
file:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content"
android:background="@color/theme_primary_color">
<View
android:layout_width="60dp"
android:layout_height="60dp"
android:background="@drawable/half_circle"
android:translationZ="3dp"
app:layout_constraintBottom_toTopOf="@id/cvTop"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/cvTop" />
<androidx.cardview.widget.CardView
android:id="@+id/cvTop"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_marginStart="8dp"
android:layout_marginTop="2dp"
android:layout_marginEnd="8dp"
app:cardCornerRadius="@dimen/card_view_corner"
app:layout_constraintTop_toTopOf="parent">
</androidx.cardview.widget.CardView>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabCall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_call"
app:backgroundTint="@color/fab_green"
app:fabSize="mini"
app:layout_constraintBottom_toBottomOf="@+id/cvTop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cvTop" />
</androidx.constraintlayout.widget.ConstraintLayout>
I tried to set negative margins to root ConstraintLayout
but the second item went to top of the first one, but I need the first item to be on top of the second one.
So, this is not exactly what you want but at least it has the same layout as you want with a simpler approach.
So, the main challenges are:
Taking a curve cutout on the CardView
probably need to be built programmatically with canvas for a better result. But for simplicity, this is replaced by a BottomAppBar
wrapped in a CoordinatorLayout
in order to have the curve effect with the top circle/gap.
Replacing the top View
with Fab
in order to have an Inset FAB by setting the layout_anchor
to the BottomAppBar
. Check material design for this.
And having the cutout behavior requires to make the FAB like it doesn't exist by setting a transparent backgroundTint
& removing the outlineProvider
Making the top cutout (gap) of a particular row get overlapped to the top row like if it is a part of it. This works with the negative margin on the root view.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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="wrap_content"
android:layout_marginBottom="-20dp"
android:background="@android:color/transparent">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/cvTop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:layout_width="60dp"
android:layout_height="60dp"
android:outlineProvider="none"
app:backgroundTint="@android:color/transparent"
app:layout_anchor="@id/navigation" />
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/navigation"
android:layout_width="match_parent"
android:layout_height="150dp"
android:layout_gravity="bottom"
android:layout_marginStart="8dp"
android:layout_marginTop="2dp"
android:layout_marginEnd="8dp"
app:backgroundTint="@android:color/holo_blue_dark">
</com.google.android.material.bottomappbar.BottomAppBar>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabCall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:backgroundTint="@color/fab_green"
app:layout_constraintBottom_toBottomOf="@+id/cvTop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/cvTop" />
<TextView
android:id="@+id/tv_item_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textSize="18sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Item No. 0" />
</androidx.constraintlayout.widget.ConstraintLayout>
Note: you can remove the TextView
, I just added it to check the item doesn't change its position like you had in your case.
Preview on the RecyclerView
UPDATE:
A new challenge:
Only the top half of the FAB
will intercept touch events, as any particular row will be laid on top of its direct top row; and hence the FAB
of the top row won't intercept events in the intersection area.
Well, this can be manipulated well using canvas & custom View.
But also this can be solved in the current approach by laying out the rows of the RecyclerViews
from bottom to top.
One way to setReverseLayout(true)
and then either:
=> Reverse the RecyclerView
list positions before submitting to the adapter
=> Or use mList.size - 1 - position
instead of position
within the adapter. Assuming the list of items is mList