How to dynamically change item view in recycler view? - android

I am writing music player with track list in recycler view. I want to change image view in clicked item. Tried to use getItemViewType, but as I can understand it sets view type only once. Here's my code:
public class TrackListAdapter extends RecyclerView.Adapter<TrackListAdapter.RegularViewHolder> implements IntConstants{
private ArrayList<Track> trackList;
private Context context;
int selectedPos = -1;
public TrackListAdapter(ArrayList<Track> trackList, Context context) {
this.trackList = trackList;
this.context = context;
EventBus.getDefault().register(this);
}
#Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
EventBus.getDefault().unregister(this);
super.onDetachedFromRecyclerView(recyclerView);
}
#Override
public RegularViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
RecyclerView.LayoutParams params = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
if (viewType == ITEM_TYPE_PLAYING){
//here i am sending xml for clicked item
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.playing_track_item, parent, false);
return new RegularViewHolder(itemView) ;
}else {
//here i'm sending xml for regular item
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.regular_track_item, parent, false);
return new RegularViewHolder(itemView) ;
}
}
#Override
public void onBindViewHolder(final RegularViewHolder holder, int position) {
Track t = trackList.get(position);
holder.textTrackTitle.setText(t.getTitle());
holder.textTrackAlbum.setText(t.getAlbum());
holder.textTrackArtist.setText(t.getArtist());
holder.parent.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EventBus.getDefault().post(new MessageEvent(TRACK_CLICKED_ACTION_CODE, holder.getAdapterPosition()));
}
});
}
#Override
public int getItemCount() {
return trackList.size();
}
class RegularViewHolder extends RecyclerView.ViewHolder{
ImageView imageAlbumArt;
TextView textTrackTitle, textTrackArtist, textTrackAlbum;
RelativeLayout parent;
private RegularViewHolder(View itemView){
super(itemView);
parent = (RelativeLayout)itemView.findViewById(R.id.item_parent);
imageAlbumArt = (ImageView)itemView.findViewById(R.id.image_album_art);
textTrackTitle = (TextView) itemView.findViewById(R.id.text_track_title);
textTrackArtist = (TextView) itemView.findViewById(R.id.text_track_artist);
textTrackAlbum = (TextView) itemView.findViewById(R.id.text_track_album);
}}
#Override
public int getItemViewType(int position) {
if (position == selectedPos){
return ITEM_TYPE_PLAYING;
}else {
return ITEM_TYPE_REGULAR;
}
}
#Subscribe
public void handleTrack(MessageEvent event){
if (event.getCode() == TRACK_SELECTED_ACTION_CODE){
selectedPos = event.getPosition();
}
}
}

You would have to update the selected position stored by the adapter, then notifyItemChanged() twice, for the positions of the old item (that used to be selected) and the new one (that is now selected).

Related

How do I change the background of a specific item onClick in a RecyclerView?

Im trying to make a selected item in the RecyclerView to change it's background when it is clicked, but once the other item in the RecyclerView is clicked, that item's background will be changed and the previously clicked item will change back to original.
I only manage to make the item change back to orignal(white background) when it is clicked the second time.
Any help please?
This is my RecyclerView Adapter
public class CharityListAdapter extends RecyclerView.Adapter<CharityListAdapter.CharityListViewHolder> {
String charityData[], descriptionData[];
int images[];
Context context;
public CharityListAdapter(Context ct, String charity[], String description[], int image[]) {
context = ct;
charityData = charity;
descriptionData= description;
images = image;
}
#NonNull
#Override
public CharityListViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.data_row, parent, false);
return new CharityListViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final CharityListViewHolder holder,final int position) {
holder.titleText.setText(charityData[position]);
holder.descText.setText(descriptionData[position]);
holder.charityImage.setImageResource(images[position]);
holder.charityLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(holder.charityLayout.isSelected()) {
holder.charityLayout.setSelected(false);
System.out.println("Set to false");
} else if(!holder.charityLayout.isSelected()){
holder.charityLayout.setSelected(true);
System.out.println("Set to true");
}
if(holder.charityLayout.isSelected()) {
holder.whiteBox.setBackgroundResource(R.drawable.bluebox);
DonateSelection.enableNextButton();
System.out.println("Blue Box");
}
if(!holder.charityLayout.isSelected()) {
holder.whiteBox.setBackgroundResource(R.drawable.box);
System.out.println("White Box");
}
}
});
}
#Override
public int getItemCount() {
return images.length;
}
public class CharityListViewHolder extends RecyclerView.ViewHolder {
TextView titleText, descText;
ImageView charityImage;
RelativeLayout whiteBox;
RelativeLayout charityLayout;
public CharityListViewHolder(#NonNull View itemView) {
super(itemView);
titleText = itemView.findViewById(R.id.titleText);
descText = itemView.findViewById(R.id.descText);
charityImage = itemView.findViewById(R.id.charityImage);
whiteBox = itemView.findViewById(R.id.whiteBox);
charityLayout = itemView.findViewById(R.id.charityLayout);
}
}
}
This is because of how RecyclerView works. It recycles your view after it disappears from your screen. You should use if/else to maintain the right state of a view. Replace your onClick action with below code.
holder.charityLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(holder.charityLayout.isSelected()) {
holder.charityLayout.setSelected(false);
System.out.println("Set to false");
} else {
holder.charityLayout.setSelected(true);
System.out.println("Set to true");
}
if(holder.charityLayout.isSelected()) {
holder.whiteBox.setBackgroundResource(R.drawable.bluebox);
DonateSelection.enableNextButton();
System.out.println("Blue Box");
}else{
holder.whiteBox.setBackgroundResource(R.drawable.box);
System.out.println("White Box");
}
}
});
Edited
Here is raw code.But, I think that should work. You should add an array of check state that recycler is composed of. By default they all will be false. Then, check it onBindViewHolder(). When click, set boolean to true and call notifyDatasetChanged.
public class CharityListAdapter extends RecyclerView.Adapter<CharityListAdapter.CharityListViewHolder> {
String charityData[], descriptionData[];
int images[]; boolean checkState[];
Context context;
public CharityListAdapter(Context ct, String charity[], String description[], int image[],boolean checkState[]) {
context = ct;
charityData = charity;
descriptionData= description;
images = image;
checkState=checkState;
}
#NonNull
#Override
public CharityListViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.data_row, parent, false);
return new CharityListViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final CharityListViewHolder holder,final int position) {
holder.titleText.setText(charityData[position]);
holder.descText.setText(descriptionData[position]);
holder.charityImage.setImageResource(images[position]);
if(checkState[position]){
holder.whiteBox.setBackgroundResource(R.drawable.bluebox);
DonateSelection.enableNextButton();
}else{
holder.whiteBox.setBackgroundResource(R.drawable.box);
}
holder.charityLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(checkState[position]){
checkState[position]=false;
}else{
checkState[position]=true;
}
notifyDataSetChanged();
}
});
}
#Override
public int getItemCount() {
return images.length;
}
public class CharityListViewHolder extends RecyclerView.ViewHolder {
TextView titleText, descText;
ImageView charityImage;
RelativeLayout whiteBox;
RelativeLayout charityLayout;
public CharityListViewHolder(#NonNull View itemView) {
super(itemView);
titleText = itemView.findViewById(R.id.titleText);
descText = itemView.findViewById(R.id.descText);
charityImage = itemView.findViewById(R.id.charityImage);
whiteBox = itemView.findViewById(R.id.whiteBox);
charityLayout = itemView.findViewById(R.id.charityLayout);
}
}
}
Rather than remembering the layout/ViewHolder you selected, it's better to remember the selected position. In your adapter add:
private int positionSelected = -1;
-1 indicates none is selected.
And then, in your ViewHolder constructor:
public CharityListViewHolder(#NonNull View itemView) {
super(itemView);
....
charityLayout = itemView.findViewById(R.id.charityLayout);
charityLayour.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
positionSelected = getAdapterPosition();
notifyDataSetChanged();
}
});
}
In your onBindViewHolder you can set the background accordingly:
if (position == positionSelected) {
// Set background for layout to indicate selected
} else {
// Set background for layout to indicate unselected
}

How to implement OnClickListener in RecyclerView?

I want to make selction menu for profiles using RecyclerView. I created it with RecyclerView. Now i am getting problem in onItemClickListener. I want to change background of CardView and Text Color on item select. at a time only one item can be selected. And on next button click it should redirect to activity according to selection.
This is my screen looks like:
public class SelectProfile extends AppCompatActivity {
private String[] mTextData;
private int[] mImgData;
RecyclerView recyclerView;
private ProfileAdapter profileAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_select_profile);
recyclerView = findViewById(R.id.recycleProfile);
RecyclerView.LayoutManager layoutManager = new GridLayoutManager(this,2);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
profileAdapter = new ProfileAdapter();
recyclerView.setAdapter(profileAdapter);
int imgData[] = {R.drawable.ic_college_icon,R.drawable.ic_parent,R.drawable.ic_student,R.drawable.ic_teaching,
R.drawable.ic_non_teaching,R.drawable.ic_other};
final String textData[] = {"School/College","Parent","Student","Teaching Staff","Non-Teaching Staff","Other"};
profileAdapter.setData(imgData,textData);
}
private class ProfileAdapter extends RecyclerView.Adapter<ProfileAdapter.ProfileAdapterViewHolder>{
int index = -1;
#NonNull
#Override
public ProfileAdapterViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
Context context = parent.getContext();
int layoutIdForListItem = R.layout.profile_item;
LayoutInflater layoutInflater = LayoutInflater.from(context);
boolean shouldAttachToParent = false;
View view = layoutInflater.inflate(layoutIdForListItem,parent,shouldAttachToParent);
return new ProfileAdapterViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final ProfileAdapterViewHolder holder,final int position) {
int mImage = mImgData[position];
String mText = mTextData[position];
holder.img.setImageResource(mImage);
holder.txt.setText(mText);
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
index = position;
notifyDataSetChanged();
}
});
if (index == position){
holder.card.setBackground(getResources().getDrawable(R.drawable.bg_select_profile));
holder.txt.setTextColor(getResources().getColor(R.color.colorPrimary));
}else {
holder.card.setCardBackgroundColor(getResources().getColor(android.R.color.white));
holder.txt.setTextColor(getResources().getColor(R.color.gray));
}
}
#Override
public int getItemCount() {
if (null == mImgData) return 0;
return mImgData.length;
}
public class ProfileAdapterViewHolder extends RecyclerView.ViewHolder {
private final AppCompatImageView img;
private final TextView txt;
private final CardView card;
private ProfileAdapterViewHolder(#NonNull View itemView) {
super(itemView);
img = itemView.findViewById(R.id.img);
txt = itemView.findViewById(R.id.txt);
card = itemView.findViewById(R.id.card);
}
}
private void setData(int[] imgData,String[] txtData){
mImgData = imgData;
mTextData = txtData;
notifyDataSetChanged();
}
}
}
Your approach of using an Index variable is right, add getter and setter method for the index. But you can't set the onClickListener on the adapter. Instead, set the listener on the card view like this ->
holder.card.setOnItemClickListener(new ClickListener() {
#Override
public void onItemClick(int position, View v) {
index = position;
notifyDataSetChanged();
}
});
Add this method inside your adapter class ->
public int getSelectedIndex(){
return this.index; }
After that, from your Activity, inside your NEXT button's onClickListener do the following thing,
btnNext.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(profileAdapter.getSelectedIndex() == 1)
//goto activity of your desire and so on
}
});
Use your card view as a parent layout for your recycler view item and apply on click listener to that card view in recycler view adapter.
holder.your_card_view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// get you recyclerview item position here
}
});
Add OnClickListner on holder root view , best place to write listener is in onCreateViewHolder , so replace your onCreateViewHolder with following
public ProfileAdapterViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
Context context = parent.getContext();
int layoutIdForListItem = R.layout.profile_item;
LayoutInflater layoutInflater = LayoutInflater.from(context);
boolean shouldAttachToParent = false;
View view = layoutInflater.inflate(layoutIdForListItem,parent,shouldAttachToParent);
ViewHolder viewHolder = new ViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
postion=viewHolder.getAdapterPosition();
notifyDataSetChanged();
}
});
return viewHolder ;
}
change and adjust your adapter like as below:
private class ProfileAdapter extends RecyclerView.Adapter<ProfileAdapter.ProfileAdapterViewHolder>{
int index = -1;
#NonNull
#Override
public ProfileAdapterViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
Context context = parent.getContext();
int layoutIdForListItem = R.layout.profile_item;
LayoutInflater layoutInflater = LayoutInflater.from(context);
boolean shouldAttachToParent = false;
View view = layoutInflater.inflate(layoutIdForListItem,parent,shouldAttachToParent);
return new ProfileAdapterViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull ProfileAdapterViewHolder holder, int position) {
int mImage = mImgData[position];
String mText = mTextData[position];
holder.img.setImageResource(mImage);
holder.txt.setText(mText);
if (index == position){
holder.card.setCardBackgroundColor(getResources().getColor(R.color.colorPrimary));
holder.txt.setTextColor(getResources().getColor(R.color.colorPrimary));
}else {
holder.card.setCardBackgroundColor(getResources().getColor(android.R.color.white));
holder.txt.setTextColor(getResources().getColor(R.color.gray));
}
}
#Override
public int getItemCount() {
if (null == mImgData) return 0;
return mImgData.length;
}
public class ProfileAdapterViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private final AppCompatImageView img;
private final TextView txt;
private final CardView card;
private ProfileAdapterViewHolder(#NonNull View itemView) {
super(itemView);
img = itemView.findViewById(R.id.img);
txt = itemView.findViewById(R.id.txt);
card = itemView.findViewById(R.id.card);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
setIndex(getAdapterPosition());
notifyDataSetChanged();
}
}
private void setData(int[] imgData,String[] txtData){
mImgData = imgData;
mTextData = txtData;
notifyDataSetChanged();
}
public void setOnItemClickListener(ClickListener clickListener){
mClickListener = clickListener;
}
private void setIndex(int index){
this.index=index;
}
}
you may not need the ClickListener.
You can pass a listener object in the constructor which implements by fragment OR activity
/**
* item click interface of adapter
*/
public interface ProfileAdapterListener {
void onItemClick(int position, ProfileAdapterViewHolder holder)
}
This interface implements by Fragment OR Activity
/**
* On item clicked Implement Method from adapter listener.
*
* #param position
*/
#Override
public void onItemClick(int position, ProfileAdapterViewHolder holder) {
// Here you can call that method
}
Then you pass this listener in the constructor of the adapter.
private void buildRecyclerView() {
profileAdapter = new ProfileAdapter(this);
recyclerView.setAdapter(profileAdapter);
}
In the constructor, you can assign like this
private ProfileAdapterListener mProfileAdapterListener;
public OfferAdapter(ProfileAdapterListener mProfileAdapterListener) {
this.mProfileAdapterListener = mProfileAdapterListener
}
}
Now you can use this listener by setting click listener on any Viwe like this
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mProfileAdapterListener.onItemClick(position, holder);
}
});
It returns to call the method of onItemClick which implements this method. This is the safe and sound method to click on each item or any view in the item.

Recyclerview selected view null on Scroll

I apply own logic on single select item on recyclerview. when i select item and scroll to last position and select new item and remove background last selected item then shows error
java.lang.NullPointerException: Attempt to read from field 'android.view.View android.support.v7.widget.RecyclerView$ViewHolder.itemView' on a null object reference
My logic is:
recyclerView.addOnItemTouchListener(
new RecyclerItemClickListener(SubServicesActivity.this, recyclerViewSubServices ,new RecyclerItemClickListener.OnItemClickListener() {
#Override public void onItemClick(View view, int position) {
String selectedName = serviceLists.get(position).getServiceTitleEnglish();
RecyclerView.ViewHolder viewHolder = recyclerViewSubServices.findViewHolderForPosition(position);
FrameLayout serviceImageBackground = (FrameLayout) viewHolder.itemView.findViewById(R.id.image_back_layout);
if(!select) {
serviceImageBackground.setBackground(getResources().getDrawable(R.drawable.selection_border_tick_rec));
select = true;
selectedPosition = position;
}else{
RecyclerView.ViewHolder viewHolderselect = recyclerViewSubServices.findViewHolderForPosition(selectedPosition);
FrameLayout serviceImageSelect = (FrameLayout) viewHolderselect.itemView.findViewById(R.id.image_back_layout);
serviceImageSelect.setBackgroundColor(getResources().getColor(R.color.material_grey_200));
serviceImageBackground.setBackground(getResources().getDrawable(R.drawable.selection_border_tick_rec));
selectedPosition = position;
}
}
#Override public void onLongItemClick(View view, int position) {
// do whatever
}
})
);
Adapter Class:
public class SubServicesAdapter extends RecyclerView.Adapter<SubServicesAdapter.ServiceViewHolder> {
List<ServiceList> subServiceModels;
Context context;
public class ServiceViewHolder extends RecyclerView.ViewHolder{
TextView textViewSubServiceName;
ImageView imageViewSubServiceIcon;
FrameLayout imageBacklayout;
CheckBox selectSubServiceCheckBox;
public ServiceViewHolder(View itemView) {
super(itemView);
this.textViewSubServiceName = (TextView) itemView.findViewById(R.id.sub_service_title_textview);
this.imageViewSubServiceIcon = (ImageView) itemView.findViewById(R.id.sub_service_icon_imageview);
this.imageBacklayout = (FrameLayout) itemView.findViewById(R.id.image_back_layout);
this.selectSubServiceCheckBox = (CheckBox) itemView.findViewById(R.id.select_sub_service_checkbox);
}
}
public SubServicesAdapter(Context _context, List<ServiceList> _subServiceModels) {
this.subServiceModels = _subServiceModels;
this.context = _context;
}
#Override
public ServiceViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.sub_service_card_layout, parent, false);
ServiceViewHolder myViewHolder = new ServiceViewHolder(view);
return myViewHolder;
}
#Override
public void onBindViewHolder(ServiceViewHolder holder, int position) {
final ServiceList serviceItem = subServiceModels.get(position);
TextView textViewSubServiceName = holder.textViewSubServiceName;
ImageView imageViewSubServiceIcon = holder.imageViewSubServiceIcon;
FrameLayout imageBackground = holder.imageBacklayout;
final CheckBox checkBox = holder.selectSubServiceCheckBox;
textViewSubServiceName.setText(subServiceModels.get(position).getServiceTitleEnglish());
Picasso.with(context)
.load(subServiceModels.get(position).getImageURL())
.resize(150, 150)
.centerCrop()
.into(imageViewSubServiceIcon);
// imageViewSubServiceIcon.setImageResource(icons[position]);
}
#Override
public int getItemCount() {
return subServiceModels.size();
}
}
I also face the same issue I was resolved the following way.
I just created the model class for maintaining the selected values. So when we scrolled "onBindViewHolder" will be called so we just check the selected position and set the background.
The problem is Recyclerview reuse the viewholder and if it is not laid down the view then it returns the null.
public void onBindViewHolder( ViewHolder holder, int position) {
File file =new File(imageUrlList.get(position));
View v=null;
if(file.length()>0 ){
Glide.with(mContext).load(imageUrlList.get(position)).into(holder.imageView);
holder.imagePath=imageUrlList.get(position);
// Get the Selected Item from model class and set the background.
**ArrayList<Integer>selectedImages=GalleryHelper.getInstance().getSelectedImagePositions();
if(selectedImages!=null && selectedImages.contains(position)){
Drawable highlight=mContext.getResources().getDrawable(R.drawable.image_border);
v= holder.itemView;
v.setBackground(highlight);
}else {
v= holder.itemView;
v.setBackground(null);
}
}else{
Log.i(TAG," This file is Empty : "+imageUrlList.get(position));
}**
}

Android cards multiple layouts based on position

I want to use different layouts based on card position on screen. For example I need that first card (position 0 uses layout first_card.xml) and other cards should use other_cards.xml layout and it should be positioned horizontally (see picture below (picture taken from official google site)).
Here's my code
public class CardAdapter extends RecyclerView.Adapter<CardAdapter.ViewHolder>{
private List<CardData> mItems;
private Context context;
private SendInfoToCards cardDatas;
private boolean mainMenu;
private static final int FIRST = 1;
private static final int SECOND = 2;
public CardAdapter(Context c, SendInfoToCards datas, boolean main) {
super();
this.context = c;
this.cardDatas = datas;
mItems = datas.getList();
this.mainMenu = main;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) {
if(!mainMenu) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_view_card_item, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(v);
v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TextView city = (TextView) v.findViewById(R.id.tv_movie);
String fragmentSwitcher = city.getText().toString();
if (fragmentSwitcher.equals("Zemelapis")) {
switchFragments(0);
} else {
switchFragments(1);
}
}
});
return viewHolder;
} else {
View v;
if(position == 0){
v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.first_layout, viewGroup, false);
}else{
v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.second_layout, viewGroup, false);
}
ViewHolder viewHolder = new ViewHolder(v);
v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TextView city = (TextView) v.findViewById(R.id.tv_movie);
String fragmentSwitcher = city.getText().toString();
}
});
return viewHolder;
}
}
private void switchFragments(int nr){
if(nr == 0){
Log.d("TAG", "mapo");
} else {
Intent intent = new Intent(context, DetailTourActivity.class);
context.startActivity(intent);
}
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
if(i != 0){
} else {
CardData data = mItems.get(i);
viewHolder.tvMovie.setText(data.getName());
viewHolder.imgThumbnail.setImageResource(data.getThumbnail());
}
}
#Override
public int getItemCount() {
return mItems.size();
}
class ViewHolder extends RecyclerView.ViewHolder{
public ImageView imgThumbnail;
public TextView tvMovie;
public ViewHolder(View itemView) {
super(itemView);
imgThumbnail = (ImageView)itemView.findViewById(R.id.img_thumbnail);
tvMovie = (TextView)itemView.findViewById(R.id.tv_movie);
}
}
}
Sadly but onCreateViewHolder method second argument position is always 0. Why is that? How to fix it?
Here is what your adapter class should roughly look like
public class CardAdapter extends RecyclerView.Adapter<RecylerView.ViewHolder>{
private List<CardData> mItems;
private Context context;
private SendInfoToCards cardDatas;
private boolean mainMenu;
private static final int FIRST = 1;
private static final int SECOND = 2;
public CardAdapter(Context c, SendInfoToCards datas, boolean main) {
super();
this.context = c;
this.cardDatas = datas;
mItems = datas.getList();
this.mainMenu = main;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) {
if(type == SECOND){
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.second_layout, viewGroup, false);
return new SecondViewViewHolder(v);
} else {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.first_layout, viewGroup, false);
return new FirstViewViewHolder(v);
}
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
if(viewHolder instanceof SecondViewViewHolder){
SecondViewViewHolder secondViewViewHolder = (SecondViewViewHolder) viewHolder;
// bind second views
secondViewViewHolder.imgThumbnail.setImageResource(R.id.img_resource);
} else {
FirstViewViewHolder firstViewViewHolder = (FirstViewViewHolder) viewHolder;
// bind firs view
firstViewViewHolder.imgThumbnail.setImageResource(R.id.img_resource);
}
}
#Override
public int getItemViewType(int position) {
if (position == 0) {
return FIRST;
} else {
return SECOND;
}
}
#Override
public int getItemCount() {
return mItems.size();
}
class FirstViewViewHolder extends RecyclerView.ViewHolder{
public ImageView imgThumbnail;
public TextView tvMovie;
public FirstViewViewHolder(View itemView) {
super(itemView);
imgThumbnail = (ImageView)itemView.findViewById(R.id.img_thumbnail);
tvMovie = (TextView)itemView.findViewById(R.id.tv_movie);
}
}
class SecondViewViewHolder extends RecyclerView.ViewHolder{
public ImageView imgThumbnail;
public TextView tvMovie;
public ImageView imgThumbnail2;
public TextView tvMovie2;
public SecondViewViewHolder(View itemView) {
super(itemView);
//find viewby id
}
}
}
What is changed
Since recyclerview can handle multiple view types you can decide which layout to inflate depending on some condition. You do this by overriding getItemViewTypeMethod() of the RecylerView Adapter
#Override
public int getItemViewType(int position) {
if (position == 0) {
return FIRST;
} else {
return SECOND;
}
}
When creating viewholder. the onCreateViewHolder method will get the view type. Based on the type you should inflate the appropriate layout and return the appropriate viewholder
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) {
if(type == SECOND){
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.second_layout, viewGroup, false);
return new SecondViewViewHolder(v);
} else {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.first_layout, viewGroup, false);
return new FirstViewViewHolder(v);
}
}
Since there are two layouts involved, I have created two View Holders to hold first and second layouts. (FirstViewHolder & SecondViewHolder). Because of this you have to set your adapter's ViewHolder type to the base ViewHolder class like this
public class CardAdapter extends RecyclerView.Adapter<RecylerView.ViewHolder>{
...
}
and now in the onBindViewHolder method, you can determine which ViewHolder is currently being bound and do your work accordingly
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
if(viewHolder instanceof SecondViewViewHolder){
SecondViewViewHolder secondViewViewHolder = (SecondViewViewHolder) viewHolder;
// bind second views
secondViewViewHolder.imgThumbnail.setImageResource(R.id.img_resource);
} else {
FirstViewViewHolder firstViewViewHolder = (FirstViewViewHolder) viewHolder;
// bind firs view
firstViewViewHolder.imgThumbnail.setImageResource(R.id.img_resource);
}
}
Check this for an example
The thing is that the second parameter is not position, it is viewType.
From the docs:
public abstract VH onCreateViewHolder (ViewGroup parent, int viewType)
Look at public void onBindViewHolder (VH holder, int position, List<Object> payloads), this is what you actually need

Android RecyclerView.Adapter onCreateViewHolder() working

I am using RecyclerView.Adapter but I am little confused regarding working of its method onCreateViewHolder.
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
if(viewType==TYPE_ITEM) {
View mView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.inflate_common_item, viewGroup, false);
ViewHolder vh = new ViewHolder(mView);
return vh;
} else {
View mView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.inflate_uncommon_item, viewGroup, false);
ViewHolderFooter vh = new ViewHolderFooter(mView);
return vh;
}
}
So incase I have 10 items in my list so for each item this method will be called and every time a new ViewHolder will be created of course it'll one time for each view but now my question is when we were using ListView and BaseAdapter with them we store ViewHolder in tag and use that. We don't create ViewHolder for each item.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
MyViewHolder mViewHolder;
if(convertView == null) {
convertView = inflater.inflate(R.layout.layout_list_item, null);
mViewHolder = new MyViewHolder();
convertView.setTag(mViewHolder);
} else {
mViewHolder = (MyViewHolder) convertView.getTag();
}
mViewHolder.tvTitle = detail(convertView, R.id.tvTitle, myList.get(position).getTitle());
mViewHolder.tvDesc = detail(convertView, R.id.tvDesc, myList.get(position).getDescription());
mViewHolder.ivIcon = detail(convertView, R.id.ivIcon, myList.get(position).getImgResId());
return convertView;
}
So are we not creating extra viewholders object. Please help me understand the pros and cons.
Thanks
onCreateViewHolder only creates a new view holder when there are no existing view holders which the RecyclerView can reuse. So, for instance, if your RecyclerView can display 5 items at a time, it will create 5-6 ViewHolders, and then automatically reuse them, each time calling onBindViewHolder.
Its similar to what your code in the ListView does (checking if convertView is null, and if not, grabbing the existing ViewHolder from the tag), except, with RecyclerView, this is all done automatically.
I suppose this is one of the pros with using a RecyclerView - you don't need to worry so much about reusing ViewHolders as you do with ListView. The con is, RecyclerView is very customisable, but has very little built in functionality - unlike ListView which is not very customisable, but has a lot of built in functionality.
You can use this :
--> Create a constructor :
/**
* Created by Deepak Sharma on 31/10/17.
*/
public class StoreListAdapter<T> extends RecyclerView.Adapter<StoreListAdapter.ViewHolder> implements Filterable {
private Collection<T> mItems;
private Context context;
private int mLayout;
IClickable iClickable;
private boolean isAnimationAllowed;
private StoreSearchFilter<T> mSearchFilter;
public StoreListAdapter(Context context)
{
this.context = context;
}
public void setData(Collection<T> items, boolean isAnimationAllowed)
{
mItems = items;
this.isAnimationAllowed = isAnimationAllowed;
}
public void setCallback(int layout, IClickable iClickable)
{
this.mLayout = layout;
this.iClickable = iClickable;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(mLayout, viewGroup, false);
iClickable.init(view);
StoreListAdapter.ViewHolder viewHolder = new StoreListAdapter.ViewHolder(view);
view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
int width = view.getMeasuredWidth();
int height = view.getMeasuredHeight();
// viewHolder.itemView.getLayoutParams().width = width;
viewHolder.itemView.getLayoutParams().height = height+24;
return viewHolder;
}
#Override
public void onBindViewHolder(StoreListAdapter.ViewHolder viewHolder, int i) {
iClickable.execute(viewHolder, mItems.toArray()[i],viewHolder.getAdapterPosition());
if (isAnimationAllowed)
setAnimation(viewHolder.itemView, i);
}
#Override
public int getItemCount() {
return mItems.size()>0?mItems.size():0;
}
#Override
public Filter getFilter() {
if (mSearchFilter == null)
mSearchFilter = new StoreSearchFilter<T>((ArrayList<StoreModel>) mItems, new IFilteredList<T>() {
#Override
public void onListFiltered(ArrayList<T> list) {
setData(list, false);
notifyDataSetChanged();
}
});
return mSearchFilter;
}
public class ViewHolder extends RecyclerView.ViewHolder {
private final TextView mTextView;
//private CheckBox mCheckBox;
ViewHolder(View v) {
super(v);
mTextView = (TextView)v.findViewById(R.id.list_item);
// Handle item click and set the selection
/*v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Redraw the old selection and the new
notifyItemChanged(selectedItem);
selectedItem = getLayoutPosition();
notifyItemChanged(selectedItem);
}
});*/
}
}
public interface IClickable<T> {
public void init(View view);
public void execute(StoreListAdapter.ViewHolder holder, T object, int position);
}
/**
* Here is the key method to apply the animation
*/
private void setAnimation(View viewToAnimate, int position)
{
Animation animation = AnimationUtils.loadAnimation(context, android.R.anim.slide_in_left);
viewToAnimate.startAnimation(animation);
}
}
--> In you Activity/Fragment :
ArrayList<StoreModel> mStoreList = new ArrayList<>();
mStoreListAdapter = new StoreListAdapter(getActivity());
boolean isAnimate = false;
mStoreListAdapter.setData(mStoreList, isAnimate);
then call callback
mStoreListAdapter.setCallback(R.layout.store_item, new StoreListAdapter.IClickable() {
#Override
public void init(View view) {
// Toast.makeText(getActivity(), "Initialized", Toast.LENGTH_SHORT).show();
}
#Override
public void execute(StoreListAdapter.ViewHolder viewHolder, Object object, int position) {
final StoreModel model = (StoreModel) object;
View view = viewHolder.itemView;
StoreListAdapter.ViewHolder holder = viewHolder;
final CoordinatorLayout fabGameview = (CoordinatorLayout) view;
final CardView cardView = (CardView) fabGameview.findViewById(R.id.store_item_cardview);
TextView txtStoreName = (TextView) cardView.findViewById(R.id.txtStoreName);
TextView txtStoreAddress = (TextView) cardView.findViewById(R.id.txtStoreAddress);
TextView txtStoreCity = (TextView) cardView.findViewById(R.id.txtStoreCity);
TextView txtPrizes = (TextView) cardView.findViewById(R.id.txtPrizes);
txtStoreName.setText(model.getStoreName());
txtStoreAddress.setText(model.getStoreAddress());
txtStoreCity.setText(model.getStoreCity());
txtPrizes.setText(String.valueOf(model.getPrizesAvailable()));
LinearLayout linearDetails = (LinearLayout) cardView.findViewById(R.id.linearDetails);
LinearLayout linearPrize = (LinearLayout) cardView.findViewById(R.id.linearPrize);
if (clickedMarkerModel != null && clickedMarkerModel == model) {
holder.itemView.setSelected(true);
// holder.itemView.setPressed(true);
// linearDetails.setBackgroundColor(getResources().getColor(R.color.colorPrimaryDark));
// linearPrize.setBackgroundColor(getResources().getColor(R.color.colorPrimaryDark));
// mRecyclerStore.stopScroll();
} else {
holder.itemView.setSelected(false);
// holder.itemView.setPressed(false);
// linearDetails.setBackground(ContextCompat.getDrawable(getActivity(), R.drawable.store_selector));
// linearPrize.setBackground(ContextCompat.getDrawable(getActivity(), R.drawable.store_selector));
}
// TODO Here scroll recycler view upto selected item
/*mRecyclerStore.smoothScrollToPosition(mStoreListAdapter.getItemCount() - 1);*/
cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
boolean isAddedToBackStack = true;
StoreDetailsAndProductListFragment storeDetailsAndProductListFragment = new StoreDetailsAndProductListFragment();
Bundle bundle = new Bundle();
bundle.putParcelable(ExtrasUtil.STORE, model);
storeDetailsAndProductListFragment.setArguments(bundle);
showOtherFragment(storeDetailsAndProductListFragment, getActivity().getFragmentManager(), isAddedToBackStack);
}
});
}
});
mRecyclerStore.setAdapter(mStoreListAdapter);

Categories

Resources