Horizontal and Vertical RecyclerView under the same scroll - android

I have to do something like Instagram does. I have a horizontal RecyclerView for stories, and, below, a vertical RecyclerView for feed. I want to accomplish the same scroll behavior(the stories should go with feed at scroll, not stay on top as fixed). The only solution I found is NestedScrollView but it makes extremely bad performance for RecyclerViews, my screen freezes almost forever. I tried a lot of tricks found here like nestedScrollEnabled, autoLayoutMeasure etc. but nothing worked. Thanks.

Sorry if this explanation is too abstract. Let me know if you need me to be more explicit.
public class VerticalAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private static final int TYPE_HEADER = 0;
private static final int TYPE_POST = 1;
List<Post> posts;
List<FeedItems> feedItems; //this array is going to populate the horizontal recycler view. Notice that is passed it on the adapter constructor
public VerticalAdapter(List<Post> posts,List<FeedItems> feedItems) {
this.posts = posts;
this.feedItems = feedItems;
}
public void notifyFeedChanged(List<FeedItems> newFeedItems){
this.feedItems.clear();
this.feedItems = newFeedItems; //set the new feed items in the array
notifyItemChanged(0); //tell the main recycler view "Hey, update your first position". This will cause the onBindViewHolder to be called again an thus, the new items will be set into the horizontal recycler view
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_HEADER)
return new HeaderViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.your_header_layout, false));
else if (viewType == TYPE_POST)
return new PostViewHolder (LayoutInflater.from(parent.getContext()).inflate(R.layout.your_post_layout, false));
throw new RuntimeException("Don't know this type");
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof HeaderViewHolder){
//set adapter for the horizontal recycler view
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(((HeaderViewHolder) holder).recyclerView.getContext(),LinearLayoutManager.HORIZONTAL, false)
((HeaderViewHolder) holder).recyclerView.setLayoutManager(linearLayoutManager);
if (((HeaderViewHolder) holder).recyclerView.getAdapter() == null){ //only create the adapter the first time. the following times update the values
AnotherAdaterYouNeedToCreateForTheHorizontalRecyclerView adapter = new AnotherAdaterYouNeedToCreateForTheHorizontalRecyclerView(feedItems);
((HeaderViewHolder) holder).recyclerView.setAdapter(adapter);
}else {
((HeaderViewHolder) holder).recyclerView.getAdapter().notifyDataSetChanged();
}
}else if (holder instanceof PostViewHolder){
//just do the normal post binding
}
}
#Override
public int getItemCount() {
return posts.size() + 1; // +1 because of the header
}
#Override
public int getItemViewType(int position) {
return position == 0 ? TYPE_HEADER : TYPE_POST;
}
private class HeaderViewHolder extends RecyclerView.ViewHolder{
RecyclerView recyclerView;
public HeaderViewHolder(View itemView) {
super(itemView);
recyclerView = itemView.findViewById(R.id.the_recycler_view_id_on_the_heaedr_layout_file);
}
}
private class PostViewHolder extends RecyclerView.ViewHolder{
ImageView imageView;
public PostViewHolder(View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.post_image_view_or_whatever);
}
}}
So, your vertical recyclerview has the Post items (or whatever your post class is) drawn vertically, that's the easy thing to achieve. Now, for the horizontal view, you should implement a recyclerview header (check my Adapter example). The header layout will have another horizontal recyclerview.

In your XML Layout try keeping both the "header" recycler view and "Post" recycler view in a nested scroll view. It works for me.
<?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="match_parent"
android:background="#FFFFFF">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:text="Welcome"
android:textAppearance="#style/TextAppearance.AppCompat.Large"
android:textColor="#000000"
android:textSize="24sp"
android:textStyle="bold" />
<TextView
android:id="#+id/date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:textAppearance="#style/TextAppearance.AppCompat.Body1"
android:textColor="#color/black_80" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="horizontal" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rvv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

Related

How to change andriod vertical items layout to horizonatly?

I'm very new to android and I'm learning android from the internet by myself I want dynamic horizontal layouts. I want items as we use in css flex layouts. I want something like this
I tried some code with that code I'm getting the result something like this
I tried this code
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.72"
android:background="#drawable/main_bg_home">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:paddingTop="#dimen/_10sdp">
<androidx.cardview.widget.CardView
android:layout_width="#dimen/_230sdp"
android:layout_height="#dimen/_50sdp"
android:layout_marginTop="#dimen/_10sdp"
android:gravity="center"
app:cardBackgroundColor="#color/home_btn_bg_color"
app:cardCornerRadius="#dimen/_8sdp"
app:cardElevation="2dp">
<RelativeLayout
android:id="#+id/wsBtn"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="#dimen/_30sdp"
android:padding="#dimen/_6sdp"
android:layout_height="#dimen/_30sdp"
android:layout_centerVertical="true"
android:layout_marginLeft="#dimen/_12sdp"
android:background="#drawable/ic_icon_bg"
android:src="#drawable/whts" />
<ImageView
android:layout_width="#dimen/_12sdp"
android:layout_height="#dimen/_12sdp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="#dimen/_15sdp"
android:src="#drawable/right_arrow"
app:tint="#color/home_right_arrow_color" />
<TextView
android:id="#+id/txt1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="#dimen/_60sdp"
android:text="#string/whatsapp"
android:textColor="#color/home_btn_txt"
android:textSize="#dimen/_13sdp"
android:textStyle="bold" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
</ScrollView>
</LinearLayout>
Please update your LinearLayout orientation to horizontal
look like this
android:orientation="horizontal"
It looks like you're trying to create a horizontally scrollable list of items, similar to the CSS flex layout. To achieve this in Android, you can use a RecyclerView with a LinearLayoutManager set to horizontal orientation. Here's an example code:
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="horizontal">
</androidx.recyclerview.widget.RecyclerView>
And in your activity/fragment class, you can set the RecyclerView's layout manager to be a LinearLayoutManager with horizontal orientation:
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
Finally, you need to set an adapter for the RecyclerView, which will provide the data and layout for each item. Here's an example of a simple adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private List<String> mData;
public MyAdapter(List<String> data) {
mData = data;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, parent, false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.textView.setText(mData.get(position));
}
#Override
public int getItemCount() {
return mData.size();
}
static class MyViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public MyViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.text_view);
}
}
}
And finally, you set the adapter to the RecyclerView:
MyAdapter adapter = new MyAdapter(data);
recyclerView.setAdapter(adapter);
Replace item_view.xml with the layout you want for each item in the list and data with your data source. This is just a simple example to get you started. You can customize the adapter and item layout as needed for your use case.
For your case, better to use RecyclerView. You can see here a example that how to implement the same.
And for Grid you can use RecyclerView.GridLayoutManager(context, 2).
And for Vertical List you can use RecyclerView.LinearLayoutManager(context).
And for Horizontal List you can use RecyclerView.LineaLayoutManager(context, LinearLayoutManager.HORIZONTAL, false).
Hope this will help you:)

Add multiple expandable cardviews to layout

I am using AleSpero's library to create expandable cardviews for my layout. Works as expected, but now I want to add multiple cardviews in the same fragment layout, dynamically binding to some async list data that loads. How would that be possible?
Followed the demo on the library. Here is how I am adding the cards in the layout:
<LinearLayout
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/watchlist_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="20dp"
android:orientation="vertical"
android:clipChildren="false"
android:background="#FAFAFA">
<com.alespero.expandablecardview.ExpandableCardView
android:id="#+id/main_profile_card"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:title="My Watchlist"
app:inner_view="#layout/watchlist_layout"
app:expandOnClick="true" />
</LinearLayout>
#layout/watchlist_layout is a layout I want to repeat, it contain some textView and a recyclerView to show list data. Any help or guidance would be great.
You will need to create different Adapter and Inner Row XML Layout files for each RecyclerView.
For Ex: You inflate a layout in a RecyclerView inside the MainActivity. You are using an Adapter class which is inflating the rows based on your List. Inside onBindViewHolder, you should get the object of the inner RecyclerView which is present in the row layout of the parent view. Once you have the object, create another list and initialize another adapter for the inner recyclerview. Use the new adapter to populate data inside it (similar to the first recyclerview).
Keep in mind, the process remains same, with each recyclerview
Steps:
Create recylcerview inside the layout which is going to display the list
Create a separate row_layout to be inflated in each row based on the number of list data
Crete an Adapter class, which receives data from the parent class and inflates the layout (row_ayout) in the recyclerview
Repeat these steps for N number of Nested RecyclerViews
For the demo, I am attaching sample codes with this answer to help you understand my concept.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recylcerViewParent"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
item_layout_row.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.alespero.expandablecardview.ExpandableCardView
android:id="#+id/main_profile_card"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
app:expandOnClick="true"
app:inner_view="#layout/watchlist_inner"
app:title="My Watchlist" />
</RelativeLayout>
item_recycler_view_favorite.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout_with_favorites"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/favorites_count"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/margin_small"
android:layout_marginLeft="#dimen/margin_small"
android:textSize="12sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/margin_small"
android:layout_marginEnd="#dimen/margin_small"
android:orientation="horizontal"
android:weightSum="2">
<Button
android:id="#+id/btn_view_details"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_weight="1"
android:background="#android:color/transparent"
android:gravity="start|center_vertical"
android:padding="5dp"
android:text="Atish"
android:textColor="#color/colorPrimaryDark" />
<Button
android:id="#+id/btn_add_symbols"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_weight="1"
android:background="#android:color/transparent"
android:gravity="end|center_vertical"
android:padding="5dp"
android:text="Agrawal"
android:textColor="#color/colorPrimaryDark" />
</LinearLayout>
</LinearLayout>
watchlist_inner.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout_with_favorites"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/favorites_count"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/margin_small"
android:layout_marginLeft="#dimen/margin_small"
android:textSize="12sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="#dimen/margin_small"
android:layout_marginEnd="#dimen/margin_small"
android:orientation="horizontal"
android:weightSum="2">
<Button
android:id="#+id/btn_view_details"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_weight="1"
android:background="#android:color/transparent"
android:gravity="start|center_vertical"
android:padding="5dp"
android:text="VIEW DETAILS"
android:textColor="#color/colorPrimaryDark" />
<Button
android:id="#+id/btn_add_symbols"
android:layout_width="0dp"
android:layout_height="30dp"
android:layout_weight="1"
android:background="#android:color/transparent"
android:gravity="end|center_vertical"
android:padding="5dp"
android:text="ADD SYMBOLS"
android:textColor="#color/colorPrimaryDark" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view_favorite"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="#dimen/margin_small"
android:layout_marginEnd="#dimen/margin_small"
android:layout_marginBottom="#dimen/margin_small" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
List<String> stringsList = new ArrayList<>();
RecyclerView recyclerViewLayout;
InnerAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerViewLayout = findViewById(R.id.recylcerViewParent);
//Dummy add 10 objects in the list
for (int i = 0; i < 10; i++) {
stringsList.add(String.valueOf(i));
}
populateRecyclerView();
}
/**
* Create N items in the recycler view
*/
private void populateRecyclerView() {
//Initialize Adapter
recyclerViewLayout.setLayoutManager(new LinearLayoutManager(this));
recyclerViewLayout.setHasFixedSize(false);
adapter = new InnerAdapter(recyclerViewLayout, this);
adapter.setData(this.stringsList);
recyclerViewLayout.setAdapter(adapter);
}
}
InnerAdapter.java
public class InnerAdapter extends RecyclerView.Adapter<InnerAdapter.ViewHolder> {
private List<String> data;
private RecyclerView recyclerView;
private int i = 0;
private Context mContext;
public InnerAdapter(RecyclerView recyclerView, Context context) {
this.recyclerView = recyclerView;
this.mContext = context;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
//Inflater creates rows from a given layout file
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View v = inflater.inflate(R.layout.item_layout_row, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
//Method to perform actions on individual row based on the position. We will get back to this later
//Change the title of the CardView
holder.main_profile_card.setTitle(String.valueOf(position));
//Creating a dummy adapter again to populate the inner recyclerview
List<String> innerData = new ArrayList<>();
for (int i = 0; i < 10; i++) {
innerData.add(String.valueOf(i));
}
//Initialize Inner Adapter
holder.recycler_view_favorite.setLayoutManager(new LinearLayoutManager(mContext));
holder.recycler_view_favorite.setHasFixedSize(false);
InnerFavAdapter adapter = new InnerFavAdapter(holder.recycler_view_favorite, mContext);
adapter.setData(innerData);
holder.recycler_view_favorite.setAdapter(adapter);
}
#Override
public int getItemCount() {
return data.size();
}
public void setData(List<String> data) {
this.data = data;
}
class ViewHolder extends RecyclerView.ViewHolder {
RecyclerView recycler_view_favorite;
ExpandableCardView main_profile_card;
ViewHolder(View itemView) {
super(itemView);
//Get the object of the views from the row layout
main_profile_card = itemView.findViewById(R.id.main_profile_card);
recycler_view_favorite = itemView.findViewById(R.id.recycler_view_favorite);
}
}
}
InnerFavAdapter.java
public class InnerFavAdapter extends RecyclerView.Adapter<InnerFavAdapter.ViewHolder> {
private List<String> data;
private RecyclerView recyclerView;
private int i = 0;
private Context mContext;
public InnerFavAdapter(RecyclerView recyclerView, Context context) {
this.recyclerView = recyclerView;
this.mContext = context;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
//Inflater creates rows from a given layout file
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View v = inflater.inflate(R.layout.inner_recycler_view_favorite, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
//Method to perform actions on individual row based on the position. We will get back to this later
}
#Override
public int getItemCount() {
return data.size();
}
public void setData(List<String> data) {
this.data = data;
}
class ViewHolder extends RecyclerView.ViewHolder {
ViewHolder(View itemView) {
super(itemView);
}
}
}

How can I place a Button below a list, but limit the list's height, if it is too long

I want to have a RecyclerView and an ImageButton below that.
If there are only a few items in the list, the button should just stick to the end of the list. Like in this picture
If the list's content is too long, the button should stick to the end of the site and the scrollable list should be limited to the top of the button. Like in this picture
I tried out many layouts and configurations but I could only achieve:
That the RecyclerView fills the screen and the button stays always at the bottom, even when there are no items in the list.
OR
That the button always sticks to the bottom of the list. If the List is too long, it kicks out the button out of the screen and even further list items are outside of the screen and the list is not scrollable.
How is it possible to get the layout how described in the first part? Is it even possible with xml-only?
Here some code for trying out:
TestActivity.java:
public class TestActivity extends AppCompatActivity {
private final int NUMBER_OF_ITEMS = 2; // change me!
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
RecyclerView recyclerView = findViewById(R.id.recyclerView);
TestAdapter adapter = new TestAdapter();
recyclerView.setAdapter(adapter);
ArrayList<Object> list = new ArrayList<>();
for(int i = 0; i < NUMBER_OF_ITEMS; i++) list.add(null);
adapter.submitList(list);
}
public class TestAdapter extends ListAdapter<Object, TestViewHolder> {
TestAdapter() {
super(new DiffUtil.ItemCallback<Object>() {
#Override
public boolean areItemsTheSame(Object oldItem, Object newItem) {
return oldItem.equals(newItem);
}
#Override
public boolean areContentsTheSame(Object oldItem, Object newItem) {
return oldItem.equals(newItem);
}
});
}
#NonNull
#Override
public TestViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new TestViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_card, parent, false));
}
#Override
public void onBindViewHolder(#NonNull TestViewHolder holder, int position) {
}
}
class TestViewHolder extends RecyclerView.ViewHolder {
TestViewHolder(View itemView) {
super(itemView);
}
}
}
and here is the card-layout
activity_card.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="100dp"
android:foreground="#color/cardview_dark_background"
android:layout_margin="16dp">
</android.support.v7.widget.CardView>
and here is the activity-layout (only behaving well, with a full list. Button instead of ImageButton for simplification)
activity_test.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
tools:context=".ui.TestActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scrollbars="vertical"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
<Button
android:id="#+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Test" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageButton
android:layout_alignParentTop="true"
android:layout_width="match_parent"
android:layout_height="50dp" />
</RelativeLayout>

RecyclerView doesn't expand underneath the row

I'm trying to obtain an effect similar to this on my RecyclerView
I've tried to Google I/O approach that is described on similar questions here. My adapter code looks as follows.
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
ViewHolder itemViewHolder = (ViewHolder) holder;
final boolean isExpanded = position == mExpandedPosition;
itemViewHolder.mNotesTV.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
holder.itemView.setActivated(isExpanded);
holder.itemView.setOnClickListener(v -> {
mExpandedPosition = isExpanded ? -1 : position;
notifyItemChanged(position);
});
}
private class ViewHolder extends RecyclerView.ViewHolder {
TextView mNotesTV;
ViewHolder(View itemView) {
super(itemView);
mNotesTV = itemView.findViewById(R.id.notesTV);
}
}
The xml file for the TextView I'm trying to expand looks like this
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="#+id/notesTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Details" />
</RelativeLayout>
The xml file with the RecyclerView looks like this
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false">
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
android:clickable="true"
android:src="#drawable/ic_add_white_24dp" />
<android.support.v7.widget.RecyclerView
android:id="#+id/historyLV"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp" />
</android.support.design.widget.CoordinatorLayout>
And what I get, instead of the effect shown above (RecyclerView item expands and underneath are the desired items), I just get my TextView inside the actual row instead of below.
If you don't already have a layout for your viewholder, make one like the one below. Change the IDs and colors according to your preference:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/alwaysVisible"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white"
android:gravity="center"
android:padding="#dimen/activity_horizontal_margin"
android:text="02 Mar, 2018"
android:textColor="#android:color/holo_blue_bright" />
<TextView
android:id="#+id/conditionallyVisible"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/holo_blue_dark"
android:gravity="center"
android:padding="#dimen/activity_horizontal_margin"
android:text="Details"
android:textColor="#android:color/white"
android:visibility="gone" />
</LinearLayout>
Next, inflate it in your adapter like this (assuming the above layout is called item_layout:
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(mContext).inflate(R.layout.item_layout, parent, false);
return new ViewHolder(itemView);
}
In your viewholder class, get both the TextViews in the above layout like this:
private class ViewHolder extends RecyclerView.ViewHolder {
TextView mDateTV;
TextView mDetailsTV;
ViewHolder(View itemView) {
super(itemView);
mDateTV = itemView.findViewById(R.id.alwaysVisible);
mDetailsTV = itemView.findViewById(R.id.conditionallyVisible);
}
}
In onBindViewHolder, do the following:
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
ViewHolder itemViewHolder = (ViewHolder) holder;
final boolean isExpanded = position == mExpandedPosition;
itemViewHolder.mDetailsTV.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
holder.itemView.setActivated(isExpanded);
holder.itemView.setOnClickListener(v -> {
mExpandedPosition = isExpanded ? -1 : position;
notifyItemChanged(position);
});
}
That's all. I hope you can get this to work.
Maybe your requirement is make expandable recyclerview.
You can check this library https://github.com/thoughtbot/expandable-recycler-view

why recyclerview lollipop load everything?

I have a list of 100 items. I'm using a recyclerview with custom adapter.
It seems that all the items are load in the same times which cause OOM because I'am loading big images.
I have no problem with listview because listview does not load everything at once.
I heard that recyclerview load everything at once on lollipop, but how can I fix that?
public class ProductAdapter extends RecyclerView.Adapter<ProductViewHolder> {
List<Produit> list;
private Context ac;
private int nbColumn;
public ProductAdapter(List<Produit> list, Context ac)
{
this.list = list;
this.ac = ac;
this.nbColumn = 2;
}
public ProductAdapter(List<Produit> list, Context ac, int nbColumn)
{
this.list = list;
this.ac = ac;
this.nbColumn = nbColumn;
}
#Override
public ProductViewHolder onCreateViewHolder(ViewGroup viewGroup, int itemType) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_product,viewGroup,false);
return new ProductViewHolder(view);
}
public void setNbColumn(int nb)
{
this.nbColumn = nb;
}
#Override
public void onBindViewHolder(final ProductViewHolder myViewHolder, int position) {
if (list != null) {
System.out.println("LOAD ITEM!!!!!!!!!!!!");
final Produit myObject = list.get(position);
myViewHolder.bind(myObject);
}
}
#Override
public int getItemCount() {
return list.size();
}
}
without start scrolling the output is
I/System.out: LOAD ITEM!!!!!!!!!!!!!!!!!
X100 times
UPDATE :
this is the code of my layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v4.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="wrap_content"
>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
>
<TextView
android:id="#+id/cat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="This is a title"
android:padding="10dp"
android:gravity="center"
android:textColor="#color/black"
android:textStyle="bold"
android:textSize="17dp"
/>
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:src="#drawable/list1_unselected"
android:id="#+id/list1"
android:layout_below="#+id/cat"
android:paddingLeft="10dp"
android:clickable="true"
/>
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:src="#drawable/list2"
android:paddingLeft="10dp"
android:id="#+id/list2"
android:layout_below="#+id/cat"
android:layout_toRightOf="#+id/list1"
android:clickable="true"
/>
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/listingGrid"
android:layout_width="match_parent"
android:nestedScrollingEnabled="false"
android:layout_below="#+id/list2"
android:layout_height="match_parent"/>
</RelativeLayout>
</android.support.v4.widget.NestedScrollView>
</RelativeLayout>
I partially figured out my problem. After removing the NestedScrollView, the behavior of my Recyclerview works as expected. The thing is how can I combine both NestedScrollView and RecyclerView?
Thank you
Set android:layout_height of your RecyclerView to match_parent or some constant value.
EDIT:
Obviously it didn't help, since you have it all wrapped in NestedScrollView.
I would suggest removing NestedScrollView and combining all views from your RelativeLayout into another layout that you would use as header in your RecyclerView
Replace if (list != null) with if (myViewHolder != null) and it will work as intended

Categories

Resources