This question already has answers here:
How to create RecyclerView with multiple view types
(23 answers)
Closed 3 years ago.
I am making an app in which I want to put texts , images and videos all in separate ViewHolders (similar to Instagram and Facebook). I know that we have to use a Recycler View with multiple ViewTypes . I am using firebase as my storage . How do I retrieve the items from firebase and place them in the appropriate ViewHolder . Please help me.
public class GovtjobsAdapter extends RecyclerView.Adapter {
private List<GovtjobBean> govtjobList;
public class MyViewHolder extends RecyclerView.ViewHolder {
public TextView tv_govtjob_title,tv_govtjob_lastdatetoapply,tv_govtjob_location;
public MyViewHolder(View view) {
super(view);
tv_govtjob_title = (TextView) view.findViewById(R.id.tv_govtjobs_title);
tv_govtjob_lastdatetoapply = (TextView) view.findViewById(R.id.tv_govtjob_lastdatetoapply);
tv_govtjob_location = (TextView) view.findViewById(R.id.tv_govtjob_location);
}
}
public GovtjobsAdapter(List<GovtjobBean> govtjobList) {
this.govtjobList = govtjobList;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.govtjob_lists, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
GovtjobBean govtjoblist = govtjobList.get(position);
holder.tv_govtjob_title.setText(govtjoblist.getGovt_title());
holder.tv_govtjob_lastdatetoapply.setText(govtjoblist.getGovt_lastdatetoapply());
holder.tv_govtjob_location.setText(govtjoblist.getGovt_location());
}
#Override
public int getItemCount() {
return govtjobList.size();
}
}
You can use the getItemViewType(int position) method of your adapter to do this. In this method, you can check the type of item at the current position if it is an Image, Video or Text and return a specific value for each. Then in your onCreateViewHolder(ViewGroup parent, Int viewType) you can use that viewType parameter to inflate your views.
You will need to create a custom recycler view class where you override onCreateViewHolder(ViewGroup parent, Int viewType).
Here is my example:
(1) in your recyclerview class create custom view holder classes. Example:
class ViewHolderCurrent extends RecyclerView.ViewHolder {
TextView locationTV;
TextView conditionTV;
...
public ViewHolderCurrent(View listItemView) {
super(listItemView);
locationTV = listItemView.findViewById(R.id.location_main_4);
conditionTV = listItemView.findViewById(R.id.condition_main_4);
...
}
}
(2) override the onCreateViewHolder to your custom view holders:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case 1:
View viewCurrent = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item4, parent, false);
return new ViewHolderCurrent(viewCurrent);
case 2:
...
case 3:
...
}
return null;
}
(3)Override the onBindViewHolder() to populate the viewholder of your choice. Example:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case 1:
ViewHolderCurrent viewHolderCurrent = (ViewHolderCurrent) holder;
viewHolderCurrent.locationTV.setText(*text*);
viewHolderCurrent.conditionTV.setText(*text*);
break;
case 2:
...
}
}
My full source code is here
You have to use recyclerview ViewTypes, check the example in this link:
Android RecyclerView Example – Multiple ViewTypes
Or you can use library like FastAdapter, it is awesome library that handle adapter items type:
https://github.com/mikepenz/FastAdapter
Related
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate
(R.layout.tfl_conversation_item, parent, false);
conversations.get(getBindingAdapterPosition(view));
getAbsoluteAdapterPosition() ;
getchildAdapterPosition(view);
return new ConversationAdapter.ViewHolder(view);
}
I used the above code, but didn't succeed. I want to get the adapter position in onCreate of RecyclerView.
You need to use viewType, if you want the value of viewType to be equal to your position, use the following code:
#Override
public int getItemViewType(int position) {
return position;
}
This is the solution, but the work of viewType is something else, learn more.
I have to develop a Recyclerview with PopulateViewHolder in an older version of Firebase. Now I'm trying to upgrade the version (not at the last one) and I don't know how to use onBindHolder or anything about this new kind of RecyclerView.
Here is how I had the setup:
firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Event, EventViewHolder>(
Event.class,
R.layout.event_row,
EventViewHolder.class,
mDatabase.orderByChild("timeStamp")
) {
#Override
protected void populateViewHolder(final EventViewHolder viewHolder, final Event model, final int position) {
final String id = getRef(position).getKey().toString().trim();
viewHolder.setTitle(model.getTitle());
viewHolder.setDateWords(model.getDateWords());
viewHolder.setDesc(model.getDesc());
}
};
mEventList.setAdapter(firebaseRecyclerAdapter);
}
public static class EventViewHolder extends RecyclerView.ViewHolder {
View mView;
public EventViewHolder(View itemView) {
super(itemView);
mView = itemView;
}
}
And here it's how I'm trying to do now:
Query searchQuery = mDatabase.orderByChild("timeStamp");
options = new FirebaseRecyclerOptions.Builder<Event>()
.setQuery(searchQuery, Event.class)
.build();
firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Event, EventViewHolder>(options) {
#Override
protected void onBindViewHolder(final EventViewHolder viewHolder, int position, Event model) {
final String id = getRef(position).getKey().toString().trim();
viewHolder.setTitle(model.getTitle());
viewHolder.setDateWords(model.getDateWords());
viewHolder.setDesc(model.getDesc());
}
#NonNull
#Override
public EventViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return null;
}
};
firebaseRecyclerAdapter.startListening();
mEventList.setAdapter(firebaseRecyclerAdapter);
The ViewHolder Class is the same.
Here is the error
Actually, I'm asking for tutorial, because I only find those that make the adapter separately, but I wanna have it in the same activity.
In your public EventViewHolder onCreateViewHolder method, instead of returning a view you are returning null. To solve this, please add the following lines of code inside this method:
#NonNull
#Override
public EventViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false);
return new EventViewHolder(view);
}
In which item_layout is a custom layout for your item. If you want to use instead of this layout a built-in XML layout document that is part of the Android OS, you can use:
android.R.layout.simple_list_item_1
I followed this tutorial by Google Developer's YouTube channel to implement AdMob native express ads.
I got the following error:
required: packagename.adapter.viewHolder
found : packagename.adapter.NativeExpressAdViewHolder
Here is how my onCreateViewHolder look like:
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case AD_VIEW_TYPE:
View nativeExpressLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.native_express_add_container, parent, false);
return new NativeExpressAdViewHolder(nativeExpressLayoutView);
case MENU_ITEM_VIEW_TYPE:
default:
View myLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list, parent, false);
return new ViewHolder(myLayoutView);
}
}
and here is my 2 different ViewHolder classes:
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
ViewHolder(View itemView) {
super(itemView);
}
}
public class NativeExpressAdViewHolder extends RecyclerView.ViewHolder {
NativeExpressAdViewHolder(View view) {
super(view);
}
}
Here is simular questions with no answers:
Error "Incompatible types" while adding NativeAds in recyclerView
How to add NativeExpressAdView with Custom Model List Android?
EDIT:
Here is my full adapter as requested:
public class MainActivityVideoAdapter extends Adapter<MainActivityVideoAdapter.ViewHolder> {
ArrayList<Bitmap> bitmapArrayList;
Context context;
LayoutInflater layoutInflater;
View myLayoutView;
ArrayList<PathModel> ThumbPathList;
ArrayList<PathModel> VideoPathList = new ArrayList();
static DBManager manager;
long _id;
private static final int MENU_ITEM_VIEW_TYPE = 0;
private static final int AD_VIEW_TYPE = 1;
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
//Video Title
TextView videoName;
//Video Image
CircularImageView videoThumb;
//PopupMenu
ImageButton viewholderOtions;
ViewHolder(View itemView) {
super(itemView);
viewholderOtions = (ImageButton) myLayoutView.findViewById(R.id.viewholderOptions);
videoName = (TextView) myLayoutView.findViewById(R.id.FilePath);
videoThumb = (CircularImageView) myLayoutView.findViewById(R.id.VideoThumbnail);
//View onClick
itemView.setOnClickListener(this);
//Popup onClick
viewholderOtions.setOnClickListener(this);
}
//Handling click events
#Override
public void onClick(View v) {
if (v == viewholderOtions) {
int position = (int) v.getTag();
showPopupMenu(viewholderOtions, position);
}
}
}
public class NativeExpressAdViewHolder extends RecyclerView.ViewHolder {
NativeExpressAdViewHolder(View view) {
super(view);
}
}
public MainActivityVideoAdapter(Context context, ArrayList<PathModel> ThumbPathList, ArrayList<PathModel> VideoPathList) {
this.context = context;
this.ThumbPathList = ThumbPathList;
this.VideoPathList = VideoPathList;
}
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case AD_VIEW_TYPE:
View nativeExpressLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.native_express_add_container, parent, false);
return new NativeExpressAdViewHolder(nativeExpressLayoutView);
case MENU_ITEM_VIEW_TYPE:
default:
View myLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list, parent, false);
return new ViewHolder(myLayoutView);
}
}
public void onBindViewHolder(final ViewHolder myHolder, final int position) {
int viewType = getItemViewType(position);
switch (viewType) {
case AD_VIEW_TYPE:
case MENU_ITEM_VIEW_TYPE:
default:
final PathModel videoPathModel = this.VideoPathList.get(position);
PathModel thumbathModel = this.ThumbPathList.get(position);
File file = new File(videoPathModel.getPath());
String filename = file.getName();
myHolder.videoName.setText(filename);
myHolder.videoThumb.setImageURI(Uri.parse(thumbathModel.getPath()));
myHolder.viewholderOtions.setTag(position);
myHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent= new Intent(context, VideoPlayerActivity.class);
intent.putExtra("fromFA2", "fromFA2");
context.startActivity(intent);
}
});
}
}
Hello #ClassA i found that you have been importing local class rather than RecyclerView.ViewHolder in onBindViewHolder()
Please go through below code, this may help you.
public class MainActivityVideoAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
/*
------------------
your constructor goes here
-----------------
*/
#Override
public int getItemCount() {
return 0;
}
public void onBindViewHolder(final RecyclerView.ViewHolder myHolder, final int position) {
int viewType = getItemViewType(position);
switch (viewType) {
case AD_VIEW_TYPE:
break;
case MENU_ITEM_VIEW_TYPE:
break;
}
}
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case AD_VIEW_TYPE:
View nativeExpressLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.native_express_add_container, parent, false);
return new NativeExpressAdViewHolder(nativeExpressLayoutView);
case MENU_ITEM_VIEW_TYPE:
View myLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list, parent, false);
return new ViewHolder(myLayoutView);
}
}
class ViewHolder extends RecyclerView.ViewHolder {
ViewHolder(View itemView) {
super(itemView);
}
}
public class NativeExpressAdViewHolder extends RecyclerView.ViewHolder {
NativeExpressAdViewHolder(View view) {
super(view);
}
}
}
This code doesn't contain your variables and logic, This code is for perfect import and use of methods.
If this resolve your problem, please make this answer approved.
Happy Coding.
I have my doubts that calling the class ViewHolder messed you up. Call it rather MenuViewHolder or something, cause now when writing simply ViewHolder in the method like this:
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
It assumes that you're thinking of your OWN class ViewHolder, rather than the RecyclerView.ViewHolder and then the types are incompatible. You can fix it by either changing the class name to something else or by changing the method declaration to:
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Although the latter would solve it as well, I'd opt for name changing, cause it's not a good practice to have classes with same names as native ones (e.g. View, ViewHolder, BaseAdapter etc.) cause it might produce confusion as it did here.
use RecyclerView's ViewHolder, not your ViewHolder.
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case AD_VIEW_TYPE:
View nativeExpressLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.native_express_add_container, parent, false);
return new NativeExpressAdViewHolder(nativeExpressLayoutView);
case MENU_ITEM_VIEW_TYPE:
default:
View myLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list, parent, false);
return new ViewHolder(myLayoutView);
}
}
I am using the following code in Adapter class to show data in a RecyclerView, but now I would like to show data from some other ArrayList in a same RecyclerView (at some positions like: 1st position and 6th position) using different layout.
That different layout (assume: another_layout.xml) contains 2 TextViews and an Image, also want to implement click on listener for that layout too..
#Override
public PlaylistCardAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// inflate a card layout
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.youtube_video_card, parent, false);
// populate the viewholder
ViewHolder vh = new ViewHolder(v);
return vh;
}
RecyclerView for more than one layout
1. Override getItemViewType(int position) method
e.g I have two layouts layout1 and layout2.I want layout1 at the top and then layout2. So getItemViewType would be something like this
#Override
public int getItemViewType(int position) {
if(position == 0){ //for layout1
return 1;
}else{
return 2; //for layout2
}
}
2. Different viewholder for each layout like this
class ViewHolder_LayoutOne extends RecyclerView.ViewHolder{
TextView name;
//Constructor
}
class ViewHolder_LayoutTwo extends RecyclerView.ViewHolder{
TextView name;
//Constructor
}
3. Inflate different layouts according to the position
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.ViewHolder vh = null;
if(viewType == 1){
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_layout_one,parent,false);
vh = new MyViewHolder_LayoutOne(view);
}else if(viewType == 2){
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_layout_two,parent,false);
vh = new MyViewHolder_LayoutTwo(view);
}
return vh;
}
4. Bind your views as per the position
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
switch(getItemViewType(position)){
case 1: //for layout 1
((ViewHolder_LayoutOne)holder).name.setText("");
break;
case 2:// for layout 2
((ViewHolder_LayoutTwo)holder).name.setText("");
break;
}
}
5.Now Most important getItemCount() method //return the number of views
#Override
public int getItemCount() {
return toptitles.length + 1;
// as i have only layout at the top and the remaining size equals the length of the array toptitles.So the overall length would be
//number of views of layout1 + number of views of layout2
}
Hope this helps!!!
you can override getItemViewType, and use different ViewHolder for different layout.
You need to override getItemViewType like this :
#Override
public int getItemViewType(int position) {
Add your condition and return viewType(Constant)
}
Now, Check for viewType and retrun viewHolder accordingly.
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEWHOLDER1) {
View v = LayoutInflater.from(activity).inflate(R.layout.layout1, parent, false);
return new ViewHolder1(v);
}else if(viewType == VIEWHOLDER2){
View v = LayoutInflater.from(activity).inflate(R.layout.layout2, parent, false);
return new ViewHolder2(v);
}
}
Now, Check for instance of viewHolder
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof ViewHolder1) {
Show values in UI accordingly.
}else if(holder instanceof ViewHolder2){
Show values in UI accordingly.
}
}
You can set the type you want in getItemViewType(int position) with the layout you want. And onCreateViewHolder you can check viewType and inflate the row accordingly.
here is an example.
I want to add the Fast-Scroll-Feature (like in the Android contacts app or in Whatsapp contacts) to my RecyclerView?
I would also use a listview but I need Cards in my app on the same layout!
If implementing fast-scroll to Recyclerview is too difficult, using a ViewStub is also an option, or not?
My RecyclerViewAdapter:
public class SongRecyclerViewAdapter extends RecyclerView.Adapter<SongRecyclerViewAdapter.Holder> {
private Song[] sSongs;
public SongRecyclerViewAdapter(Song[] songs) {
sSongs = songs;
}
#Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.adapter_songview, parent, false);
Holder holder = new Holder(view);
return holder;
}
#Override
public void onBindViewHolder(Holder holder, int position) {
//holder.imvSong.setImageResource(R.drawable.standardartwork);
holder.txvSongTitle.setText(sSongs[position].getTitle());
holder.txvSongInfo.setText(sSongs[position].getArtists());
}
#Override
public int getItemCount() {
return sSongs != null ? sSongs.length : 0;
}
public class Holder extends RecyclerView.ViewHolder {
LinearLayout linearLayout;
ImageView imvSong;
TextView txvSongTitle;
TextView txvSongInfo;
public Holder(View layout) {
super(layout);
linearLayout = (LinearLayout) layout;
imvSong = (ImageView) layout.findViewById(R.id.imvSong);
txvSongTitle = (TextView) layout.findViewById(R.id.adap_txvSongtitle);
txvSongInfo = (TextView) layout.findViewById(R.id.txvSongInfo);
}
}
}
Thanks!
the problem in your code would be the commented line :
//holder.imvSong.setImageResource(R.drawable.standardartwork);
to handle this you have 2 solutions:
1) if u have limited small amount of known images, you can to create bitmaps/drawables once when initializing then you will only need to reference those.
2) create a bitmap cache, smart way to decode bitmaps along with reusable bitmap pool and pooling mechanism. This is not trivial and this what 3rd party libraries do for you. I would not suggest to implemented it yourself.