Reputation: 13029
I would like to implement a screen where I have a Card view containing a RecyclerView.
The CardView should of the same height of the content of the recycler view, this means that if the RecyclerView has few item, I should see the bottom corners and the bottom shadow of the card but if the RecyclerView has many items, the Card view should "scroll" with the RecyclerView to have the bottom corners and shadow of the cardview at the bottom of the RecylerView.
Here what it should look like when the RecyclerView is at top :
When the user begins to scroll, the top corners disappear with the RecyclerView scrolling :
And finally, when the user reaches the bottom of the RecyclerView, the bottom corners and the shadow of the CardView appears :
From now, I managed to have a working implementation by putting the RecyclerView inside the CardView and the CardView inside a NestedScrollView but this breaks the fling gesture...
<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:clipChildren="false"
android:id="@+id/containerLayout"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
tools:ignore="MissingPrefix">
<android.support.v4.widget.NestedScrollView
android:clipToPadding="false"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:paddingBottom="16dp"
android:paddingLeft="85dp"
android:paddingRight="85dp"
android:paddingTop="16dp">
<android.support.v7.widget.CardView
android:layout_height="wrap_content"
android:layout_width="match_parent"
app:cardBackgroundColor="?android:attr/windowBackground">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
</android.support.v7.widget.CardView>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
Do you have any hints or idea on how I could implement such design ? I guess that CoordinatorLayout could help me but I couldn't find anything ...
Thank you
Upvotes: 11
Views: 4653
Reputation: 3832
Picking up Oknesif's idea of a manipulated adapter, I made an adapter with three layouts (topitem, middleitem, bottomitem) with two XML drawable shapes for topitem and bottomitem. Thus, I was able to completely get rid of the NestedScrollView
and the CardView
.
This is what it looks like:
And here is the code. First, MainActivity
:
public class MainActivity extends AppCompatActivity {
final static int LIST_SIZE = 100;
final static int TOP = 0;
final static int BOTTOM = LIST_SIZE;
final static int MIDDLE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
final ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < LIST_SIZE; i++) {
list.add(i);
}
class Viewholder extends RecyclerView.ViewHolder {
TextView textView;
Viewholder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.textView);
}
}
RecyclerView recyclerView = findViewById(R.id.recyclerView);
final RecyclerView.Adapter<Viewholder> adapter = new RecyclerView.Adapter<Viewholder>() {
LayoutInflater inflater = LayoutInflater.from(MainActivity.this);
@Override
public Viewholder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case TOP:
return new Viewholder(inflater.inflate(R.layout.topitem, parent, false));
case BOTTOM:
return new Viewholder(inflater.inflate(R.layout.bottomitem, parent, false));
case MIDDLE:
default:
return new Viewholder(inflater.inflate(R.layout.middleitem, parent, false));
}
}
@Override
public void onBindViewHolder(Viewholder holder, int position) {
holder.textView.setText(String.valueOf(list.get(position)));
if (position != 0 && position != LIST_SIZE - 1) {
int color = position % 2 == 0 ? android.R.color.holo_orange_dark : android.R.color.holo_orange_light;
holder.itemView.setBackgroundColor(getResources().getColor(color));
}
}
@Override
public int getItemCount() {
return LIST_SIZE;
}
@Override
public int getItemViewType(int position) {
int itemViewType;
switch (position) {
case 0:
itemViewType = TOP;
break;
case LIST_SIZE - 1:
itemViewType = BOTTOM;
break;
default:
itemViewType = MIDDLE;
}
return itemViewType;
}
};
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
}
}
res/layout/activity.xml
:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/containerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:paddingLeft="25dp"
android:paddingRight="25dp" />
</android.support.design.widget.CoordinatorLayout>
res/layout/topitem.xml
:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/topbackground"
android:layout_marginTop="50dp"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="24sp"
android:textStyle="bold" />
res/layout/middleitem.xml
:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="24sp"
android:textStyle="bold" />
res/layout/bottomitem.xml
:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bottombackground"
android:layout_marginBottom="50dp"
android:textAlignment="center"
android:textColor="@android:color/white"
android:textSize="24sp"
android:textStyle="bold" />
res/drawable/topbackground.xml
:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="5dp"
android:topRightRadius="5dp" />
<solid android:color="@android:color/holo_orange_dark" />
</shape>
res/drawable/bottombackground.xml
:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:bottomLeftRadius="5dp"
android:bottomRightRadius="5dp" />
<solid android:color="@android:color/holo_orange_light" />
</shape>
EDIT:
Adding this line to the bottom XML item layouts:
android:elevation="12dp"
and changing the background to white, gives the following result:
Upvotes: 5
Reputation: 217
it's just a simple line of code
recycler.setNestedScrollingEnabled(false);
and don't forget to make cardview height to wrap_content
Upvotes: 1
Reputation: 1261
I have a suggestion based on the Constraintlayout which I have used before.
You can create two Guideline
to set the starting and ending position of the CardView during the scrolling process. Let me illustrate the XML for the start position of the view
<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:clipChildren="false"
android:id="@+id/containerLayout"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
tools:ignore="MissingPrefix">
<android.support.constraint.Guideline
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/guideline"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.1"/>
<android.support.constraint.Guideline
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/guideline2"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.9"/>
<android.support.v7.widget.CardView
android:layout_height="wrap_content"
android:layout_width="match_parent"
app:cardBackgroundColor="?android:attr/windowBackground"
app:layout_constraintTop_toTopOf="@+id/guideline">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_height="wrap_content"
android:layout_width="match_parent" />
</android.support.v7.widget.CardView>
here I am assuming that you want to leave roughly 10% of screen space empty on top. If you want less or more, please adjust.
Once the user starts scrolling you can adjust the top constraint of the Cardview to the top of the parent and once he reaches the bottom of the list you can adjust the bottom constraint of the Cardview to the guideline2
which will leave 10% of screen space below.
This should achieve the desired effect without much performance issues since you are doing away with the Scrollview.
Please let me know if you need me to elaborate any part of my answer in more detail.
Upvotes: 0