RecyclerView Multiple Layout View(s) in an Adapter class - android

Here is what I have achieved ? 3 different sections, 10 different items in each section.
Here is the tutorial link I am following and below is the Screenshot:
Trying to show different Views for each and every Section. Like:
For Section 1 (layout_1.xml)
For Section 2 (layout_2.xml)
For Section 3 (layout_3.xml)
But showing layout view of layout_1.xml in every Section... (Section 1, 2, 3)
May I know where I am doing mistake in my code, what I have missed ?
public class SectionListDataAdapter extends RecyclerView.Adapter<SectionListDataAdapter.SingleItemRowHolder> {
private ArrayList<SingleItemModel> itemsList;
private Context mContext;
public SectionListDataAdapter(Context context, ArrayList<SingleItemModel> itemsList) {
this.itemsList = itemsList;
this.mContext = context;
}
#Override
public SingleItemRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
switch (i) {
case 0:
View viewONE = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_1, null, false);
SingleItemRowHolder rowONE = new SingleItemRowHolder(viewONE);
return rowONE;
case 1:
View viewTWO = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_2, null, false);
SingleItemRowHolder rowTWO = new SingleItemRowHolder(viewTWO);
return rowTWO;
case 2:
View viewTHREE = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_3, null, false);
SingleItemRowHolder rowTHREE = new SingleItemRowHolder(viewTHREE);
return rowTHREE;
}
return null;
}
#Override
public void onBindViewHolder(SingleItemRowHolder holder, int i) {
SingleItemModel singleItem = itemsList.get(i);
holder.tvTitle.setText(singleItem.getName());
}
#Override
public int getItemCount() {
return (null != itemsList ? itemsList.size() : 0);
}
public class SingleItemRowHolder extends RecyclerView.ViewHolder {
protected TextView tvTitle;
protected ImageView itemImage;
public SingleItemRowHolder(View view) {
super(view);
this.tvTitle = (TextView) view.findViewById(R.id.tvTitle);
this.itemImage = (ImageView) view.findViewById(R.id.itemImage);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), tvTitle.getText(), Toast.LENGTH_SHORT).show();
}
});
}
}
}

Use this inside adapter's getItemViewType:
#Override
public int getItemViewType(int position) {
if (position == 0) {
return 0;
} else if(position == 1) {
return 1;
} else {
return 2;
}
}

to use multiple layouts according to position in recyclerview you have to override the getItemViewType(int position) method inside the adapter :-
#Override
public int getItemViewType(int position) {
if(position==0)
return 0;
else if(position==1)
return 1;
else
return 2;
}

FYI
RecyclerView can also be used to inflate multiple view types .
It will be easiest for you that create different Holder.
Create Different Adapter is best Solutions
Try with
#Override
public SingleItemRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
switch (i) {
case 0:
View viewONE = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_1, null, false);
SingleItemRowHolder rowONE = new SingleItemRowHolder(viewONE);
return rowONE;
case 1:
View viewTWO = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_2, null, false);
SingleItemRowHolderTwo rowTWO = new SingleItemRowHolderTwo (viewTWO);
return rowTWO;
case 2:
View viewTHREE = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_3, null, false);
SingleItemRowHolderThree rowTHREE = new SingleItemRowHolderThree(viewTHREE);
return rowTHREE;
}
return null;
}
Read RecyclerView can also be used to inflate multiple view types

As it was mention already, in order to getItemViewType method of RecyclerView.Adapter class, because if you will see in implementation of that method, you will see that it just return 0 all the time.
public int getItemViewType(int position) {
return 0;
}
And here adjusted code of your adapter which should solve your problem.
public class SectionListDataAdapter extends RecyclerView.Adapter<SectionListDataAdapter.SingleItemRowHolder> {
private static final int ITEM_TYPE_ROW_1 = 0;
private static final int ITEM_TYPE_ROW_2 = 1;
private static final int ITEM_TYPE_ROW_3 = 2;
private ArrayList<SingleItemModel> itemsList;
private Context context;
public SectionListDataAdapter(Context context, ArrayList<SingleItemModel> itemsList) {
this.itemsList = itemsList;
this.context = context;
}
#Override
public int getItemViewType(int position) {
switch (position) {
case 0:
return ITEM_TYPE_ROW_1;
case 1:
return ITEM_TYPE_ROW_2;
case 2:
return ITEM_TYPE_ROW_3;
}
throw new RuntimeException(String.format("unexpected position - %d", position));
}
#Override
public SingleItemRowHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
switch (viewType) {
case ITEM_TYPE_ROW_1:
View viewOne = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_1, null, false);
return new SingleItemRowHolder(viewOne);
case ITEM_TYPE_ROW_2:
View viewTwo = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_2, null, false);
return new SingleItemRowHolder(viewTwo);
case ITEM_TYPE_ROW_3:
View viewThree = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_3, null, false);
return new SingleItemRowHolder(viewThree);
}
throw new RuntimeException(String.format("unexpected viewType - %d", viewType));
}
#Override
public void onBindViewHolder(SingleItemRowHolder holder, int i) {
SingleItemModel singleItem = itemsList.get(i);
holder.tvTitle.setText(singleItem.getName());
}
#Override
public int getItemCount() {
return (null != itemsList ? itemsList.size() : 0);
}
class SingleItemRowHolder extends RecyclerView.ViewHolder {
TextView tvTitle;
ImageView itemImage;
public SingleItemRowHolder(View view) {
super(view);
this.tvTitle = (TextView) view.findViewById(R.id.tvTitle);
this.itemImage = (ImageView) view.findViewById(R.id.itemImage);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(v.getContext(), tvTitle.getText(), Toast.LENGTH_SHORT).show();
}
});
}
}
}

You can do it in a more simple way. Pass any flag while initializing adapter.
public class SectionListDataAdapter extends RecyclerView.Adapter<SectionListDataAdapter.SingleItemRowHolder> {
private ArrayList<SingleItemModel> itemsList;
private Context context;
private int view;
public SectionListDataAdapter(Context context, ArrayList<SingleItemModel> itemsList, int layoutFlag) {
this.itemsList = itemsList;
this.context = context;
switch(layoutFlag) {
case 0:
view = R.layout.layout_1;
break;
case 1:
view = R.layout.layout_2;
break;
case 2:
view = R.layout.layout_3;
break;
}
}
...
...
...
}
Use this view for layout reference. You just have to tell which layout to be inflated at the time of setting adapter.

You need to override the method
int getItemViewType (int position)
It receives the row number and you need to return the "type" of row i.e. 1 2 or 3.
The result will then be passed to onCreateViewHolder.

If for example you want to show this list of views:
type1
type2
type3
type1
type2
type3
then that should do the work:
public class SectionListDataAdapter extends RecyclerView.Adapter<SectionListDataAdapter.SingleItemRowHolder> {
private static final int ITEM_TYPE_ROW_1 = 0;
private static final int ITEM_TYPE_ROW_2 = 1;
private static final int ITEM_TYPE_ROW_3 = 2;
private ArrayList<SingleItemModel> itemsList;
private Context context;
private ArrayList<Integer> viewTypes = new ArrayList<>();
public SectionListDataAdapter(Context context, ArrayList<SingleItemModel> itemsList) {
this.itemsList = itemsList;
this.context = context;
viewTypes.add(ITEM_TYPE_ROW_1);
viewTypes.add(ITEM_TYPE_ROW_2);
viewTypes.add(ITEM_TYPE_ROW_3);
viewTypes.add(ITEM_TYPE_ROW_1);
viewTypes.add(ITEM_TYPE_ROW_2);
viewTypes.add(ITEM_TYPE_ROW_3);
}
#Override
public int getItemViewType(int position) {
return viewTypes.get(position);
}
#Override
public int getItemCount() {
return viewTypes.size();
}
.......
........
If you want to add/remove rows then it can be done inserting/removing viewTypes in the viewTypes Array and then calling RecyclerView notifyItemInserted or notifyItemRemoved methods the list will be updated with the new order and type of views.

Simply use frame layout with your fragment and add this fragment in your framelayout that will add like you want for that. So its also easy to handle. hope this will help you

Yes you need to override getItemViewType(int position) method which helps to inflate different views in recyclerview.
I am posting a sample code which may help you.
public class TransactionHistoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final int TYPE_HEADER = 1;
private final int TYPE_CHILD = 2;
private final Context mContext;
private final List<TransactionResultEntity> mTransactionList;
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_HEADER:
View headerView = LayoutInflater.from(mContext)
.inflate(R.layout.row_transaction_header, parent, false);
return new ParentTypeDataObjectHolder(headerView);
case TYPE_CHILD:
View childView = LayoutInflater.from(mContext)
.inflate(R.layout.row_transaction_child, parent, false);
return new ChildTypeDataObjectHolder(childView);
}
return null;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case TYPE_HEADER:
ParentTypeDataObjectHolder parentTypeDataObjectHolder = (ParentTypeDataObjectHolder) holder;
parentTypeDataObjectHolder.headerYearMonthTv.setText(mTransactionList.get(holder.getAdapterPosition()).getRowLabel());
break;
case TYPE_CHILD:
ChildTypeDataObjectHolder childTypeDataObjectHolder = (ChildTypeDataObjectHolder) holder;
childTypeDataObjectHolder.txnAmountTv.setText(mTransactionList.get(holder.getAdapterPosition()).getTransactionAmount());
break;
}
}
#Override
public int getItemCount() {
return mTransactionList.size();
}
#Override
public int getItemViewType(int position) {
if (mTransactionList.get(position).getDataType() == TYPE_HEADER)
return TYPE_HEADER;
else
return TYPE_CHILD;
}
class ParentTypeDataObjectHolder extends RecyclerView.ViewHolder {
private final TextView headerYearMonthTv;
public ParentTypeDataObjectHolder(View itemView) {
super(itemView);
headerYearMonthTv = (TextView) itemView.findViewById(R.id.row_transaction_header_tv);
}
}
class ChildTypeDataObjectHolder extends RecyclerView.ViewHolder {
TextView txnAmountTv;
public ChildTypeDataObjectHolder(View itemView) {
super(itemView);
txnAmountTv = (TextView) itemView.findViewById(R.id.transaction_child_txn_amount_tv);
}
}
}

All you need to do is override the method getItemViewType() inside your adapter.
You can write it as:
#Override
public int getItemViewType(int position) {
if (position < 0) {
return 0;
} else if(position < 20) {
return 1;
} else {
return 2;
}
}
Now the above logic works if your itemsList ArrayList have first 10 items of section 1, next 10 items of section 2, and last 10 items of section 3.
If that is not the case then you can have an integer field sectionNumber in your SingleItemModel class which specifies the section number in which that model belongs to. Now you can modify the method getItemViewType() as
#Override
public int getItemViewType(int position) {
SingleItemModel singleItemModel = itemsList.get(position);
if (singleItemModel.getSection() == 1) {
return 0;
} else if(singleItemModel.getSection() == 2) {
return 1;
} else {
return 2;
}
}

Okay, If I got it right, you want to make the second adapter, the one providing the row lists, variable, so it supports different layouts, based not on its position, but on some data from the main adapter (the one providing the sections). Therefore, overriding getItemViewType won't work, because the section data is contained in the main adapter, it does not even get there. So, the best, and cleanest course, would be to use... abstraction. Forget about multiple viewholders. Use one, and into it, bind a custom view. The custom views will provide both the specific layout files and will set the controls included in them. The holder will do just what it is intended to do: save ram by reusing views. the adavantage of this, is that you can have a clean hierarchy, which can grow in complexity in time, instead of a big, fat adapter which will become too hard to maintain.Here it is:
Since this is a lot of code to put it in here, I took your example project and modified to provide what I understood you were trying to do. Here it is:
https://github.com/fcopardo/exampleCustomViewsInHolder/tree/master
The highlights:
public class SectionListDataAdapter extends RecyclerView.Adapter<SectionListDataAdapter.SingleItemRowHolder> {
private ArrayList<SingleItemModel> itemsList;
private Context context;
private String section;
public SectionListDataAdapter(Context context, ArrayList<SingleItemModel> itemsList, String sectionName) {
this.itemsList = itemsList;
this.context = context;
this.section = sectionName;
}
#Override
public SingleItemRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
return new SingleItemRowHolder(RowFactory.getRow(context, section));
}
#Override
public void onBindViewHolder(SingleItemRowHolder holder, int i) {
holder.setData(itemsList.get(i));
}
#Override
public int getItemCount() {
return (null != itemsList ? itemsList.size() : 0);
}
public class SingleItemRowHolder extends RecyclerView.ViewHolder {
protected AbstractRowElement rowElement;
public SingleItemRowHolder(AbstractRowElement view) {
super(view);
this.rowElement = view;
}
public void setData(SingleItemModel singleItemModel){
rowElement.setItem(singleItemModel);
}
}
}
this is the variable layout adapter. As you can see, it uses only one ViewHolder, and a factory to provide the view instances you need.
public class RowFactory {
public static AbstractRowElement getRow(Context context, String name){
switch (name){
case "Section 1": return new FullRowElement(context);
case "Section 2": return new TextRowElement(context);
case "Section 3": return new ImageRowElement(context);
default:
Log.e("inflate", name);
return new FullRowElement(context);
}
}
}
this provides the custom views, each one using a different layout, but working with the same dataset, based on the section title.
public abstract class AbstractRowElement extends CardView{
protected int layout = 0;
protected SingleItemModel singleItemModel;
public AbstractRowElement(Context context) {
super(context);
inflateBaseLayout();
}
public AbstractRowElement(Context context, AttributeSet attrs) {
super(context, attrs);
inflateBaseLayout();
}
public AbstractRowElement(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflateBaseLayout();
}
protected void inflateBaseLayout() {
this.setContainer();
if(this.layout != 0) {
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(layout, this, true);
this.inflateComponents();
}
}
protected abstract void setContainer();
protected abstract void inflateComponents();
public void setItem(SingleItemModel itemModel){
this.singleItemModel = itemModel;
this.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getContext(), singleItemModel.getName()+"\n"+singleItemModel.getDescription(), Toast.LENGTH_SHORT).show();
}
});
setData(singleItemModel);
}
public abstract void setData(SingleItemModel itemModel);
}
Finally, this is the base view class for the adapter. Subclasses define the layout file to use, and put the desired data in the controls. The rest is pretty straightforward.
It would be totally possible to make this without custom views. You could just make something like :
int layoutFile = getLayoutForSection(section);
View v = LayoutInflater.from(viewGroup.getContext()).inflate(layoutFile, null);
But since I don't know how complex is the view you intend to create, it is best to keep things nicely separated. Have fun!

Related

NestedRecyclerView Problem - Last item of the second RecyclerView is duplicated into the first one after the keyboard appears

I'm desperate.
I have a nested recycler view. Each outer element has an array of inner elements. A different adapter has been created for the inner elements. I am creating an array of external elements of class "KairosWithEvents", each of which contains internal elements of class "Event". Everything is displayed well. When elements are added, everything is also updated. For testing, I created two objects of the "KairosWithEvent" class. In the first object I have placed two objects of the "Event" class, and in the second - three objects. But when I want to change the EditText value, the keyboard appears. And the last element of the second object appears in the first object. How can I fix it? Objects are not moved or duplicated, but showed incorrectly.
This is what a nestled recycler view looks like initially.
And this is what a nested recycler view looks like after the keyboard appears. The "Эвент5" element is duplicated to the first element for some reason.
Here's my code: Outer Adapter:
public class EventAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static List<Item> items;
private static RecyclerViewClickInterface listener;
private RecyclerView.RecycledViewPool sharedPool = new RecyclerView.RecycledViewPool();
private Context context;
public EventAdapter() {
items = new ArrayList<>();
this.context = context;
}
public EventAdapter(List<Item> newItems){
items = newItems;
}
class KairosViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
private TextView title;
private CheckBox iv;
private RecyclerView rv;
public KairosViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.tv);
iv = itemView.findViewById(R.id.iv);
rv = itemView.findViewById(R.id.rvSteaks);
itemView.setBackgroundColor(Color.parseColor("#91b3f2"));
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
#RequiresApi(api = Build.VERSION_CODES.N)
void bind(KairosWithEvents kairosWithEvents){
title.setText(kairosWithEvents.kairos.kairosId + " = " + kairosWithEvents.kairos.title);
LinearLayoutManager layoutManager = new LinearLayoutManager(
rv.getContext(),
LinearLayoutManager.VERTICAL,
false);
List<Item> itemsEvents = new ArrayList<>();
kairosWithEvents.events.forEach(i -> itemsEvents.add(new Item(Constants.EVENT_KAIROS, i)));
SubAdapter childAdapter = new SubAdapter(itemsEvents);
rv.setLayoutManager(layoutManager);
rv.setAdapter(childAdapter);
rv.setRecycledViewPool(sharedPool);
}
#Override
public void onClick(View view) {
int position = getAdapterPosition();
if (listener != null && position != RecyclerView.NO_POSITION){
listener.onItemKairosWithEvents( ((KairosWithEvents) items.get(position).object) );
}
}
#Override
public boolean onLongClick(View view) {
return false;
}
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
switch (viewType){
case Constants.KAIROS:
return new KairosViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item_ex, parent, false));
}
return null;
}
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)){
case Constants.KAIROS:
KairosWithEvents kairos = ((KairosWithEvents) items.get(position).object);
((KairosViewHolder) holder).bind(kairos);
break;
}
}
#Override
public int getItemCount() {
return items.size();
}
#Override
public int getItemViewType(int position) {
return items.get(position).type;
}
}
Inner Adapter:
public class SubAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private static List<Item> items;
public SubAdapter(List<Item> newItems) {
items = newItems;
}
static class EventViewHolder extends RecyclerView.ViewHolder {
private TextView title;
public EventViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.tv);
itemView.setBackgroundColor(Color.parseColor("#ffc8a8"));
}
void bind(Event event){
title.setText(event.eventId + " = " + event.title);
}
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
switch (viewType) {
case Constants.EVENT_KAIROS:
return new EventViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item_sub, parent, false));
}
return null;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)){
case Constants.EVENT_KAIROS:
Event event = (Event) items.get(position).object;
((EventViewHolder) holder).bind(event);
break;
}
}
#Override
public int getItemCount() {
return items.size();
}
#Override
public int getItemViewType(int position) {
return items.get(position).type;
}
}
In SubAdapter, remove the static keyword from your items field:
private static List<Item> items;
Should be this instead:
private List<Item> items;
You should make the same change to your outer adapter, but since there's only one of them at a time it doesn't wind up causing issues.

How do I make a dynamic list of items the children of a RecyclerView's items

Inside of my RecyclerView I want to have a dynamic number of items that are children of each item in my RecyclerView. I temporarily made extra layouts and set their visibility to gone and then turn them visible when the add button is pressed, but I know that isn't good practice. I don't think a ListView nested inside of a RecyclerView would be the right thing. If it is, then please enlighten me on how I would go about that. note: I can't get the first line of my code to show in the code snippet for some reason
Exercise Adapter
public class ExerciseAdapter extends RecyclerView.Adapter<ExerciseAdapter.ExerciseAdapterViewHolder> {
private List<Exercise> mExerciseList;
private static int counter = 0;
public class ExerciseAdapterViewHolder extends RecyclerView.ViewHolder {
final TextView anotherSetTextview;
final LinearLayout linearLayout4;
final LinearLayout linearLayout5;
ExerciseAdapterViewHolder(View view) {
super(view);
anotherSetTextview = view.findViewById(R.id.another_set);
linearLayout4 = view.findViewById(R.id.layout4);
linearLayout5 = view.findViewById(R.id.layout5);
}
}
#Override
public ExerciseAdapterViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
Context context = viewGroup.getContext();
int layoutIdForListItem = R.layout.list_item_cardview;
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(layoutIdForListItem, viewGroup, false);
return new ExerciseAdapterViewHolder(view);
}
#Override
public void onBindViewHolder(final ExerciseAdapterViewHolder holder, int position) {
Exercise currentExercise = mExerciseList.get(position);
String count = Integer.toString(position + 1) + ".";
holder.anotherSetTextview.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
counter++;
if(counter==1)
holder.linearLayout4.setVisibility(View.VISIBLE);
else if(counter==2)
holder.linearLayout5.setVisibility(View.VISIBLE);
}
});
}
#Override
public int getItemCount() {
if (null == mExerciseList) return 0;
return mExerciseList.size();
}
public void setExerciseData(List<Exercise> exerciseData) {
mExerciseList = exerciseData;
notifyDataSetChanged();
}
public void addNewExercise(Exercise e){
mExerciseList.add(e);
notifyDataSetChanged();
}
}
I'm making a workout tracker app for my class and I want the user to add exercises as needed and then they add the sets for each exercise as needed. Basically each Exercise object in the RecyclerView needs to have their own dynamic number of Set objects as the children. This screenshot of what I have so far should help visualize what I'm going for. I'm still new to posting on here, so forgive me for the poor formatting.
you can inflate the view dynamically in recycler view item. just have a Linear layout in R.layout.list_item_cardview layout, create a the data you want as another child layout and inflate the child layouts as you wish.
public class ExerciseAdapter
extendsRecyclerView.Adapter<ExerciseAdapter.ExerciseAdapterViewHolder> {
private List<Exercise> mExerciseList;
public class ExerciseAdapterViewHolder extends RecyclerView.ViewHolder {
final TextView anotherSetTextview;
final LinearLayout layout;
ExerciseAdapterViewHolder(View view) {
super(view);
anotherSetTextview = view.findViewById(R.id.another_set);
layout = view.findViewById(R.id.layout);
}
}
#Override
public ExerciseAdapterViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
Context context = viewGroup.getContext();
int layoutIdForListItem = R.layout.list_item_cardview;
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(layoutIdForListItem, viewGroup, false);
return new ExerciseAdapterViewHolder(view);
}
#Override
public void onBindViewHolder(final ExerciseAdapterViewHolder holder, int position) {
final Exercise currentExercise = mExerciseList.get(position);
for(int i=0;i<currentExercise.count;i++)
{
LayoutInflater inflater = LayoutInflater.from(holder.itemView.getContext());
LinearLayout linearLayout = inflater.inflate(R.layout.child, null);
holder.layout.addView(linearLayout);
}
holder.anotherSetTextview.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
currentExercise.count++;
notifyDataSetChanged();
}
});
}
#Override
public int getItemCount() {
if (null == mExerciseList) return 0;
return mExerciseList.size();
}
public void setExerciseData(List<Exercise> exerciseData) {
mExerciseList = exerciseData;
notifyDataSetChanged();
}
public void addNewExercise(Exercise e){
mExerciseList.add(e);
notifyDataSetChanged();
}
}

Multiple Views with RecyclerView - One view replacing the other

Card 1 for Ingredients and layout 2 for steps - Image public class DetailAdapter extends RecyclerView.Adapter {
private List<Ingredient> ingredientList;
private List<Step> stepList;
private Context context;
private boolean check;
private int which_layout;
public DetailAdapter(Context context, BakingModel bakingModel){
// Log.d("Inside Adapter" , bakingModel.getIngredients().get(0).getIngredient());
this.context = context;
ingredientList = bakingModel.getIngredients();
ingredientList.addAll(bakingModel.getIngredients());
stepList = bakingModel.getSteps();
stepList.addAll(bakingModel.getSteps());
Log.d("Inside Adapter" , ingredientList.get(0).getIngredient());
check = false;
which_layout = 0;
}
public static class DetailView1 extends RecyclerView.ViewHolder {
#Nullable#BindView(R.id.ingredient)
TextView textView_ing;
#Nullable#BindView(R.id.ingredient_list)
EditText editText_inglist;
public DetailView1(View itemView) {
super(itemView);
ButterKnife.bind(this,itemView);
}
}
public static class DetailView2 extends RecyclerView.ViewHolder{
#Nullable #BindView(R.id.step)
TextView textView_step;
public DetailView2(View itemView) {
super(itemView);
ButterKnife.bind(this,itemView);
}
}
#Override
public int getItemViewType(int position) {
if(!check){
which_layout = 1;
check = true;
Log.d("getItemView","Layout 1");
}
else {
which_layout = 2;
}
return which_layout;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
RecyclerView.ViewHolder viewHolder;
LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());
switch (which_layout){
case 1 :
View v1 = inflater.inflate(R.layout.fragment_detail1,viewGroup,false);
viewHolder = new DetailView1(v1);
Log.d("onCreateView","Create Layout 1");
break;
default:
View v2 = inflater.inflate(R.layout.fragment_detail2,viewGroup,false);
viewHolder = new DetailView2(v2);
Log.d("onCreateView","Create Layout 2");
}
return viewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
switch (viewHolder.getItemViewType()) {
case 1 :
DetailView1 detailView1 = (DetailView1) viewHolder;
detailView1.textView_ing.setText(R.string.ingredients);
for(Ingredient ingredient : ingredientList) {
detailView1.editText_inglist.append(ingredient.getIngredient() + "\n");
}
Log.d("onBindView","Bind Layout 1");
break;
default:
DetailView2 detailView2= (DetailView2) viewHolder;
detailView2.textView_step.setText(stepList.get(i).getShortDescription());
Log.d("onBindView","Bind Layout 2");
}
}
#Override
public int getItemCount() {
return stepList.size()+1;
}
}
When I change the value of which layout under the getItemView to 1 then the same edit text is filled stepList.size() number of times. Otherwise if it is as it is now, then there is no edit text. Only the step description that is DetailView2 is being populated.
DetailView1 is being replaced by DetailView2.
Edit : Basically what I am trying to achieve is create a card for ingredients. And then multiple cards for steps.

Android change TextView textSize on RecyclerView adapter from Activity

i'm trying to find how can i change my RecyclerView adapter textViews from Activity, in my activity i have two widgets such as increment_text_size and decrement_text_size which they must change adapter textviews,
for achieve to that, i create simple listener on activity to manage them:
Activity:
public interface IonChangeBookContentTextSize {
void incrementTextSize();
void decrementTextSize();
}
public static void setIonChangeBookContentTextSize(IonChangeBookContentTextSize l) {
ionChangeBookContentTextSize = l;
}
and after click on widgets i use this listener on adapter
Activity:
#OnClick(R.id.decrement_text_size)
public void decrement_text_size(View view) {
if (ionChangeBookContentTextSize != null) {
ionChangeBookContentTextSize.decrementTextSize();
}
}
#OnClick(R.id.increment_text_size)
public void increment_text_size(View view) {
if (ionChangeBookContentTextSize != null) {
ionChangeBookContentTextSize.incrementTextSize();
}
}
now in adapter i'm using this listener
public class ShowBookContentsAdapter extends RecyclerView.Adapter<ShowBookContentsAdapter.ShowBookContentsViewHolder> {
private List<Contents> list;
private Context context;
private static final int NOTE = 1;
public static IonChangeBottomViewVisibility ionChangeBottomViewvisibility;
private ShowBookContentsViewHolder holder;
private View view;
public ShowBookContentsAdapter(List<Contents> items, Context mContext, IonChangeBottomViewVisibility mOnChangeBottomViewVisibility) {
list = items;
context = mContext;
ionChangeBottomViewvisibility = mOnChangeBottomViewVisibility;
}
#Override
public ShowBookContentsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int layout = -1;
switch (viewType) {
case 0:
layout = R.layout.item_book_content_paragraph;
break;
case 1:
layout = R.layout.item_book_content_heading_one;
break;
}
view = LayoutInflater.from(parent.getContext()).inflate(layout, parent, false);
holder = new ShowBookContentsViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(ShowBookContentsViewHolder holder, final int position) {
switch (list.get(position).getContentType()) {
case 0:
implementingHeadingParagraphView(holder, position);
break;
case 1:
implementingHeadingOneView(holder, position);
break;
}
}
private void implementingHeadingParagraphView(final ShowBookContentsViewHolder holder, final int position) {
Utils.overrideFonts(context, holder.book_content_paragraph, PersianFontType.SHABNAM);
holder.book_content_paragraph.setText(Html.fromHtml(list.get(position).getContent()));
ActivityShowBookContent.setIonChangeBookContentTextSize(new ActivityShowBookContent.IonChangeBookContentTextSize() {
#Override
public void incrementTextSize() {
holder.book_content_paragraph.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
}
#Override
public void decrementTextSize() {
holder.book_content_paragraph.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
}
});
}
#Override
public int getItemViewType(int position) {
return list.get(position).getContentType();
}
#Override
public int getItemCount() {
return list.size();
}
public int getItemPosition(int itemId) {
return itemPositions.get(itemId);
}
public class ShowBookContentsViewHolder extends RecyclerView.ViewHolder {
#Nullable
#BindView(R.id.book_content_paragraph)
TextView book_content_paragraph;
#Nullable
#BindView(R.id.book_content_heading_one)
TextView book_content_heading_one;
public ShowBookContentsViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
}
}
}
implementing this listener as :
ActivityShowBookContent.setIonChangeBookContentTextSize(new ActivityShowBookContent.IonChangeBookContentTextSize() {
#Override
public void incrementTextSize() {
holder.book_content_paragraph.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
}
#Override
public void decrementTextSize() {
holder.book_content_paragraph.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
}
});
on implementingHeadingParagraphView method work for current position, not for all rows on recyclerview adapter, how can i fix this problem?
You do not have to create a listener for this purpose. You should hold a field named textSize in your adapter. Then, set this whenever you want from your activity.
public class ShowBookContentsAdapter extends RecyclerView.Adapter<ShowBookContentsAdapter.ShowBookContentsViewHolder> {
private int textSize;
// constructor etc.
#Override
public ShowBookContentsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_book_content_paragraph, parent, false);
final ShowBookContentsViewHolder holder new ShowBookContentsViewHolder(view);
return holder;
}
#Override
public void onBindViewHolder(ShowBookContentsViewHolder holder, final int position) {
implementingHeadingParagraphView(holder, position);
}
private void implementingHeadingParagraphView(final ShowBookContentsViewHolder holder, final int position) {
Utils.overrideFonts(context, holder.book_content_paragraph, PersianFontType.SHABNAM);
holder.book_content_paragraph.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSize);
holder.book_content_paragraph.setText(Html.fromHtml(list.get(position).getContent()));
}
public void setTextSizes(int textSize) {
this.textSize = textSize;
notifyDataSetChanged();
}
//... other adapter methods
public class ShowBookContentsViewHolder extends RecyclerView.ViewHolder {
#Nullable
#BindView(R.id.book_content_paragraph)
TextView book_content_paragraph;
#Nullable
#BindView(R.id.book_content_heading_one)
TextView book_content_heading_one;
public ShowBookContentsViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
}
}
call this from your activity
showBookContentsAdapter.setTextSizes(18);
You have to call notifydatasetchanged from you activity
1.First, save the font size on constant variable if temporary or use shared preferences if need in whole life cycle of app
Make a method in your activity to save font size
private void saveFontSize(boolean isFont){
IS_LARGE_FONT= isFont;
recyclerView.post(new Runnable(){
adapter.notifyDataSetChanged();
});
}
In your adapter class just check that value in bindholder
if(IS_LARGE_FONT)
{
//set large font
}
else{
// set small font
}

how to make the header of Recycle view invisible

I have a Recyclerview with header view as slider images and remaining view as normal recycler items.I am wondering if there is any way around to make the header view invisible depending upon some sort of condition.The recycler view consists of two separate layout files for this purpose: layout1 for header items and layout2 for normal recycler items and adapter will pick a layout and binds corresponding data at runtime.
This is my RecyclerView adapter RestaurantAdapter.java
public class RestaurantAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final String TAG = RestaurantAdapter.class.getName();
private List<Restaurant> mList;
private Context mContext;
private RestaurantType mRestaurantType;
private static final int RECYCLER_HEADER=0,RECYCLER_ITEMS=1;
private LayoutInflater inflater;
private SlideItemViewHolder slideItemViewHolder;
private List<ImageSliderPOJO> mData;
public RestaurantAdapter(Context context, List<Restaurant> list, RestaurantType restaurantType) {
this.mContext = context;
this.mList = list;
this.mRestaurantType = restaurantType;
inflater=LayoutInflater.from(context);
}
public void updateAdapter(List<ImageSliderPOJO> data){
this.mData = data;
this.notifyDataSetChanged();
}
#Override
public int getItemCount() {
return mList.size();
}
#Override
public int getItemViewType(int position) {
return position == 0 ? RECYCLER_HEADER : RECYCLER_ITEMS;
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int i) {
int viewType=viewHolder.getItemViewType();
switch (viewType){
case RECYCLER_HEADER:
slideItemViewHolder = (SlideItemViewHolder) viewHolder;
slideItemViewHolder.updateHeader();
break;
case RECYCLER_ITEMS:
final RecyclerItemViewHolder holder = (RecyclerItemViewHolder) viewHolder;
final Restaurant restaurant = mList.get(i);
Picasso.with(mContext)
.load(restaurant.getVendorLogo())
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.ic_launcher)
.into(holder.restaurentImageView);
holder.restaurentNameTextView.setText(restaurant.getName());
//Remaining code here
break;
default:
throw new RuntimeException(TAG+":Unable to bind the viewType"+viewType);
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
switch (viewType){
case RECYCLER_HEADER:
return new SlideItemViewHolder(inflater.inflate(R.layout.slide_show_restaurant_fragment_list,viewGroup,false));
case RECYCLER_ITEMS:
return new RecyclerItemViewHolder(inflater.inflate(R.layout.new_restautant_list_items, viewGroup, false));
default:
throw new RuntimeException(TAG+":Invalid ViewType "+viewType);
}
}
public static class RecyclerItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private ClickListener clickListener;
//Initialization here.
public RecyclerItemViewHolder(View v) {
super(v);
ButterKnife.inject(this, v);
v.setOnClickListener(this);
}
#Override
public void onClick(View v) {
clickListener.onClick(v, getPosition(), false);
}
public interface ClickListener {
/**
* Called when the view is clicked.
*
* #param v view that is clicked
* #param position of the clicked item
* #param isLongClick true if long click, false otherwise
*/
public void onClick(View v, int position, boolean isLongClick);
}
/* Setter for listener. */
public void setClickListener(ClickListener clickListener) {
this.clickListener = clickListener;
}
}
// id = 87,170
private class SlideItemViewHolder extends RecyclerView.ViewHolder {
SliderLayout sliderLayout;
LinearLayout rootLinearLayout;
public SlideItemViewHolder(View recyclerHeader) {
super(recyclerHeader);
sliderLayout = (SliderLayout) recyclerHeader.findViewById(R.id.home_slider);
rootLinearLayout = (LinearLayout) recyclerHeader.findViewById(R.id.rootLinearLayout);
}
private void updateHeader() {
if(Util.isNetworkAvailable(mContext)){
for (int i = 0; i < mData.size(); i++) {
DefaultSliderView defaultSliderView = new DefaultSliderView(mContext);
final int finalI = i;
defaultSliderView.image(mData.get(finalI).getImageUrl())
.setOnSliderClickListener(new BaseSliderView.OnSliderClickListener() {
#Override
public void onSliderClick(BaseSliderView slider) {
Restaurant restaurantById = Restaurant.searchByRestaurantId(mData.get(finalI).getTargetVendorId());
if(restaurantById != null)
openDetailFragment(restaurantById);
}
});
sliderLayout.addSlider(defaultSliderView);
}
}
}
}
public void openDetailFragment(Restaurant restaurant) {
Intent intent = new Intent(mContext, DetailTabActivity.class);
intent.putExtra(DetailTabActivity.INTENT_RESTAURANT_DATA, restaurant);
mContext.startActivity(intent);
}
public SliderLayout getSliderLayout(){
return slideItemViewHolder.sliderLayout;
}
}
And this adapter is set and updated from this fragment RestaurantFragment.java as:
private void setAdapter() {
dismissDialog();
if (getActivity() != null)
getActivity().runOnUiThread(new Runnable(){
#Override
public void run() {
if (restaurantList != null && restaurantList.size() > 0) {
restaurantRecyclerView.setVisibility(View.VISIBLE);
mEmptyListTextView.setVisibility(View.GONE);
restaurentListAdapter = new RestaurantAdapter(getActivity(), restaurantList, mRestaurantType);
restaurantRecyclerView.setAdapter(restaurentListAdapter);
restaurentListAdapter.updateAdapter(mData);
restaurantRecyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener(mLayoutManager) {
#Override
public void onLoadMore(int current_page) {
mCurrentPage = mCurrentPage + 1;
getHttpResturantData();
}
});
}
}
});
}
Is this much of an explanation helpful or should I paste more code?
Based on your code, it's possible to remove the header based on a certain condition.
Adjust your code to cater for the following:
#Override
public int getItemViewType(int position) {
if(hasHeaeder()) { // where you add the header
return position == 0 ? RECYCLER_HEADER : RECYCLER_ITEMS;
} else { // where you don't add the header
return RECYCLER_ITEMS;
}
}
This code also needs changing (currently it's wrong since it doesn't take care of the fact that the header adds 1 to the position).
final Restaurant restaurant = mList.get(i);
Replace it with
final Restaurant restaurant = hasHeader() ? mList.get(i +1) : mList.get(i);
Where hasHeader() is the code you need to write in order to determine whether or not the recycler should contain a header.

Categories

Resources