GridviewLayoutManager with headers - java

I am working on an app
I have implemented a working recycler view that receives a jsonarray, passes the data to a string array.
I now want to add section headers to the layout manager.
I have read two schools of thought on this:
- Change the spansize of the view to match the total columns of the grid
- create a custom adapter that loads a different view if the item is a section header.
Im not sure which way to go with this and its starting to confuse me
I have a list of data in an array which includes both headers and grid data (myDataset), i have also created another array with the mapping for the dataset in (myDatamap). In myDatamap i have a list of field types (1 for header and 0 for griddata. I was hoping to pass both arrays to the adaptor and for it decide if its a header or a griditem and then load the appropriate view.
I am leaning more towards loading a different view for the header items, allowing for me to customise the layout of the header easier.
Here is my adaptor code
package com.example.alex.recyclerview2;
import java.util.ArrayList;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.squareup.picasso.Picasso;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private ArrayList<String> mDataset;
private ArrayList<Integer> mDatamap;
private Context context;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public class ViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public TextView txtHeader;
public TextView txtFooter;
public ImageView imgImage;
public ViewHolder(View v) {
super(v);
txtHeader = (TextView) v.findViewById(R.id.firstLine);
txtFooter = (TextView) v.findViewById(R.id.secondLine);
imgImage = (ImageView) v.findViewById(R.id.icon);
}
}
public void add(int position, String item) {
mDataset.add(position, item);
notifyItemInserted(position);
}
public void remove(String item) {
int position = mDataset.indexOf(item);
mDataset.remove(position);
notifyItemRemoved(position);
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(ArrayList<String> myDataset, ArrayList<Integer> myDatamap) {mDataset = myDataset;myDatamap=mDatamap; }
// Create new views (invoked by the layout manager)
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.sub_layout, parent, false);
context = v.getContext();
// set the view's size, margins, paddings and layout parameters
ViewHolder vh = new ViewHolder(v);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
final String name = mDataset.get(position);
Picasso.with(context).load("http://www.500kgiveaway.co.uk/" + name).resize(200,200).into(holder.imgImage);
// holder.txtHeader.setText(mDataset.get(position));
holder.txtHeader.setText(name);
holder.txtHeader.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
remove(name);
}
});
holder.txtFooter.setText("Footer: " + mDataset.get(position));
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return mDataset.size();
}
}

ive not had much interest in this post but here is the answer
i hope this help someone else
What i have done is implemented a custom view adaptor to manage the item types
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.squareup.picasso.Picasso;
import java.util.ArrayList;
public class ElementsAdapter extends RecyclerView.Adapter<ElementsAdapter.ViewHolder> {
private ArrayList<String> mDataset;
private ArrayList<Integer> mDatamap;
public Context context;
private static final int VIEW_HEADER = 0;
private static final int VIEW_NORMAL = 1;
private View headerView;
private int datasetSize;
public class ViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public TextView txtHeader;
public TextView txtFooter;
public ImageView imgImage;
//header
public TextView headertext;
public ViewHolder(View v, int viewType) {
super(v);
switch (viewType){
case 1:
txtHeader = (TextView) v.findViewById(R.id.firstLine);
txtFooter = (TextView) v.findViewById(R.id.secondLine);
imgImage = (ImageView) v.findViewById(R.id.icon);
case 0:
headertext = (TextView) v.findViewById(R.id.headertext);
}
}
}
public ElementsAdapter(ArrayList<String> myDataset, ArrayList<Integer> myDatamap) {
mDataset = myDataset;
mDatamap = myDatamap;
}
#Override
public int getItemViewType(int position) {
return isHeader(position) == 1 ? VIEW_HEADER : VIEW_NORMAL;
}
#Override
public int getItemCount() {
return mDataset.size();
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_HEADER) {
// create a new view
View sub_view = LayoutInflater.from(parent.getContext()).inflate(R.layout.header, parent, false);
Context context = sub_view.getContext();
// set the view's size, margins, paddings and layout parameters
ViewHolder vh = new ViewHolder(sub_view,viewType);
return vh;
// return new HeaderViewHolder(headerView);
} else {
// create a new view
View sub_view = LayoutInflater.from(parent.getContext()).inflate(R.layout.sub_layout, parent, false);
context = sub_view.getContext();
// set the view's size, margins, paddings and layout parameters
ViewHolder vh = new ViewHolder(sub_view, viewType);
return vh;
}
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
if (isHeader(position) == 1) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
final String name = mDataset.get(position);
// holder.txtHeader.setText(mDataset.get(position));
viewHolder.headertext.setText(name);
} else {
// - get element from your dataset at this position
// - replace the contents of the view with that element
final String name = mDataset.get(position);
Picasso.with(context).load("http://www.500kgiveaway.co.uk/"+name).resize(200,200).into(viewHolder.imgImage);
// holder.txtHeader.setText(mDataset.get(position));
viewHolder.txtHeader.setText(name);
viewHolder.txtHeader.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick (View v){
//remove(name);
}
}
);
viewHolder.txtFooter.setText("Footer: "+mDataset.get(position));
}
//ViewHolder holder = (ViewHolder) viewHolder;
//holder.textView.setText("Position " + (position - 1));
}
public int isHeader(int position) {
return mDatamap.get(position) ==1 ? 1:0;}
}

Why don't you use both solutions? If you set the span size you can easily set a textview or whatever you want the header to be.
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int position) {
return adapter.isHeader(position) ? gridLayoutManager.getSpanCount() : 1;
}
});
Then in the adapter have a general class Item that says if the item is a header and has some information about the real item that should be shown, in my case since I have image paths for showing images and titles for headers I just use a attribute text but you can use something like int realPositionInTheirLists since headers titles and images paths are in two separate lists:
private static class Item {
public boolean isHeader;
public String text;
public Item(String text, boolean isHeader) {
this.isHeader = isHeader;
this.text = text;
}
}
Then you have something like this on the methods that tell which type of item it is:
#Override
public int getItemViewType(int position) {
return mItems.get(position).isHeader ? VIEW_TYPE_HEADER : VIEW_TYPE_CONTENT;
}
public boolean isHeader(int position) {
return mItems.get(position).isHeader;
}
Then finally on the two methods that inflate the view and bind the data you inflate the view that you want depending on whether it is a header or not and bind the data using the Item class:
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
if (viewType == VIEW_TYPE_HEADER) {
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.grid_header, parent, false);
} else {
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.grid_item, parent, false);
}
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
final Item item = mItems.get(position);
holder.bindItem(item, position);
}
Being the holder.bindItem a method of the ViewHolder class. There you can choose however you want to separate both views.

Related

App is crashing when i click on any item in recyclerview

I was creating a recycler view and trying to create a toast when any item is clicked on using interface(i'm doing for first time). But my app is crashing when i click any item in recycler view.
Here is my main java file code.
package com.example.creatingrecyclerview;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements newsOnCkicked {
RecyclerView r;
String arr[]={"1","2","3","4","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1","1"};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
r=findViewById(R.id.rv);
r.setLayoutManager(new LinearLayoutManager(this));
CustomAdapter c=new CustomAdapter(arr,this);
r.setAdapter(c);
}
#Override
public void onClick(View view) {
}
#Override
public String onItemClicked(String s) {
Toast.makeText(this, "Item clicked is "+ s, Toast.LENGTH_SHORT).show();
return null;
}
}
And this is my customadapter java code.
package com.example.creatingrecyclerview;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
private String[] localDataSet;
private newsOnCkicked k;
/**
* Provide a reference to the type of views that you are using
* (custom ViewHolder).
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView textView;
public ViewHolder(View view) {
super(view);
// Define click listener for the ViewHolder's View
textView = (TextView) view.findViewById(R.id.textView2);
}
public TextView getTextView() {
return textView;
}
}
/**
* Initialize the dataset of the Adapter.
*
* #param dataSet String[] containing the data to populate views to be used
* by RecyclerView.
*/
public CustomAdapter(String[] dataSet,newsOnCkicked k) {
localDataSet = dataSet;
this.k=k;
}
// Create new views (invoked by the layout manager)
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
// Create a new view, which defines the UI of the list item
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.mylayout, viewGroup, false);
RecyclerView.ViewHolder v=new ViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View vi) {
k.onItemClicked(localDataSet[v.getAdapterPosition()]);
}
});
return new ViewHolder(view);
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
// Get element from your dataset at this position and replace the
// contents of the view with that element
viewHolder.getTextView().setText(localDataSet[viewHolder.getAdapterPosition()]);
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return localDataSet.length;
}
}
interface newsOnCkicked extends View.OnClickListener {
String onItemClicked(String s);
}
Please help me find the error in the code. It will be great:)
You have to set you click listener with viewHolder . copy the code below and paste then run
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
private String[] localDataSet;
private newsOnCkicked k;
/**
* Provide a reference to the type of views that you are using
* (custom ViewHolder).
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
private final TextView textView;
public ViewHolder(View view) {
super(view);
// Define click listener for the ViewHolder's View
textView = (TextView) view.findViewById(R.id.textView2);
}
public TextView getTextView() {
return textView;
}
}
/**
* Initialize the dataset of the Adapter.
*
* #param dataSet String[] containing the data to populate views to be used
* by RecyclerView.
*/
public CustomAdapter(String[] dataSet,newsOnCkicked k) {
localDataSet = dataSet;
this.k=k;
}
// Create new views (invoked by the layout manager)
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
// Create a new view, which defines the UI of the list item
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.mylayout, viewGroup, false);
return new ViewHolder(view);
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
// Get element from your dataset at this position and replace the
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View vi) {
k.onItemClicked(localDataSet[viewHolder.getAdapterPosition()]);
}
});
viewHolder.getTextView().setText(localDataSet[viewHolder.getAdapterPosition()]);
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return localDataSet.length;
}
}
interface newsOnCkicked extends View.OnClickListener {
String onItemClicked(String s);
}
if still get problem comment below with error log.

How to add a footer button to my recycler view

I want to add a footer to my recycler view. I have attached the custom adapter code below. How can I do this? I want the button to fill the recycler view with new items. I can do the button functioning on my own. I'm only facing the problem adding the footer.
I want something like the image attached below:
package com.example.myapplication.Adapter;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.example.myapplication.Interface.ItemClickListener;
import com.example.myapplication.Model.RSSObject;
import com.example.myapplication.R;
class FeedViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener{
public TextView textTitle,txtDate,textContent;
private ItemClickListener itemClickListener;
public FeedViewHolder(View itemView) {
super(itemView);
textTitle = (TextView) itemView.findViewById(R.id.textTitle);
txtDate = itemView.findViewById(R.id.textPubDate);
textContent = itemView.findViewById(R.id.textContent);
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
public FeedViewHolder(View itemView, ItemClickListener itemClickListener) {
super(itemView);
this.itemClickListener = itemClickListener;
}
#Override
public void onClick(View v) {
itemClickListener.onClick(v, getAdapterPosition(), false);
}
#Override
public boolean onLongClick(View v) {
itemClickListener.onClick(v, getAdapterPosition(), true);
return true;
}
}
public class FeedAdapter extends RecyclerView.Adapter<FeedViewHolder> {
private RSSObject.RssObject rssObject;
private Context mContext;
private LayoutInflater layoutInflater;
public FeedAdapter(RSSObject.RssObject rssObject, Context mContext) {
this.rssObject = rssObject;
this.mContext = mContext;
layoutInflater = layoutInflater.from(mContext);
}
#Override
public int getItemViewType(int position) {
return super.getItemViewType(position);
}
#Override
public FeedViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View itemView = layoutInflater.inflate(R.layout.row,parent,false);
return new FeedViewHolder(itemView);
}
#Override
public void onBindViewHolder(final FeedViewHolder holder, final int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String url = rssObject.getItems().get(holder.getAdapterPosition()).getLink();
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(url));
v.getContext().startActivity(i);
}
});
String title = rssObject.getItems().get(position).getTitle();
title = title.replace("&","");
holder.textTitle.setText(title);
String description = rssObject.getItems().get(position).getDescription();
description = android.text.Html.fromHtml(description).toString();
holder.textContent.setText(description);
holder.txtDate.setText(rssObject.getItems().get(position).getPubDate());
}
#Override
public int getItemCount() {
return rssObject.items.size();
}
}
There are two ways:
If you want a fixed view put under RecyclerView in your xml file a Relative Layout (or orher) with the button inside
Or if you want the button as an element of the list you should add a view with the button ad the last position of your view
If you wish to add a footer, you need to specify it as another type of view.
In your adapter code you should add logic for creation of another holder for the footer.
Please see the following Kotlin example:
const val REGULAR_VIEW_TYPE = 0
const val FOOTER_VIEW_TYPE = 1
class FooterExampleAdapter(
private val data: List<String>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
when (viewType) { // Use 'when' to create the correct holder for any position
REGULAR_VIEW_TYPE -> RegularViewHolder(parent.inflateView(R.layout.regular_layout))
FOOTER_VIEW_TYPE -> FooterViewHolder(parent.inflateView(R.layout.footer_layout))
}
// Handle binding by checking holder type
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is RegularViewHolder) holder.bind(data[position])
else if (holder is FooterViewHolder) holder.bind()
}
override fun getItemCount() = data.size + 1 // We need the extra 1 for the footer to be counted
// Here is where we specify which view type should be presented at position
override fun getItemViewType(position: Int) =
if (position == data.size) FOOTER_VIEW_TYPE else REGULAR_VIEW_TYPE
}
class RegularViewHolder(v: View) : RecyclerView.ViewHolder(v) {
fun bind(string: String) {}
}
class FooterViewHolder(v: View) : RecyclerView.ViewHolder(v) {
fun bind() {}
}

Different layouts for cardViews in a RecycleView [duplicate]

From Create dynamic lists with RecyclerView:
When we create a RecyclerView.Adapter we have to specify ViewHolder that will bind with the adapter.
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private String[] mDataset;
public MyAdapter(String[] myDataset) {
mDataset = myDataset;
}
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public ViewHolder(TextView v) {
super(v);
mTextView = v;
}
}
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.some_layout, parent, false);
//findViewById...
ViewHolder vh = new ViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.mTextView.setText(mDataset[position]);
}
#Override
public int getItemCount() {
return mDataset.length;
}
}
Is it possible to create RecyclerView with multiple view types?
Yes, it's possible. Just implement getItemViewType(), and take care of the viewType parameter in onCreateViewHolder().
So you do something like:
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
class ViewHolder0 extends RecyclerView.ViewHolder {
...
public ViewHolder0(View itemView){
...
}
}
class ViewHolder2 extends RecyclerView.ViewHolder {
...
public ViewHolder2(View itemView){
...
}
#Override
public int getItemViewType(int position) {
// Just as an example, return 0 or 2 depending on position
// Note that unlike in ListView adapters, types don't have to be contiguous
return position % 2 * 2;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case 0: return new ViewHolder0(...);
case 2: return new ViewHolder2(...);
...
}
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
switch (holder.getItemViewType()) {
case 0:
ViewHolder0 viewHolder0 = (ViewHolder0)holder;
...
break;
case 2:
ViewHolder2 viewHolder2 = (ViewHolder2)holder;
...
break;
}
}
}
If the layouts for view types are only a few and binding logics are simple, follow Anton's solution. But the code will be messy if you need to manage the complex layouts and binding logics.
I believe the following solution will be useful for someone who need to handle complex view types.
Base DataBinder class
abstract public class DataBinder<T extends RecyclerView.ViewHolder> {
private DataBindAdapter mDataBindAdapter;
public DataBinder(DataBindAdapter dataBindAdapter) {
mDataBindAdapter = dataBindAdapter;
}
abstract public T newViewHolder(ViewGroup parent);
abstract public void bindViewHolder(T holder, int position);
abstract public int getItemCount();
......
}
The functions needed to define in this class are pretty much same as the adapter class when creating the single view type.
For each view type, create the class by extending this DataBinder.
Sample DataBinder class
public class Sample1Binder extends DataBinder<Sample1Binder.ViewHolder> {
private List<String> mDataSet = new ArrayList();
public Sample1Binder(DataBindAdapter dataBindAdapter) {
super(dataBindAdapter);
}
#Override
public ViewHolder newViewHolder(ViewGroup parent) {
View view = LayoutInflater.from(parent.getContext()).inflate(
R.layout.layout_sample1, parent, false);
return new ViewHolder(view);
}
#Override
public void bindViewHolder(ViewHolder holder, int position) {
String title = mDataSet.get(position);
holder.mTitleText.setText(title);
}
#Override
public int getItemCount() {
return mDataSet.size();
}
public void setDataSet(List<String> dataSet) {
mDataSet.addAll(dataSet);
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView mTitleText;
public ViewHolder(View view) {
super(view);
mTitleText = (TextView) view.findViewById(R.id.title_type1);
}
}
}
In order to manage DataBinder classes, create an adapter class.
Base DataBindAdapter class
abstract public class DataBindAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return getDataBinder(viewType).newViewHolder(parent);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
int binderPosition = getBinderPosition(position);
getDataBinder(viewHolder.getItemViewType()).bindViewHolder(viewHolder, binderPosition);
}
#Override
public abstract int getItemCount();
#Override
public abstract int getItemViewType(int position);
public abstract <T extends DataBinder> T getDataBinder(int viewType);
public abstract int getPosition(DataBinder binder, int binderPosition);
public abstract int getBinderPosition(int position);
......
}
Create the class by extending this base class, and then instantiate DataBinder classes and override abstract methods
getItemCount
Return the total item count of DataBinders
getItemViewType
Define the mapping logic between the adapter position and view type.
getDataBinder
Return the DataBinder instance based on the view type
getPosition
Define convert logic to the adapter position from the position in the specified DataBinder
getBinderPosition
Define convert logic to the position in the DataBinder from the adapter position
I left a more detailed solution and samples on GitHub, so please refer to RecyclerView-MultipleViewTypeAdapter if you need.
The below is not pseudocode. I have tested it and it has worked for me.
I wanted to create a headerview in my recyclerview and then display a list of pictures below the header which the user can click on.
I used a few switches in my code and don't know if that is the most efficient way to do this, so feel free to give your comments:
public class ViewHolder extends RecyclerView.ViewHolder{
//These are the general elements in the RecyclerView
public TextView place;
public ImageView pics;
//This is the Header on the Recycler (viewType = 0)
public TextView name, description;
//This constructor would switch what to findViewBy according to the type of viewType
public ViewHolder(View v, int viewType) {
super(v);
if (viewType == 0) {
name = (TextView) v.findViewById(R.id.name);
decsription = (TextView) v.findViewById(R.id.description);
} else if (viewType == 1) {
place = (TextView) v.findViewById(R.id.place);
pics = (ImageView) v.findViewById(R.id.pics);
}
}
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType)
{
View v;
ViewHolder vh;
// create a new view
switch (viewType) {
case 0: //This would be the header view in my Recycler
v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recyclerview_welcome, parent, false);
vh = new ViewHolder(v,viewType);
return vh;
default: //This would be the normal list with the pictures of the places in the world
v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recyclerview_picture, parent, false);
vh = new ViewHolder(v, viewType);
v.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
Intent intent = new Intent(mContext, nextActivity.class);
intent.putExtra("ListNo",mRecyclerView.getChildPosition(v));
mContext.startActivity(intent);
}
});
return vh;
}
}
//Overridden so that I can display custom rows in the recyclerview
#Override
public int getItemViewType(int position) {
int viewType = 1; //Default is 1
if (position == 0) viewType = 0; //If zero, it will be a header view
return viewType;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
//position == 0 means it's the info header view on the Recycler
if (position == 0) {
holder.name.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mContext,"name clicked", Toast.LENGTH_SHORT).show();
}
});
holder.description.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mContext,"description clicked", Toast.LENGTH_SHORT).show();
}
});
//This means it is beyond the headerview now as it is no longer 0. For testing purposes, I'm alternating between two pics for now
} else if (position > 0) {
holder.place.setText(mDataset[position]);
if (position % 2 == 0) {
holder.pics.setImageDrawable(mContext.getResources().getDrawable(R.drawable.pic1));
}
if (position % 2 == 1) {
holder.pics.setImageDrawable(mContext.getResources().getDrawable(R.drawable.pic2));
}
}
}
Here is a complete sample to show a RecyclerView with two types, the view type decided by the object.
Class model
open class RecyclerViewItem
class SectionItem(val title: String) : RecyclerViewItem()
class ContentItem(val name: String, val number: Int) : RecyclerViewItem()
Adapter code
const val VIEW_TYPE_SECTION = 1
const val VIEW_TYPE_ITEM = 2
class UserAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var data = listOf<RecyclerViewItem>()
override fun getItemViewType(position: Int): Int {
if (data[position] is SectionItem) {
return VIEW_TYPE_SECTION
}
return VIEW_TYPE_ITEM
}
override fun getItemCount(): Int {
return data.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (viewType == VIEW_TYPE_SECTION) {
return SectionViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_user_section, parent, false)
)
}
return ContentViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.item_user_content, parent, false)
)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = data[position]
if (holder is SectionViewHolder && item is SectionItem) {
holder.bind(item)
}
if (holder is ContentViewHolder && item is ContentItem) {
holder.bind(item)
}
}
internal inner class SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(item: SectionItem) {
itemView.text_section.text = item.title
}
}
internal inner class ContentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(item: ContentItem) {
itemView.text_name.text = item.name
itemView.text_number.text = item.number.toString()
}
}
}
item_user_section.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/text_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#eee"
android:padding="16dp" />
item_user_content.xml
<?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="wrap_content"
android:orientation="horizontal"
android:padding="32dp">
<TextView
android:id="#+id/text_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Name" />
<TextView
android:id="#+id/text_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Example using
val dataSet = arrayListOf<RecyclerViewItem>(
SectionItem("A1"),
ContentItem("11", 11),
ContentItem("12", 12),
ContentItem("13", 13),
SectionItem("A2"),
ContentItem("21", 21),
ContentItem("22", 22),
SectionItem("A3"),
ContentItem("31", 31),
ContentItem("32", 32),
ContentItem("33", 33),
ContentItem("33", 34),
)
recyclerAdapter.data = dataSet
recyclerAdapter.notifyDataSetChanged()
Create a different ViewHolder for different layouts
RecyclerView can have any number of viewholders you want, but for better readability let’s see how to create one with two ViewHolders.
It can be done in three simple steps
Override public int getItemViewType(int position)
Return different ViewHolders based on the ViewType in onCreateViewHolder() method
Populate View based on the itemViewType in onBindViewHolder() method
Here is a small code snippet:
public class YourListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int LAYOUT_ONE = 0;
private static final int LAYOUT_TWO = 1;
#Override
public int getItemViewType(int position)
{
if(position==0)
return LAYOUT_ONE;
else
return LAYOUT_TWO;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = null;
RecyclerView.ViewHolder viewHolder = null;
if(viewType==LAYOUT_ONE)
{
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.one,parent,false);
viewHolder = new ViewHolderOne(view);
}
else
{
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.two,parent,false);
viewHolder= new ViewHolderTwo(view);
}
return viewHolder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
if(holder.getItemViewType() == LAYOUT_ONE)
{
// Typecast Viewholder
// Set Viewholder properties
// Add any click listener if any
}
else {
ViewHolderOne vaultItemHolder = (ViewHolderOne) holder;
vaultItemHolder.name.setText(displayText);
vaultItemHolder.name.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
.......
}
});
}
}
//**************** VIEW HOLDER 1 ******************//
public class ViewHolderOne extends RecyclerView.ViewHolder {
public TextView name;
public ViewHolderOne(View itemView) {
super(itemView);
name = (TextView)itemView.findViewById(R.id.displayName);
}
}
//**************** VIEW HOLDER 2 ******************//
public class ViewHolderTwo extends RecyclerView.ViewHolder {
public ViewHolderTwo(View itemView) {
super(itemView);
..... Do something
}
}
}
getItemViewType(int position) is the key.
In my opinion, the starting point to create this kind of recyclerView is the knowledge of this method. Since this method is optional to override, it is not visible in RecylerView class by default which in turn makes many developers (including me) wonder where to begin.
Once you know that this method exists, creating such RecyclerView would be a cakewalk.
Let's see one example to prove my point. If you want to show two layout
at alternate positions do this
#Override
public int getItemViewType(int position)
{
if(position%2==0) // Even position
return LAYOUT_ONE;
else // Odd position
return LAYOUT_TWO;
}
Relevant Links:
Check out the project where I have implemented this.
Yes, it is possible.
Write a generic view holder:
public abstract class GenericViewHolder extends RecyclerView.ViewHolder
{
public GenericViewHolder(View itemView) {
super(itemView);
}
public abstract void setDataOnView(int position);
}
then create your view holders and make them extend the GenericViewHolder. For example, this one:
public class SectionViewHolder extends GenericViewHolder{
public final View mView;
public final TextView dividerTxtV;
public SectionViewHolder(View itemView) {
super(itemView);
mView = itemView;
dividerTxtV = (TextView) mView.findViewById(R.id.dividerTxtV);
}
#Override
public void setDataOnView(int position) {
try {
String title= sections.get(position);
if(title!= null)
this.dividerTxtV.setText(title);
}catch (Exception e){
new CustomError("Error!"+e.getMessage(), null, false, null, e);
}
}
}
then the RecyclerView.Adapter class will look like this one:
public class MyClassRecyclerViewAdapter extends RecyclerView.Adapter<MyClassRecyclerViewAdapter.GenericViewHolder> {
#Override
public int getItemViewType(int position) {
// depends on your problem
switch (position) {
case : return VIEW_TYPE1;
case : return VIEW_TYPE2;
...
}
}
#Override
public GenericViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
if(viewType == VIEW_TYPE1){
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout1, parent, false);
return new SectionViewHolder(view);
}else if( viewType == VIEW_TYPE2){
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout2, parent, false);
return new OtherViewHolder(view);
}
// Cont. other view holders ...
return null;
}
#Override
public void onBindViewHolder(GenericViewHolder holder, int position) {
holder.setDataOnView(position);
}
Yes, it is possible.
In your adapter getItemViewType Layout like this ....
public class MultiViewTypeAdapter extends RecyclerView.Adapter {
private ArrayList<Model>dataSet;
Context mContext;
int total_types;
MediaPlayer mPlayer;
private boolean fabStateVolume = false;
public static class TextTypeViewHolder extends RecyclerView.ViewHolder {
TextView txtType;
CardView cardView;
public TextTypeViewHolder(View itemView) {
super(itemView);
this.txtType = (TextView) itemView.findViewById(R.id.type);
this.cardView = (CardView) itemView.findViewById(R.id.card_view);
}
}
public static class ImageTypeViewHolder extends RecyclerView.ViewHolder {
TextView txtType;
ImageView image;
public ImageTypeViewHolder(View itemView) {
super(itemView);
this.txtType = (TextView) itemView.findViewById(R.id.type);
this.image = (ImageView) itemView.findViewById(R.id.background);
}
}
public static class AudioTypeViewHolder extends RecyclerView.ViewHolder {
TextView txtType;
FloatingActionButton fab;
public AudioTypeViewHolder(View itemView) {
super(itemView);
this.txtType = (TextView) itemView.findViewById(R.id.type);
this.fab = (FloatingActionButton) itemView.findViewById(R.id.fab);
}
}
public MultiViewTypeAdapter(ArrayList<Model>data, Context context) {
this.dataSet = data;
this.mContext = context;
total_types = dataSet.size();
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
switch (viewType) {
case Model.TEXT_TYPE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.text_type, parent, false);
return new TextTypeViewHolder(view);
case Model.IMAGE_TYPE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.image_type, parent, false);
return new ImageTypeViewHolder(view);
case Model.AUDIO_TYPE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.audio_type, parent, false);
return new AudioTypeViewHolder(view);
}
return null;
}
#Override
public int getItemViewType(int position) {
switch (dataSet.get(position).type) {
case 0:
return Model.TEXT_TYPE;
case 1:
return Model.IMAGE_TYPE;
case 2:
return Model.AUDIO_TYPE;
default:
return -1;
}
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int listPosition) {
Model object = dataSet.get(listPosition);
if (object != null) {
switch (object.type) {
case Model.TEXT_TYPE:
((TextTypeViewHolder) holder).txtType.setText(object.text);
break;
case Model.IMAGE_TYPE:
((ImageTypeViewHolder) holder).txtType.setText(object.text);
((ImageTypeViewHolder) holder).image.setImageResource(object.data);
break;
case Model.AUDIO_TYPE:
((AudioTypeViewHolder) holder).txtType.setText(object.text);
}
}
}
#Override
public int getItemCount() {
return dataSet.size();
}
}
For the reference link: Android RecyclerView Example – Multiple ViewTypes
Simpler than ever, forget about ViewTypes. It is not recommended to use multiple viewtypes inside one adapter. It will mess the code and break the single responsibility principle since now the adapter needs to handle logic to know which view to inflate.
Now imagine working in large teams where each team has to work in one of those viewtypes features. It will be a mess to touch the same adapter by all the teams that work in the different viewtypes. This is solved using ConcatAdapter where you isolate the adapters. Code them one by one and then just merge them inside one view.
From recyclerview:1.2.0-alpha04 you now can use ConcatAdapter.
If you need a view with different viewTypes, you can just write the Adapters for each section and just use ConcatAdapter to merge all of them inside one recyclerview.
ConcatAdapter
This image shows three different viewtypes that one recyclerview has, header, content and footer.
You only create one adapter for each section, and then just use ConcatAdapter to merge them inside one recyclerview:
val firstAdapter: FirstAdapter = …
val secondAdapter: SecondAdapter = …
val thirdAdapter: ThirdAdapter = …
val concatAdapter = ConcatAdapter(firstAdapter, secondAdapter,
thirdAdapter)
recyclerView.adapter = concatAdapter
That's all you need to know. If you want to handle loading state, for example remove the last adapter after some loading happened, you can use LoadState.
For more in depth information follow Florina Muntenescu post here https://medium.com/androiddevelopers/merge-adapters-sequentially-with-mergeadapter-294d2942127a
Following Anton's solution, I came up with this ViewHolder which holds/handles/delegates different type of layouts.
But I am not sure if the replacing new layout would work when the recycling view's ViewHolder is not the type of the data roll in.
So basically,
onCreateViewHolder(ViewGroup parent, int viewType) is only called when new view layout is needed;
getItemViewType(int position) will be called for the viewType;
onBindViewHolder(ViewHolder holder, int position) is always called when recycling the view (new data is brought in and try to display with that ViewHolder).
So when onBindViewHolder is called it needs to be put in the right view layout and update the ViewHolder.
public int getItemViewType(int position) {
TypedData data = mDataSource.get(position);
return data.type;
}
public ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
return ViewHolder.makeViewHolder(parent, viewType);
}
public void onBindViewHolder(ViewHolder holder,
int position) {
TypedData data = mDataSource.get(position);
holder.updateData(data);
}
///
public static class ViewHolder extends
RecyclerView.ViewHolder {
ViewGroup mParentViewGroup;
View mCurrentViewThisViewHolderIsFor;
int mDataType;
public TypeOneViewHolder mTypeOneViewHolder;
public TypeTwoViewHolder mTypeTwoViewHolder;
static ViewHolder makeViewHolder(ViewGroup vwGrp,
int dataType) {
View v = getLayoutView(vwGrp, dataType);
return new ViewHolder(vwGrp, v, viewType);
}
static View getLayoutView(ViewGroup vwGrp,
int dataType) {
int layoutId = getLayoutId(dataType);
return LayoutInflater.from(vwGrp.getContext())
.inflate(layoutId, null);
}
static int getLayoutId(int dataType) {
if (dataType == TYPE_ONE) {
return R.layout.type_one_layout;
} else if (dataType == TYPE_TWO) {
return R.layout.type_two_layout;
}
}
public ViewHolder(ViewGroup vwGrp, View v,
int dataType) {
super(v);
mDataType = dataType;
mParentViewGroup = vwGrp;
mCurrentViewThisViewHolderIsFor = v;
if (data.type == TYPE_ONE) {
mTypeOneViewHolder = new TypeOneViewHolder(v);
} else if (data.type == TYPE_TWO) {
mTypeTwoViewHolder = new TypeTwoViewHolder(v);
}
}
public void updateData(TypeData data) {
mDataType = data.type;
if (data.type == TYPE_ONE) {
mTypeTwoViewHolder = null;
if (mTypeOneViewHolder == null) {
View newView = getLayoutView(mParentViewGroup,
data.type);
/**
* How can I replace a new view with
the view in the parent
view container?
*/
replaceView(mCurrentViewThisViewHolderIsFor,
newView);
mCurrentViewThisViewHolderIsFor = newView;
mTypeOneViewHolder =
new TypeOneViewHolder(newView);
}
mTypeOneViewHolder.updateDataTypeOne(data);
} else if (data.type == TYPE_TWO){
mTypeOneViewHolder = null;
if (mTypeTwoViewHolder == null) {
View newView = getLayoutView(mParentViewGroup,
data.type);
/**
* How can I replace a new view with
the view in the parent view
container?
*/
replaceView(mCurrentViewThisViewHolderIsFor,
newView);
mCurrentViewThisViewHolderIsFor = newView;
mTypeTwoViewHolder =
new TypeTwoViewHolder(newView);
}
mTypeTwoViewHolder.updateDataTypeOne(data);
}
}
}
public static void replaceView(View currentView,
View newView) {
ViewGroup parent = (ViewGroup)currentView.getParent();
if(parent == null) {
return;
}
final int index = parent.indexOfChild(currentView);
parent.removeView(currentView);
parent.addView(newView, index);
}
ViewHolder has member mItemViewType to hold the view.
It looks like in onBindViewHolder(ViewHolder holder, int position) the ViewHolder passed in has been picked up (or created) by looked at getItemViewType(int position) to make sure it is a match, so it may not need to worry there that ViewHolder's type does not match the data[position]'s type.
It looks like The recycle ViewHolder is picked by type, so no warrior there.
Building a RecyclerView LayoutManager – Part 1 answers this question.
It gets the recycle ViewHolder like:
holder = getRecycledViewPool().getRecycledView(mAdapter.getItemViewType(offsetPosition));
Or create a new one if not find recycle ViewHolder of the right type.
public ViewHolder getRecycledView(int viewType) {
final ArrayList<ViewHolder> scrapHeap = mScrap.get(viewType);
if (scrapHeap != null && !scrapHeap.isEmpty()) {
final int index = scrapHeap.size() - 1;
final ViewHolder scrap = scrapHeap.get(index);
scrapHeap.remove(index);
return scrap;
}
return null;
}
View getViewForPosition(int position, boolean dryRun) {
......
if (holder == null) {
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
+ "position " + position + "(offset:" + offsetPosition + ")."
+ "state:" + mState.getItemCount());
}
final int type = mAdapter.getItemViewType(offsetPosition);
// 2) Find from scrap via stable ids, if exists
if (mAdapter.hasStableIds()) {
holder = getScrapViewForId(mAdapter.getItemId(offsetPosition), type, dryRun);
if (holder != null) {
// update position
holder.mPosition = offsetPosition;
fromScrap = true;
}
}
if (holder == null && mViewCacheExtension != null) {
// We are NOT sending the offsetPosition because LayoutManager does not
// know it.
final View view = mViewCacheExtension
.getViewForPositionAndType(this, position, type);
if (view != null) {
holder = getChildViewHolder(view);
if (holder == null) {
throw new IllegalArgumentException("getViewForPositionAndType returned"
+ " a view which does not have a ViewHolder");
} else if (holder.shouldIgnore()) {
throw new IllegalArgumentException("getViewForPositionAndType returned"
+ " a view that is ignored. You must call stopIgnoring before"
+ " returning this view.");
}
}
}
if (holder == null) { // fallback to recycler
// try recycler.
// Head to the shared pool.
if (DEBUG) {
Log.d(TAG, "getViewForPosition(" + position + ") fetching from shared "
+ "pool");
}
holder = getRecycledViewPool()
.getRecycledView(mAdapter.getItemViewType(offsetPosition));
if (holder != null) {
holder.resetInternal();
if (FORCE_INVALIDATE_DISPLAY_LIST) {
invalidateDisplayListInt(holder);
}
}
}
if (holder == null) {
holder = mAdapter.createViewHolder(RecyclerView.this,
mAdapter.getItemViewType(offsetPosition));
if (DEBUG) {
Log.d(TAG, "getViewForPosition created new ViewHolder");
}
}
}
boolean bound = false;
if (mState.isPreLayout() && holder.isBound()) {
// do not update unless we absolutely have to.
holder.mPreLayoutPosition = position;
} else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
if (DEBUG && holder.isRemoved()) {
throw new IllegalStateException("Removed holder should be bound and it should"
+ " come here only in pre-layout. Holder: " + holder);
}
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
mAdapter.bindViewHolder(holder, offsetPosition);
attachAccessibilityDelegate(holder.itemView);
bound = true;
if (mState.isPreLayout()) {
holder.mPreLayoutPosition = position;
}
}
final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
final LayoutParams rvLayoutParams;
if (lp == null) {
rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
holder.itemView.setLayoutParams(rvLayoutParams);
} else if (!checkLayoutParams(lp)) {
rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
holder.itemView.setLayoutParams(rvLayoutParams);
} else {
rvLayoutParams = (LayoutParams) lp;
}
rvLayoutParams.mViewHolder = holder;
rvLayoutParams.mPendingInvalidate = fromScrap && bound;
return holder.itemView;
}
Although the selected answer is correct, I just want to further elaborate it. I found a useful Custom Adapter for multiple View Types in RecyclerView.
Its Kotlin version is here.
The custom adapter is the following:
public class CustomAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final Context context;
ArrayList<String> list; // ArrayList of your Data Model
final int VIEW_TYPE_ONE = 1;
final int VIEW_TYPE_TWO = 2;
public CustomAdapter(Context context, ArrayList<String> list) { // you can pass other parameters in constructor
this.context = context;
this.list = list;
}
private class ViewHolder1 extends RecyclerView.ViewHolder {
TextView yourView;
ViewHolder1(final View itemView) {
super(itemView);
yourView = itemView.findViewById(R.id.yourView); // Initialize your All views prensent in list items
}
void bind(int position) {
// This method will be called anytime a list item is created or update its data
// Do your stuff here
yourView.setText(list.get(position));
}
}
private class ViewHolder2 extends RecyclerView.ViewHolder {
TextView yourView;
ViewHolder2(final View itemView) {
super(itemView);
yourView = itemView.findViewById(R.id.yourView); // Initialize your All views prensent in list items
}
void bind(int position) {
// This method will be called anytime a list item is created or update its data
//Do your stuff here
yourView.setText(list.get(position));
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ONE) {
return new ViewHolder1(LayoutInflater.from(context).inflate(R.layout.your_list_item_1, parent, false));
}
//if its not VIEW_TYPE_ONE then its VIEW_TYPE_TWO
return new ViewHolder2(LayoutInflater.from(context).inflate(R.layout.your_list_item_2, parent, false));
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (list.get(position).type == Something) { // Put your condition, according to your requirements
((ViewHolder1) holder).bind(position);
} else {
((ViewHolder2) holder).bind(position);
}
}
#Override
public int getItemCount() {
return list.size();
}
#Override
public int getItemViewType(int position) {
// Here you can get decide from your model's ArrayList, which type of view you need to load. Like
if (list.get(position).type == Something) { // Put your condition, according to your requirements
return VIEW_TYPE_ONE;
}
return VIEW_TYPE_TWO;
}
}
I have a better solution which allows to create multiple view types in a declarative and type safe way. It’s written in Kotlin which, by the way, is really nice.
Simple view holders for all required view types
class ViewHolderMedium(itemView: View) : RecyclerView.ViewHolder(itemView) {
val icon: ImageView = itemView.findViewById(R.id.icon) as ImageView
val label: TextView = itemView.findViewById(R.id.label) as TextView
}
There is an abstraction of adapter data item. Note that a view type is represented by a hashCode of particular view holder class (KClass in Kotlin)
trait AdapterItem {
val viewType: Int
fun bindViewHolder(viewHolder: RecyclerView.ViewHolder)
}
abstract class AdapterItemBase<T>(val viewHolderClass: KClass<T>) : AdapterItem {
override val viewType: Int = viewHolderClass.hashCode()
abstract fun bindViewHolder(viewHolder: T)
override fun bindViewHolder(viewHolder: RecyclerView.ViewHolder) {
bindViewHolder(viewHolder as T)
}
}
Only bindViewHolder needs to be overridden in concrete adapter item classes (type safe way).
class AdapterItemMedium(val icon: Drawable, val label: String, val onClick: () -> Unit) : AdapterItemBase<ViewHolderMedium>(ViewHolderMedium::class) {
override fun bindViewHolder(viewHolder: ViewHolderMedium) {
viewHolder.icon.setImageDrawable(icon)
viewHolder.label.setText(label)
viewHolder.itemView.setOnClickListener { onClick() }
}
}
List of such AdapterItemMedium objects is a data source for the adapter which actually accepts List<AdapterItem>. See below.
The important part of this solution is a view holder factory which will provide fresh instances of a specific ViewHolder:
class ViewHolderProvider {
private val viewHolderFactories = hashMapOf<Int, Pair<Int, Any>>()
fun provideViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val (layoutId: Int, f: Any) = viewHolderFactories.get(viewType)
val viewHolderFactory = f as (View) -> RecyclerView.ViewHolder
val view = LayoutInflater.from(viewGroup.getContext()).inflate(layoutId, viewGroup, false)
return viewHolderFactory(view)
}
fun registerViewHolderFactory<T>(key: KClass<T>, layoutId: Int, viewHolderFactory: (View) -> T) {
viewHolderFactories.put(key.hashCode(), Pair(layoutId, viewHolderFactory))
}
}
And the simple adapter class looks like this:
public class MultitypeAdapter(val items: List<AdapterItem>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val viewHolderProvider = ViewHolderProvider() // inject ex Dagger2
init {
viewHolderProvider!!.registerViewHolderFactory(ViewHolderMedium::class, R.layout.item_medium, { itemView ->
ViewHolderMedium(itemView)
})
}
override fun getItemViewType(position: Int): Int {
return items[position].viewType
}
override fun getItemCount(): Int {
return items.size()
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder? {
return viewHolderProvider!!.provideViewHolder(viewGroup, viewType)
}
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
items[position].bindViewHolder(viewHolder)
}
}
There are only three steps to create a new view type:
create a view holder class
create an adapter item class (extending from AdapterItemBase)
register the view holder class in ViewHolderProvider
Here is an example of this concept: android-drawer-template.
It goes even further - a view type which acts as a spinner component, with selectable adapter items.
It is very simple and straightforward.
Just override the getItemViewType() method in your adapter. On the basis of data return different itemViewType values. E.g., consider an object of type Person with a member isMale, if isMale is true, return 1 and isMale is false, return 2 in the getItemViewType() method.
Now coming to the createViewHolder (ViewGroup parent, int viewType), on the basis of different viewType yon can inflate the different layout file. Like the following:
if (viewType == 1){
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.male, parent, false);
return new AdapterMaleViewHolder(view);
}
else{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.female, parent, false);
return new AdapterFemaleViewHolder(view);
}
in onBindViewHolder (VH holder,int position) check where holder is an instance of AdapterFemaleViewHolder or AdapterMaleViewHolder by instanceof and accordingly assign the values.
ViewHolder may be like this
class AdapterMaleViewHolder extends RecyclerView.ViewHolder {
...
public AdapterMaleViewHolder(View itemView){
...
}
}
class AdapterFemaleViewHolder extends RecyclerView.ViewHolder {
...
public AdapterFemaleViewHolder(View itemView){
...
}
}
I recommend this library from Hannes Dorfmann. It encapsulates all the logic related to a particular view type in a separate object called "AdapterDelegate".
https://github.com/sockeqwe/AdapterDelegates
public class CatAdapterDelegate extends AdapterDelegate<List<Animal>> {
private LayoutInflater inflater;
public CatAdapterDelegate(Activity activity) {
inflater = activity.getLayoutInflater();
}
#Override public boolean isForViewType(#NonNull List<Animal> items, int position) {
return items.get(position) instanceof Cat;
}
#NonNull #Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
return new CatViewHolder(inflater.inflate(R.layout.item_cat, parent, false));
}
#Override public void onBindViewHolder(#NonNull List<Animal> items, int position,
#NonNull RecyclerView.ViewHolder holder, #Nullable List<Object> payloads) {
CatViewHolder vh = (CatViewHolder) holder;
Cat cat = (Cat) items.get(position);
vh.name.setText(cat.getName());
}
static class CatViewHolder extends RecyclerView.ViewHolder {
public TextView name;
public CatViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.name);
}
}
}
public class AnimalAdapter extends ListDelegationAdapter<List<Animal>> {
public AnimalAdapter(Activity activity, List<Animal> items) {
// DelegatesManager is a protected Field in ListDelegationAdapter
delegatesManager.addDelegate(new CatAdapterDelegate(activity))
.addDelegate(new DogAdapterDelegate(activity))
.addDelegate(new GeckoAdapterDelegate(activity))
.addDelegate(23, new SnakeAdapterDelegate(activity));
// Set the items from super class.
setItems(items);
}
}
I firstly recommend you to read Hannes Dorfmann's great article about this topic.
When a new view type comes, you have to edit your adapter and you have to handle so many messy things. Your adapter should be open for extension, but closed for modification.
You may check this two project, they can give the idea about how to handle different ViewTypes in Adapter:
https://github.com/sockeqwe/AdapterDelegates
https://github.com/ibrahimyilmaz/kiel
If anyone is interested to see the super simple solution written in Kotlin, check the blogpost I just created. The example in the blogpost is based on creating Sectioned RecyclerView:
https://brona.blog/2020/06/sectioned-recyclerview-in-three-steps/
Actually, I'd like to improve on Anton's answer.
Since getItemViewType(int position) returns an integer value, you can return the layout resource ID you'd need to inflate. That way you'd save some logic in onCreateViewHolder(ViewGroup parent, int viewType) method.
Also, I wouldn't suggest doing intensive calculations in getItemCount() as that particular function is called at least 5 times while rendering the list, as well as while rendering each item beyond the visible items. Sadly since notifyDatasetChanged() method is final, you can't really override it, but you can call it from another function within the adapter.
You can use the library: https://github.com/vivchar/RendererRecyclerViewAdapter
mRecyclerViewAdapter = new RendererRecyclerViewAdapter(); /* Included from library */
mRecyclerViewAdapter.registerRenderer(new SomeViewRenderer(SomeModel.TYPE, this));
mRecyclerViewAdapter.registerRenderer(...); /* You can use several types of cells */
For each item, you should to implement a ViewRenderer, ViewHolder, SomeModel:
ViewHolder - it is a simple view holder of recycler view.
SomeModel - it is your model with ItemModel interface
public class SomeViewRenderer extends ViewRenderer<SomeModel, SomeViewHolder> {
public SomeViewRenderer(final int type, final Context context) {
super(type, context);
}
#Override
public void bindView(#NonNull final SomeModel model, #NonNull final SomeViewHolder holder) {
holder.mTitle.setText(model.getTitle());
}
#NonNull
#Override
public SomeViewHolder createViewHolder(#Nullable final ViewGroup parent) {
return new SomeViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.some_item, parent, false));
}
}
For more details you can look at the documentation.
View types implementation becomes easier with Kotlin. Here is a sample with this light library https://github.com/Link184/KidAdapter
recyclerView.setUp {
withViewType {
withLayoutResId(R.layout.item_int)
withItems(mutableListOf(1, 2, 3, 4, 5, 6))
bind<Int> { // this - is adapter view hoder itemView, it - current item
intName.text = it.toString()
}
}
withViewType("SECOND_STRING_TAG") {
withLayoutResId(R.layout.item_text)
withItems(mutableListOf("eight", "nine", "ten", "eleven", "twelve"))
bind<String> {
stringName.text = it
}
}
}
You can deal with multipleViewTypes RecyclerAdapter by making getItemViewType() return the expected viewType value for that position.
I prepared an MultipleViewTypeAdapter for constructing an MCQ list for examinations which may throw a question that may have two or more valid answers (checkbox options) and a single answer questions (radiobutton options).
For this I get the type of question from the API response and I used that for deciding which view I have to show for that question.
public class MultiViewTypeAdapter extends RecyclerView.Adapter {
Context mContext;
ArrayList<Question> dataSet;
ArrayList<String> questions;
private Object radiobuttontype1;
//Viewholder to display Questions with checkboxes
public static class Checkboxtype2 extends RecyclerView.ViewHolder {
ImageView imgclockcheck;
CheckBox checkbox;
public Checkboxtype2(#NonNull View itemView) {
super(itemView);
imgclockcheck = (ImageView) itemView.findViewById(R.id.clockout_cbox_image);
checkbox = (CheckBox) itemView.findViewById(R.id.clockout_cbox);
}
}
//Viewholder to display Questions with radiobuttons
public static class Radiobuttontype1 extends RecyclerView.ViewHolder {
ImageView clockout_imageradiobutton;
RadioButton clockout_radiobutton;
TextView sample;
public radiobuttontype1(View itemView) {
super(itemView);
clockout_imageradiobutton = (ImageView) itemView.findViewById(R.id.clockout_imageradiobutton);
clockout_radiobutton = (RadioButton) itemView.findViewById(R.id.clockout_radiobutton);
sample = (TextView) itemView.findViewById(R.id.sample);
}
}
public MultiViewTypeAdapter(ArrayList<QueDatum> data, Context context) {
this.dataSet = data;
this.mContext = context;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int viewType) {
if (viewType.equalsIgnoreCase("1")) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_radio_list_row, viewGroup, false);
return new radiobuttontype1(view);
} else if (viewType.equalsIgnoreCase("2")) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_cbox_list_row, viewGroup, false);
view.setHorizontalFadingEdgeEnabled(true);
return new Checkboxtype2(view);
} else if (viewType.equalsIgnoreCase("3")) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_radio_list_row, viewGroup, false);
return new Radiobuttontype1(view);
} else if (viewType.equalsIgnoreCase("4")) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_radio_list_row, viewGroup, false);
return new Radiobuttontype1(view);
} else if (viewType.equalsIgnoreCase("5")) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.clockout_radio_list_row, viewGroup, false);
return new Radiobuttontype1(view);
}
return null;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder viewHolder, int viewType) {
if (viewType.equalsIgnoreCase("1")) {
options = dataSet.get(i).getOptions();
question = dataSet.get(i).getQuestion();
image = options.get(i).getValue();
((radiobuttontype1) viewHolder).clockout_radiobutton.setChecked(false);
((radiobuttontype1) viewHolder).sample.setText(question);
//Loading image bitmap in the ViewHolder's View
Picasso.with(mContext)
.load(image)
.into(((radiobuttontype1) viewHolder).clockout_imageradiobutton);
} else if (viewType.equalsIgnoreCase("2")) {
options = (ArrayList<Clockout_questions_Option>) dataSet.get(i).getOptions();
question = dataSet.get(i).getQuestion();
image = options.get(i).getValue();
//Loading image bitmap in the ViewHolder's View
Picasso.with(mContext)
.load(image)
.into(((Checkboxtype2) viewHolder).imgclockcheck);
} else if (viewType.equalsIgnoreCase("3")) {
//Fit data to viewHolder for ViewType 3
} else if (viewType.equalsIgnoreCase("4")) {
//Fit data to viewHolder for ViewType 4
} else if (viewType.equalsIgnoreCase("5")) {
//Fit data to viewHolder for ViewType 5
}
}
#Override
public int getItemCount() {
return dataSet.size();
}
/**
* Returns viewType for that position by picking the viewType value from the
* dataset
*/
#Override
public int getItemViewType(int position) {
return dataSet.get(position).getViewType();
}
}
You can avoid multiple conditionals based viewHolder data fillings in onBindViewHolder() by assigning same ids for the similar views across viewHolders which differ in their positioning.
If you want to use it in conjunction with Android Data Binding look into the https://github.com/evant/binding-collection-adapter - it is by far the best solution for the multiple view types RecyclerView I have even seen.
You may use it like
var items: AsyncDiffPagedObservableList<BaseListItem> =
AsyncDiffPagedObservableList(GenericDiff)
val onItemBind: OnItemBind<BaseListItem> =
OnItemBind { itemBinding, _, item -> itemBinding.set(BR.item, item.layoutRes) }
And then in the layout where the list is:
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:enableAnimations="#{false}"
app:scrollToPosition="#{viewModel.scrollPosition}"
app:itemBinding="#{viewModel.onItemBind}"
app:items="#{viewModel.items}"
app:reverseLayoutManager="#{true}"/>
Your list items must implement the BaseListItem interface which looks like this:
interface BaseListItem {
val layoutRes: Int
}
And the item view should look something like this:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="item"
type="...presentation.somescreen.list.YourListItem"/>
</data>
...
</layout>
Where YourListItem implements BaseListItem.
First you must create two layout XML files. After that inside recyclerview adapter TYPE_CALL and TYPE_EMAIL are two static values with 1 and 2 respectively in the adapter class.
Now define two static values ​​at the Recycler view Adapter class level, for example: private static int TYPE_CALL = 1; private static int TYPE_EMAIL = 2;
Now create the view holder with multiple views like this:
class CallViewHolder extends RecyclerView.ViewHolder {
private TextView txtName;
private TextView txtAddress;
CallViewHolder(#NonNull View itemView) {
super(itemView);
txtName = itemView.findViewById(R.id.txtName);
txtAddress = itemView.findViewById(R.id.txtAddress);
}
}
class EmailViewHolder extends RecyclerView.ViewHolder {
private TextView txtName;
private TextView txtAddress;
EmailViewHolder(#NonNull View itemView) {
super(itemView);
txtName = itemView.findViewById(R.id.txtName);
txtAddress = itemView.findViewById(R.id.txtAddress);
}
}
Now code as below in onCreateViewHolder and onBindViewHolder method in the recyclerview adapter:
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int viewType) {
View view;
if (viewType == TYPE_CALL) { // for call layout
view = LayoutInflater.from(context).inflate(R.layout.item_call, viewGroup, false);
return new CallViewHolder(view);
} else { // for email layout
view = LayoutInflater.from(context).inflate(R.layout.item_email, viewGroup, false);
return new EmailViewHolder(view);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder viewHolder, int position) {
if (getItemViewType(position) == TYPE_CALL) {
((CallViewHolder) viewHolder).setCallDetails(employees.get(position));
} else {
((EmailViewHolder) viewHolder).setEmailDetails(employees.get(position));
}
}
I did something like this. I passed "fragmentType" and created two ViewHolders and on basis of this, I classified my Layouts accordingly in a single adapter that can have different Layouts and LayoutManagers
private Context mContext;
protected IOnLoyaltyCardCategoriesItemClicked mListener;
private String fragmentType;
private View view;
public LoyaltyCardsCategoriesRecyclerViewAdapter(Context context, IOnLoyaltyCardCategoriesItemClicked itemListener, String fragmentType) {
this.mContext = context;
this.mListener = itemListener;
this.fragmentType = fragmentType;
}
public class LoyaltyCardCategoriesFragmentViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private ImageView lc_categories_iv;
private TextView lc_categories_name_tv;
private int pos;
public LoyaltyCardCategoriesFragmentViewHolder(View v) {
super(v);
view.setOnClickListener(this);
lc_categories_iv = (ImageView) v.findViewById(R.id.lc_categories_iv);
lc_categories_name_tv = (TextView) v.findViewById(R.id.lc_categories_name_tv);
}
public void setData(int pos) {
this.pos = pos;
lc_categories_iv.setImageResource(R.mipmap.ic_launcher);
lc_categories_name_tv.setText("Loyalty Card Categories");
}
#Override
public void onClick(View view) {
if (mListener != null) {
mListener.onLoyaltyCardCategoriesItemClicked(pos);
}
}
}
public class MyLoyaltyCardsFragmentTagViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public ImageButton lc_categories_btn;
private int pos;
public MyLoyaltyCardsFragmentTagViewHolder(View v) {
super(v);
lc_categories_btn = (ImageButton) v.findViewById(R.id.lc_categories_btn);
lc_categories_btn.setOnClickListener(this);
}
public void setData(int pos) {
this.pos = pos;
lc_categories_btn.setImageResource(R.mipmap.ic_launcher);
}
#Override
public void onClick(View view) {
if (mListener != null) {
mListener.onLoyaltyCardCategoriesItemClicked(pos);
}
}
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (fragmentType.equalsIgnoreCase(Constants.LoyaltyCardCategoriesFragmentTag)) {
view = LayoutInflater.from(mContext).inflate(R.layout.loyalty_cards_categories_frag_item, parent, false);
return new LoyaltyCardCategoriesFragmentViewHolder(view);
} else if (fragmentType.equalsIgnoreCase(Constants.MyLoyaltyCardsFragmentTag)) {
view = LayoutInflater.from(mContext).inflate(R.layout.my_loyalty_cards_categories_frag_item, parent, false);
return new MyLoyaltyCardsFragmentTagViewHolder(view);
} else {
return null;
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
if (fragmentType.equalsIgnoreCase(Constants.LoyaltyCardCategoriesFragmentTag)) {
((LoyaltyCardCategoriesFragmentViewHolder) holder).setData(position);
} else if (fragmentType.equalsIgnoreCase(Constants.MyLoyaltyCardsFragmentTag)) {
((MyLoyaltyCardsFragmentTagViewHolder) holder).setData(position);
}
}
#Override
public int getItemCount() {
return 7;
}
I see there are a lot of great answers, incredibly detailed and extensive. In my case, I always understand things better if I follow along the reasoning from almost scratch, step by step. I would recommend you check this link out and whenever you have similar questions, search for any codelabs that address the issue.
Android Kotlin Fundamentals: Headers in RecyclerView

How to get a View from xml file using LayoutInflater in a java class not extending Activity

I have a java class where I am displaying a dialog using an XML layout file. I want to set the text/contents of the layout dynamically.
To achieve this I am writing a method like this :
private void setContentMessage(String theMessage)
{
View contentView = LayoutInflater.from(context).inflate(R.layout.dialog_content, null, false);
TextView titleMessage = (TextView) contentView.findViewById(R.id.title_message);
titleMessage.setText(theMessage);
}
So here inside the inflate method I am using null because I don't know what to use.
Generally, we use ViewGroup object as the second argument in inflate method but I don't know how to create a ViewGroup inside a java class not extending Activity.
The function that I have written above is making not change inside the dialog layout. So please tell how can I inflate a layout in a java class.
AmitSmartDialog.java
package com.amitupadhyay.touchme.utility;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast;
import com.amitupadhyay.touchme.R;
import com.orhanobut.dialogplus.DialogPlus;
import com.orhanobut.dialogplus.Holder;
import com.orhanobut.dialogplus.OnCancelListener;
import com.orhanobut.dialogplus.OnClickListener;
import com.orhanobut.dialogplus.OnDismissListener;
import com.orhanobut.dialogplus.OnItemClickListener;
import com.orhanobut.dialogplus.ViewHolder;
/**
* Created by aupadhyay on 12/9/16.
*/
public class AmitSmartDialog {
Context context;
public AmitSmartDialog(Context context) {
this.context = context;
}
public void showDialog(int holderId, int gravity, boolean showHeader, boolean showFooter, boolean expanded, String message) {
setContentMessage(message);
Holder holder;
holder = new ViewHolder(R.layout.dialog_content);
OnClickListener clickListener = new OnClickListener() {
#Override
public void onClick(DialogPlus dialog, View view) {
switch (view.getId()) {
case R.id.like_it_button:
Toast.makeText(context, "We're glad that you like it", Toast.LENGTH_LONG).show();
break;
case R.id.love_it_button:
Toast.makeText(context, "We're glad that you love it", Toast.LENGTH_LONG).show();
break;
}
dialog.dismiss();
}
};
OnItemClickListener itemClickListener = new OnItemClickListener() {
#Override
public void onItemClick(DialogPlus dialog, Object item, View view, int position) {
}
};
OnDismissListener dismissListener = new OnDismissListener() {
#Override
public void onDismiss(DialogPlus dialog) {
}
};
OnCancelListener cancelListener = new OnCancelListener() {
#Override
public void onCancel(DialogPlus dialog) {
}
};
showCompleteDialog(holder, gravity, new BaseAdapter() {
#Override
public int getCount() {
return 0;
}
#Override
public Object getItem(int i) {
return null;
}
#Override
public long getItemId(int i) {
return 0;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
return null;
}
}, clickListener, itemClickListener, dismissListener, cancelListener,
expanded);
}
private void showCompleteDialog(Holder holder, int gravity, BaseAdapter adapter,
OnClickListener clickListener, OnItemClickListener itemClickListener,
OnDismissListener dismissListener, OnCancelListener cancelListener,
boolean expanded) {
final DialogPlus dialog = DialogPlus.newDialog(context)
.setContentHolder(holder)
.setHeader(R.layout.dialog_header)
.setCancelable(false)
.setGravity(gravity)
.setAdapter(adapter)
.setOnClickListener(clickListener)
.setOnItemClickListener(new OnItemClickListener() {
#Override public void onItemClick(DialogPlus dialog, Object item, View view, int position) {
}
})
.setOnDismissListener(dismissListener)
.setExpanded(expanded)
.setContentHeight(ViewGroup.LayoutParams.WRAP_CONTENT)
.setOnCancelListener(cancelListener)
.setOverlayBackgroundResource(android.R.color.transparent)
.create();
dialog.show();
}
private void setContentMessage(String theMessage)
{
Toast.makeText(context, "HI BRO", Toast.LENGTH_SHORT).show();
View contentView = LayoutInflater.from(context).inflate(R.layout.dialog_content, null, false);
TextView titleMessage = (TextView) contentView.findViewById(R.id.title_message);
titleMessage.setText(theMessage);
}
}
Use this way :
LayoutInflater inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View contentView = inflater.inflate(R.layout.dialog_content, null,false);
Or
LayoutInflater inflater = LayoutInflater.from(getApplicationContext);
View contentView = inflater.inflate(R.layout.dialog_content, null,false);
UpDate :
Do some Change like this way
setContentMessage(message); pass context as parameter.
so change like this
setContentMessage(message, context);
and change this also.
private void setContentMessage(String theMessage,Context context)
{
View contentView = contxet.getLayoutInflater().inflate(R.layout.dialog_content, null);
TextView titleMessage = (TextView) contentView.findViewById(R.id.title_message);
titleMessage.setText(theMessage);
}
Pass context in an argument of that method then use this
"(LayoutInflater)
mActivity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);"
private void setContentMessage(String theMessage, Activity mActivity)
{
LayoutInflater mInflater = (LayoutInflater)
mActivity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
View contentView = mInflater.inflate(R.layout.dialog_content, null, false);
TextView titleMessage = (TextView) contentView.findViewById(R.id.title_message);
titleMessage.setText(theMessage);
}
From what I'm understanding from your code, you are creating a dialog with a custom view, and then you want to set a message to some textview inside that dialog.
I see that you also use a holder, so instead of calling setContentMessage(message), which is redundant, you could something like this
Holder holder;
holder = new ViewHolder(R.layout.dialog_content);
holder.titleMessage = message
Inside the Holder implementation you should have a field that points to the title textView (that's how a holder pattern should be anyway).

Unable to resolve "startActivity()" when trying to use it from RecyclerView's onClickListener()

I am new to android programming and trying to implement the RecyclerView. Please forgive me if the question is redundant but I could not find my answer anywhere else. So, I'm finally posting my question.
import android.content.Context;
import android.content.Intent;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
public class QueriesAdapter extends RecyclerView.Adapter<QueriesAdapter.QueryViewHolder> {
private List<Query> list;
private Context mContext;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public class QueryViewHolder extends RecyclerView.ViewHolder{
// each data item is just a string in this case
protected TextView questionTextView;
public QueryViewHolder (View view)
{
super(view);
this.questionTextView = (TextView) view.findViewById(R.id.list_item_question);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public QueriesAdapter(Context context, List<Query> myDataset) {
mContext = context;
list = myDataset;
}
// Create new views (invoked by the layout manager)
#Override
public QueriesAdapter.QueryViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item, parent, false);
// set the view's size, margins, paddings and layout parameters
QueryViewHolder vh = new QueryViewHolder(v);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(QueryViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
holder.questionTextView.setText(list.get(position).mQuery);
holder.questionTextView.setTag(holder);
}
View.OnClickListener clickListener = new View.OnClickListener(){
#Override
public void onClick(View v) {
QueryViewHolder viewHolder = (QueryViewHolder)v.getTag();
int position = viewHolder.getPosition();
Query q = list.get(position);
Intent i = new Intent(mContext, Answer.class);
i.putExtra("QueryId", q.id);
i.putExtra("question", q.mQuery);
i.putExtra("answer", q.mAnswer);
startActivity(i);
//This line is showing error that it cannot resolve startActivity() function, I think that I did not pass the context properly.
//Anyway, help me on this.
}
};
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return list.size();
}
}
Now, this is a part of my MainActivity which I used to instantiate the QueriesAdapter Class(a Custom Adapter)
mAdapter = new QueriesAdapter(this, list);
You must use mContext to start a method which is default in the Activity try with mContext.startActivity(i)
You need to implement in the following way. Define a click listener in RecyclerView adapter class so that event can be consumed in corresponding Activity or Fragment. This helps making the events to handle easier
public class ListViewNotifyAdapter extends RecyclerView.Adapter <...>{
//defining a click listner
private static MyClickListener myClickListener;
//usual methods and variables goes here
public void setOnItemClickListener(MyClickListener myClickListener) {
this.myClickListener = myClickListener;
}
//interface for click
public interface MyClickListener {
public void onItemClick(int position, View v);
}
//view holder
public static class NotificationObjectHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
TextView txtEventAlias;
TextView txtTime;
TextView txtLogMessage;
public NotificationObjectHolder(View viewItem){
super(viewItem);
txtEventAlias=(TextView)viewItem.findViewById(R.id.txtEventAlias);
txtTime=(TextView)viewItem.findViewById(R.id.txtTime);
txtLogMessage=(TextView)viewItem.findViewById(R.id.txtLogMessage);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
myClickListener.onItemClick(getPosition(), v);
}
}
}
Now in the class which you have implemented the RecyclerView do like the following
#Override
public void onResume() {
super.onResume();
((ListViewNotifyAdapter) mAdapter).setOnItemClickListener(
new ListViewNotifyAdapter.MyClickListener() {
#Override
public void onItemClick(int position, View v) {
showAlert(position);
}
});
}
void showAlert(int position){
//action for the item click
}
Use mContext.startActivity(i);.

Categories

Resources