Switch between RecyclerView layouts when click on AlertDialog item list
To prevent mixed layout when scrolling you should use ViewFlipper
with three different RecyclerView's , meaning RecyclerView for each layout.
Step 1:
- Create a public enum for the three layouts
public enum ViewType {
CARD_LIST_LAYOUT, TITLE_LAYOUT, CARD_MAGAZINE_LAYOUT;
}
- In PostAdapter create a
ViewType
variable and pass it into constructor, and checking via if & else ononCreateViewHolder
to know Which layout choosed
PostAdapter(Context context, List<Item> items, ViewType viewType) {
this.context = context;
this.items = items;
this.viewType = viewType;
}
@NonNull
@Override
public PostAdapter.PostViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
View cardListLayout = inflater.inflate(R.layout.post_item_card_layout, parent, false);
View titleLayout = inflater.inflate(R.layout.post_item_grid_layout, parent, false);
View cardMagazineLayout = inflater.inflate(R.layout.card_magazine_layout, parent, false);
if (this.viewType == ViewType.TITLE_LAYOUT) {
return new PostViewHolder(titleLayout);
} else if (this.viewType == ViewType.CARD_LIST_LAYOUT) {
return new PostViewHolder(cardListLayout);
} else {
return new PostViewHolder(cardMagazineLayout);
}
}
Step 2:
- Create a
ViewFlipper
in the class that contains the main RecyclerView, with three child's Layout's and RecyclerView's
<ViewFlipper 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:id="@+id/parentLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
>
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/titleRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
<LinearLayout
android:id="@+id/linearLayout2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/cardRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
<LinearLayout
android:id="@+id/linearLayout3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/cardMagazineRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
</RelativeLayout>
</ViewFlipper>
- In MainActivity define variable's like this
viewFlipper = ((ViewFlipper) findViewById(R.id.parentLayout));
titleRecyclerView = (RecyclerView) findViewById(R.id.titleRecyclerView);
cardRecyclerView = (RecyclerView) findViewById(R.id.cardRecyclerView);
cardMagazineRecyclerView = (RecyclerView) findViewById(R.id.cardMagazineRecyclerView);
linearLayoutManager1 = new LinearLayoutManager(this);
cardRecyclerView.setLayoutManager(linearLayoutManager1);
linearLayoutManager2 = new LinearLayoutManager(this);
cardMagazineRecyclerView.setLayoutManager(linearLayoutManager2);
gridLayoutManager = new GridLayoutManager(this, 2, RecyclerView.VERTICAL, false);
titleRecyclerView.setLayoutManager(gridLayoutManager);
adapter1 = new PostAdapter(this, items, ViewType.TITLE_LAYOUT);
titleRecyclerView.setAdapter(adapter1);
adapter2 = new PostAdapter(this, items, ViewType.CARD_LIST_LAYOUT);
cardRecyclerView.setAdapter(adapter2);
adapter3 = new PostAdapter(this, items, ViewType.CARD_MAGAZINE_LAYOUT);
cardMagazineRecyclerView.setAdapter(adapter3);
- add
ScrollListener
for each RecyclerView
titleRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
}
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
}
}
});
cardRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
}
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
}
}
});
cardMagazineRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
}
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy > 0) {
}
}
});
Step 3:
- And finally on
onOptionsItemSelected
switch between three layout when click
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.change_layout) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.choose_layout));
String[] layouts = {"Title Layout", "Cards List", "Card Magazine Layout"};
builder.setItems(layouts, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int index) {
switch (index) {
case 0: // Title layout
viewFlipper.setDisplayedChild(0);
break;
case 1: // Cards List Layout
viewFlipper.setDisplayedChild(1);
break;
case 2: // Cards Magazine Layout
viewFlipper.setDisplayedChild(2);
}
}
});
AlertDialog dialog = builder.create();
dialog.show();
return true;
}
[Important Note]
you should doing any implementation of the old main RecyclerView three times each one for every layout or RecyclerView like the above example of ScrollListener
.
Hope this working with you, and please inform me if you have any inquiries
No need to use multiple Recyclerview
for this
You can achieve this using single Recyclerview
with Multiple viewType
When you want to change the layout of Recyclerview
just change the LayoutManager
and viewType
of your Recyclerview
it will work
SAMPLE CODE
Try this way
First Create a Three layout for your Multiple viewType
grid_layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="150dp"
android:layout_height="150dp"
app:cardElevation="10dp"
app:cardUseCompatPadding="true">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="150dp"
android:adjustViewBounds="true"
android:contentDescription="@string/app_name"
android:scaleType="centerCrop"
android:src="@drawable/dishu" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#6a000000"
android:gravity="center"
android:padding="10dp"
android:text="dummy text"
android:textColor="@android:color/white" />
</RelativeLayout>
</android.support.v7.widget.CardView>
cardlist_layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView 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"
app:cardElevation="10dp"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="150dp"
android:adjustViewBounds="true"
android:contentDescription="@string/app_name"
android:scaleType="centerCrop"
android:src="@drawable/dishu" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="5dp"
android:paddingEnd="10dp"
android:text="Dummy Title"
android:textColor="@android:color/black"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="5dp"
android:paddingEnd="10dp"
android:text="Dummy Tex" />
</LinearLayout>
</android.support.v7.widget.CardView>
title_layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView 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"
app:cardElevation="10dp"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="dummy text"
android:textColor="@android:color/black"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:text="I have three different layouts cardsListLayout , titleLayout , cardMagazineLayoutand there may be more in the future, which used as a views on onCreateViewHolder method." />
</LinearLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="150dp"
android:adjustViewBounds="true"
android:contentDescription="@string/app_name"
android:scaleType="centerCrop"
android:src="@drawable/dishu" />
</LinearLayout>
</android.support.v7.widget.CardView>
DataAdapter
package neel.com.recyclerviewdemo;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class DataAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public static final int ITEM_TYPE_GRID = 0;
public static final int ITEM_TYPE_CARD_LIST = 1;
public static final int ITEM_TYPE_TITLE_LIST = 2;
private Context mContext;
private int VIEW_TYPE = 0;
public DataAdapter(Context mContext) {
this.mContext = mContext;
}
public void setVIEW_TYPE(int viewType) {
VIEW_TYPE = viewType;
notifyDataSetChanged();
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = null;
// check here the viewType and return RecyclerView.ViewHolder based on view type
switch (VIEW_TYPE) {
case ITEM_TYPE_GRID:
// if VIEW_TYPE is Grid than return GridViewHolder
view = LayoutInflater.from(mContext).inflate(R.layout.grid_layout, parent, false);
return new GridViewHolder(view);
case ITEM_TYPE_CARD_LIST:
// if VIEW_TYPE is Card List than return CardListViewHolder
view = LayoutInflater.from(mContext).inflate(R.layout.cardlist_layout, parent, false);
return new CardListViewHolder(view);
case ITEM_TYPE_TITLE_LIST:
// if VIEW_TYPE is Title List than return TitleListViewHolder
view = LayoutInflater.from(mContext).inflate(R.layout.title_layout, parent, false);
return new TitleListViewHolder(view);
}
return new GridViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
final int itemType = getItemViewType(position);
// First check here the View Type
// than set data based on View Type to your recyclerview item
if (itemType == ITEM_TYPE_GRID) {
if (holder instanceof CardViewHolder) {
GridViewHolder viewHolder = (GridViewHolder) holder;
// write here code for your grid list
}
} else if (itemType == ITEM_TYPE_CARD_LIST) {
if (holder instanceof CardViewHolder) {
CardListViewHolder buttonViewHolder = (CardListViewHolder) holder;
// write here code for your grid list
}
} else if (itemType == ITEM_TYPE_TITLE_LIST) {
if (holder instanceof CardViewHolder) {
TitleListViewHolder buttonViewHolder = (TitleListViewHolder) holder;
// write here code for your TitleListViewHolder
}
}
}
@Override
public int getItemCount() {
return 40;
}
// RecyclerView.ViewHolder class for gridLayoutManager
public class GridViewHolder extends RecyclerView.ViewHolder {
public GridViewHolder(@NonNull View itemView) {
super(itemView);
}
}
// RecyclerView.ViewHolder class for Card list View
public class CardListViewHolder extends RecyclerView.ViewHolder {
public CardListViewHolder(@NonNull View itemView) {
super(itemView);
}
}
// RecyclerView.ViewHolder class for Title list View
public class TitleListViewHolder extends RecyclerView.ViewHolder {
public TitleListViewHolder(@NonNull View itemView) {
super(itemView);
}
}
}
MainActivity
package neel.com.recyclerviewdemo;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
public class MainActivity extends AppCompatActivity {
DataAdapter dataAdapter;
private RecyclerView myRecyclerView;
private LinearLayoutManager linearLayoutManager;
private GridLayoutManager gridLayoutManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myRecyclerView = findViewById(R.id.myRecyclerView);
myRecyclerView.setHasFixedSize(true);
linearLayoutManager = new LinearLayoutManager(this);
gridLayoutManager = new GridLayoutManager(this, 3);
myRecyclerView.setLayoutManager(gridLayoutManager);
dataAdapter = new DataAdapter(this);
myRecyclerView.setAdapter(dataAdapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.home_menu, menu);
// return true so that the menu pop up is opened
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_dialog) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Please choose a layout");
String[] layouts = {"Title Layout", "Cards List", "Grid View"};
builder.setItems(layouts, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int index) {
switch (index) {
case 0: // Title layout
dataAdapter.setVIEW_TYPE(2);
myRecyclerView.setLayoutManager(linearLayoutManager);
myRecyclerView.setAdapter(dataAdapter);
break;
case 1: // Cards List
dataAdapter.setVIEW_TYPE(1);
myRecyclerView.setLayoutManager(linearLayoutManager);
myRecyclerView.setAdapter(dataAdapter);
break;
case 2: // Grid Layout
dataAdapter.setVIEW_TYPE(0);
myRecyclerView.setLayoutManager(gridLayoutManager);
myRecyclerView.setAdapter(dataAdapter);
}
}
});
builder.show();
}
return super.onOptionsItemSelected(item);
}
}
activity_main layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/myRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
OUTPUT
https://youtu.be/L3slKTy2bzI
You can simply achieve this by implementing VIEW_TYPE inside your adapter. There is a overridden method in RecyclerView Adapter public int getViewType(int position)
, generally this method is used for mixing various types of views into a RecyclerView.
But you can use this in your case too. First define an enumeration of your view types. Then inside your adapter maintain a variable to store the current viewType, update that variable accordingly from the alert dialog item click.
Use that current viewType value into your onCreateViewHolder
and onBindViewHolder
methods to determine which layout you should use currently and which UI elements need to be updated now. Also update the RecyclerView layout manager from the alert dialog items on click.
public class PostAdapter extends RecyclerView.Adapter<PostAdapter.PostViewHolder> {
public enum ViewType {
VIEW_TYPE_GRID, VIEW_TYPE_CARD, VIEW_TYPE_CARD_MAGAZINE
}
private Context context;
private List<Item> items;
private ViewType currentViewType;
public PostAdapter(Context context, List<Item> items, ViewType viewType) {
this.context = context;
this.items = items;
this.currentViewType = viewType;
}
// Call this method from alert dialog item click.
public void updateViewType(ViewType type) {
this.currentViewType = type;
}
@Override
public int getItemViewType(int position) {
return this.currentViewType.ordinal();
}
@Override
public int getItemCount() {
return items.size();
}
@Override
public PostAdapter.PostViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
// here's the layouts that you want to switch between, based on the viewType
if (viewType == ViewType.VIEW_TYPE_CARD.ordinal()) {
View cardLayout = inflater.inflate(R.layout. post_item_card_layout, parent, false);
return new PostViewHolder(cardLayout);
}
else if (viewType == ViewType.VIEW_TYPE_GRID.ordinal()) {
View gridLayout = inflater.inflate(R.layout. post_item_grid_layout,parent,false);
return new PostViewHolder(gridLayout);
}
else {
View cardMagazineLayout = inflater.inflate(R.layout. card_magazine_layout, parent, false);
return new PostViewHolder(cardMagazineLayout);
}
}
@Override
public void onBindViewHolder(@NonNull PostViewHolder holder, int position) {
final Item item = items.get(position);
// Similarly handle different view layout items here based on the viewType returned from getItemViewType method.
if (getItemViewType(position) == ViewType.VIEW_TYPE_CARD.ordinal()) {
} else if (getItemViewType(position) == ViewType.VIEW_TYPE_GRID.ordinal()) {
} else {
}
}
}
Update: Every time you called the updateViewType
method you also need to call the notifyDataSetChanged
method like this:
mAdapter.updateViewType(PostAdapter.ViewType.VIEW_TYPE_GRID);
mAdapter.notifyDataSetChanged()
*** I have tested this code on my device and it is working as expected. Please note that you should carefully handle the VIEW_TYPE when inflating layouts and using layout elements like TextView and ImageView. In my previous answer I have mixed up the view types with wrong layouts, that might be the only reason so that you have seen mixed layouts as output.
*** Also, the getItemViewType
method was mistakenly renamed as getViewType
and that was another culprit in my code which causes mixed output.