Setting data on ViewHolder - android

I am using a RecyclerView in my project and I am having an issue where my RecyclerView is being populated by the same values as if the values are being overwritten inside the forEach in onBindViewHolder below:
class ProfListAdapter extends RecyclerView.Adapter<ViewHolder>{
private LayoutInflater mLayoutInflater;
public ProfListAdapter(Context context) {
mLayoutInflater = LayoutInflater.from(context);
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType){
return new ViewHolder(mLayoutInflater
.inflate(R.layout.recycler_view, viewGroup, false));
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, final int position){
System.out.println(position);
final String profName;
Integer k = 0;
for (Integer key : mData.keySet()) {
viewHolder.setData(mData.get(key));
}
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
Intent intent = ProfPager.newIntent(getActivity(), position);
startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return mData.size();
}
}
class ViewHolder extends RecyclerView.ViewHolder {
private TextView mTextView;
private ViewHolder(View itemView){
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id._prof); // galleryimage
}
private void setData(String profName){
mTextView.setText(profName);
System.out.println("setData" + profName);
}
}
When I log out to the console I can see that the value in setData() is getting the correct item to display. However, what is being displayed inside the fragment is just the last profName that is comes across in the forEach. What could be causing this behaviour? Here is a link to the entire fragment: https://pastebin.com/xS7CSbbK
If I use the position, it does not always coincide with the key in the Hashmap that stores the SQLite _id.

Setting data on ViewHolder
You may not need the for each loop inside the onBindViewHolder, instead try to set data using the position value available at onBindViewHolder. cheers :)

For RecyclerView , use ArrayList for simplicity
And change your onBindViewHolder like this
#Override
public void onBindViewHolder(ViewHolder viewHolder, final int position){
System.out.println(position);
viewHolder.setData(mData.get(position));
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v){
Intent intent = ProfPager.newIntent(getActivity(), position);
startActivity(intent);
}
});
}
I assume mData as ArrayList

Related

Why am I getting random result on item click in RecyclerView?

Why am I getting random result on item click? My error is inside the btn onClick Listener but what shows in my TextView (reference_number) is fine. when button clicked - it will open another activity to show more details about it but it shows random result, after I scroll my recyclerView it will change the result again. ## im confused already, below is all the codes inside my Adapter
What's the cause of this error and where did I get this wrong?
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>{
int i;
#NonNull
#Override
public RecyclerViewAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_history_pending, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull RecyclerViewAdapter.ViewHolder holder, final int position) {
i = holder.getAdapterPosition();
holder.reference_number.setText("#"+mainActivity.myBookings.getCargoBooking().get(i).getReferenceNumber());
holder.btn_details.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String fee;
fee = isNull(mainActivity.myBookings.getCargoBooking().get(i).getCargoItem().get(0).getCargoFee().toString());
Intent i = new Intent(mainActivity.context, BookingDetailsActivity.class);
i.putExtra("fee", fee);
startActivity(i);
}
});
}
private String isNull(String s){
if (s != null){
return s;
} else return "";
}
#Override
public int getItemCount() {
return mainActivity.myBookings.getCargoBooking().size();
}
public class ViewHolder extends RecyclerView.ViewHolder{
TextView reference_number, driver_schedule;
Button btn_details;
public ViewHolder(View itemView) {
super(itemView);
reference_number = itemView.findViewById(R.id.tv_pending_trackNum);
driver_schedule = itemView.findViewById(R.id.tv_view_driver_schedule);
btn_details = itemView.findViewById(R.id.btn_pending_view);
}
}
}
Your problem is the "i" variable in your adapter scope, remove it and declare it as local (inside your onBindViewHolder method) and it should work.
That's because your instance variable i keeps changing whenever onBindViewHolder is called, say when the RecyclerView is being scrolled for example. Instead, change the variable i to local:
#Override
public void onBindViewHolder(#NonNull RecyclerViewAdapter.ViewHolder holder, final int position) {
final int i = holder.getAdapterPosition();

Getting Wrong position of RecyclerView Item in next Fragment after applying SearchFilter and OnClick on RecyclerView Item

I have set RecyclerView on Fragment.I have set OnClick listener for RecyclerView item from RecyclerView Adapter.I get Filtered item using SearchFilter.After onClick of RecyclerView Item I need to get the Item Position and pass in Bundles to Next Fragment.But I am getting wrong position after filtering item.This is code for setting Recyclerview Adapter
public class StatusAdapter extends RecyclerView.Adapter<StatusAdapter.ViewHolder> {
private ArrayList<CurrentEntry> originalList,filterList;
Context context;
public void filter(String newText)
{
filterList.clear();
if(TextUtils.isEmpty(newText))
{
filterList.addAll(originalList);
}
else
{
for(CurrentStatusEntry entry:originalList)
{
if(entry.getName().toLowerCase().contains(newText))
filterList.add(entry);
}
notifyDataSetChanged();
}
// notifyDataSetChanged();
}
public StatusAdapter(ArrayList<CurrentEntry> originalList,Context context)
{
this.originalList=originalList;
this.context=context;
this.filterList=new ArrayList<CurrentEntry>();
this.filterList.addAll(this.originalList);
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v= LayoutInflater.from(parent.getContext()).inflate(R.layout.current_status_card,parent,false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
final CurrentEntry current=filterList.get(position);
int temppos=holder.getAdapterPosition();
int finalPos=temppos+1;
holder.Name.setText(current.getName());
holder.People.setText(current.getNo());
holder.Estimate.setText(current.getEsti());
holder.bookTime.setText(current.getTime());
holder.myNumber.setText(String.valueOf(finalPos));
holder.lapsedTime.setText(current.getLapsedTime());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position=holder.getAdapterPosition();
Bundle bundle=new Bundle();
bundle.putInt("position",position);
StatusInfo satusInfo=new StatusInfo();
satusInfo.setArguments(bundle);
AppCompatActivity activity=(AppCompatActivity)v.getContext();
activity.getSupportFragmentManager().beginTransaction().add(R.id.frame,satusInfo).addToBackStack("info").commit();
}
});
}
#Override
public int getItemCount() {
return (null!=filterList ? filterList.size():0);
}
This is code for my ViewHolder
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView Name ;
private TextView People;
private TextView Estimate;
private TextView myNumber;
private TextView bookTime;
private TextView lapsedTime;
private ViewHolder(View itemView) {
super(itemView);
Name=(TextView)itemView.findViewById(R.id.name);
People=(TextView)itemView.findViewById(R.id.people);
Estimate=(TextView)itemView.findViewById(R.id.estimate);
myNumber=(TextView)itemView.findViewById(R.id.no);
bookTime=(TextView)itemView.findViewById(R.id.bookTime);
lapsedTime=(TextView)itemView.findViewById(R.id.lapsedTime);
}
How to get correct position of RecyclerView item after filtering ?
Find position from originalList using object index,i have updated the code
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position=holder.getAdapterPosition();
Bundle bundle=new Bundle();
// change
bundle.putInt("position", originalList.indexOf(filterList.get(position)));
StatusInfo satusInfo=new StatusInfo();
satusInfo.setArguments(bundle);
AppCompatActivity activity=(AppCompatActivity)v.getContext();
activity.getSupportFragmentManager().beginTransaction().add(R.id.frame,satusInfo).addToBackStack("info").commit();
}
});
Passing position is Useless try to pass direct object or id

notifyDataSetChanged not working on when adapter change

I am getting data from server and then parsing it and storing it in a List. I am using this list for the RecyclerView's adapter. I am using Fragments.
I extended
RecyclerView.Adapter<RecyclerView.ViewHolder>
this my complete adapter class
public class GridAdapter extends RecyclerView.Adapter<GridAdapter.ViewHolder> {
private List<ProductModel> list;
ItemClickListener itemClickListener;
public GridAdapter(List<ProductModel> list, ItemClickListener itemClickListener) {
this.list = list;
this.itemClickListener = itemClickListener;
}
public void update(int position,ProductModel pm){
Log.v("update adapter",position+"");
list.set(position,pm);
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.grid_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
final ProductModel cp = list.get(position);
holder.tvName.setText(cp.name);
holder.tvPrice.setText(cp.price);
holder.ratingBar.setRating(cp.rate);
Log.v("grid",cp.id + "=="+ cp.checked);
if(cp.checked==1){
holder.imgCheck.setVisibility(View.VISIBLE);
}else{
holder.imgCheck.setVisibility(View.GONE);
}
LayerDrawable stars = (LayerDrawable) holder.ratingBar.getProgressDrawable();
stars.getDrawable(2).setColorFilter(Color.YELLOW, PorterDuff.Mode.SRC_ATOP);
holder.lnrItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
itemClickListener.onItemClick(cp,position);
}
});
}
#Override
public int getItemCount() {
return list.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView tvName, tvPrice;
ImageView imgMenu, imgCheck;
LinearLayout lnrItem;
RatingBar ratingBar;
public ViewHolder(View itemView) {
super(itemView);
lnrItem = itemView.findViewById(R.id.lnrItem);
imgMenu = itemView.findViewById(R.id.imgMenu);
imgCheck = itemView.findViewById(R.id.imgCheck);
tvName = itemView.findViewById(R.id.tvName);
ratingBar = itemView.findViewById(R.id.ratingBar);
tvPrice = itemView.findViewById(R.id.tvPrice);
}
}
}
and in fragment activity i use this code to load adapter to recyclerview
public void fillListProduct(List<ProductModel> mItems) { //method for load adapter for the first time
mAdapterGrid = new GridAdapter(mItems,this);
recGrid.setAdapter(mAdapterGrid);
}
void update(){ //method to change checked value
mAdapterGrid.update(cp.row,cp);
mAdapterGrid.notifyDataSetChanged();
}
in my custom adapter i have 3 variable lets call id, name, checked, everything work properly, in first load i set 0 as default value for checked, in my scenario user can change checked value by tap the row of recycleview.
my question is, when user tap desire row then checked will change from 0 to 1 and display it to recycelview, i already use notifydatasetchange() but not working.
Please help.
thanks
Try to remove mAdapterGrid.notifyDataSetChanged();
and modify this function in your adpater:
public void update(int position,ProductModel pm){
Log.v("update adapter",position+"");
list.set(position,pm);
notifyItemChanged(position);
}

sending row id when RecyclerView item clicked

I have prepared a Database in my asset folder . I have one recyclerView for showing the title in the database . Now i want show DetailActivity with specificid . my means is show description of post . I think it can be done with intent , but i don't know how .
public class FehrestRecyclerAdapter extends RecyclerView.Adapter<FehrestRecyclerAdapter.ViewHolder> {
private Context ctx;
private List<Post> posts;
//constructor
public FehrestRecyclerAdapter(Context ctx, List<Post> posts) {
this.ctx = ctx;
this.posts= posts;
}
//ViewHodler Pattern
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private TextView txtTitle;
public ViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
txtTitle= (TextView)itemView.findViewById(R.id.txtTitle);
}
#Override
public void onClick(View v) {
//Toast.makeText(v.getContext() , txtTitle.getText() , Toast.LENGTH_SHORT).show();
Intent intent = new Intent(itemView.getContext() , DetailActivity.class);
intent.putExtra("postId" , "id");
itemView.getContext().startActivity(intent);
}
}
//OncreateViewHolder
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_row , parent , false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
//OnBindViewHolder
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Post current = Post.get(position);
holder.txtTitle.setText(current.getTitle());
}
//getItemCount method
#Override
public int getItemCount() {
return posts.size();
}
}
i have a 3 columns in my database
1.id 2.title 3.desc
and 3 methods in my Post model Class :
getId() 2.getTitle() 3.getDesc()
as you can see i can send intent to DetailActivity . But i don't know how can i achieve to sending specific id to DetailActivity. thanks
Make one Variable id in ViewHolder and assign that in onBindViewHolder() , I updated the code according to that.
public class FehrestRecyclerAdapter extends RecyclerView.Adapter<FehrestRecyclerAdapter.ViewHolder> {
private Context ctx;
private List<Post> posts;
//constructor
public FehrestRecyclerAdapter(Context ctx, List<Post> posts) {
this.ctx = ctx;
this.posts= posts;
}
//ViewHodler Pattern
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private TextView txtTitle;
private int id;
public ViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
txtTitle= (TextView)itemView.findViewById(R.id.txtTitle);
}
#Override
public void onClick(View v) {
//Toast.makeText(v.getContext() , txtTitle.getText() , Toast.LENGTH_SHORT).show();
Intent intent = new Intent(itemView.getContext() , DetailActivity.class);
// here pass id through intent
intent.putExtra("postId" , id);
itemView.getContext().startActivity(intent);
}
}
//OncreateViewHolder
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_row , parent , false);
ViewHolder viewHolder = new ViewHolder(v);
return viewHolder;
}
//OnBindViewHolder
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Post current = Post.get(position);
holder.txtTitle.setText(current.getTitle());
// add this line
holder.id = Post.get(postion).getId();
}
//getItemCount method
#Override
public int getItemCount() {
return posts.size();
}
}
How can i achieve to sending specific id to DetailActivity
Use setTag method of holder.txtTitle TextView for storing id with TextView and call getTag inside onClick of View Click like:
1. In onBindViewHolder save current using setTag:
holder.txtTitle.setTag(current);
holder.txtTitle.setText(current.getTitle());
2. In onClick of View use getTag as:
#Override
public void onClick(View v) {
txtTitle= (TextView)v.findViewById(R.id.txtTitle);
Post current=(Post)txtTitle.getTag();
intent.putExtra("postId" , current.getId());
intent.putExtra("postTitle" , current.getTitle());
....
}

How to add OnClickListner to CardView?

I am working with RecyclerView and CardView. I want to attach OnClickListner to each card. I tried with many answers available on stackoverflow, but they are not working for me. So far I have tried -
public class SubjectAdapter extends RecyclerView.Adapter<SubjectAdapter.ViewHolder> implements View.OnClickListener,
View.OnLongClickListener{
private static final String LOGCAT = "SubjectAdapter";
private final Context mContext;
List<Subject> SubjectsList;
public SubjectAdapter(Context context) {
super();
this.mContext = context;
SQLiteDatabase.loadLibs(mContext);
DBHelper myDbHelper = new DBHelper(mContext);
SubjectsList = new ArrayList<Subject>();
SubjectsList = myDbHelper.getAllSubjects();
myDbHelper.close();
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.subject_cardview_row, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(v);
// Below two lines are NOT working
viewHolder.tvSubjectName.setOnClickListener(this);
//viewHolder.setOnClickListener(this);
return viewHolder;
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
Subject subject = SubjectsList.get(i);
viewHolder.tvSubjectName.setText(subject.getSubject_Name());
viewHolder.tvCounts.setText(String.valueOf(subject.getSubject_Number_of_Questions()));
// Below two lines are NOT working
viewHolder.tvSubjectName.setOnClickListener(this);
//viewHolder.setOnClickListener(this);
}
#Override
public int getItemCount() {
return SubjectsList.size();
}
#Override
public void onClick(View v) {
// It's not working either
ViewHolder holder = (ViewHolder) v.getTag();
int position = holder.getPosition();
if (v.getId() == holder.tvSubjectName.getId()){
Log.d(LOGCAT, "tvSubjectName onClick at" + position);
//Toast.makeText(mContext, "tvSubjectName onClick at" + position, Toast.LENGTH_LONG).show();
} else {
Log.d(LOGCAT, "RecyclerView Item onClick at " + position);
//Toast.makeText(mContext, "RecyclerView Item onClick at " + position, Toast.LENGTH_LONG).show();
}
}
#Override
public boolean onLongClick(View v) {
return false;
}
class ViewHolder extends RecyclerView.ViewHolder {
public TextView tvSubjectName;
public TextView tvCounts;
public ViewHolder(View itemView) {
super(itemView);
tvSubjectName = (TextView) itemView.findViewById(R.id.tv_subject_name);
tvCounts = (TextView) itemView.findViewById(R.id.tv_text_counts);
}
}
}
As one can see, I have tried setOnClickListener with both onCreateViewHolder and onBindViewHolder, also as separate onClick, but none of them seems to be working for me. So, I want to know, How to add OnClickListner to CardView?
View returned by onClick does not necessarily correspond to View row hierarchy of the recycler view.
I think you should modify onBindViewHolder with
#Override
public void onBindViewHolder(final ViewHolder viewHolder, final int i) {
final Subject subject = SubjectsList.get(i);
viewHolder.tvSubjectName.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v) {
Log.d(LOGCAT, "tvSubjectName onClick at" + i);
// etc
}
});
...
}
If you use long click then replace View.OnClickListener with View.OnLongClickListener and onClick with onLongClick.
This worked for me !
Put the setOnClickListener-method inside the constructor of your ViewHolder class.
class ViewHolder extends RecyclerView.ViewHolder
{
public TextView tvSubjectName;
public TextView tvCounts;
public ViewHolder(View itemView)
{
super(itemView);
tvSubjectName = (TextView) itemView.findViewById(R.id.tv_subject_name);
tvCounts = (TextView) itemView.findViewById(R.id.tv_text_counts);
itemView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
//write here the code for wathever you want to do with a card
//...
}
});
}
}
Check out Lucas Rocha's new TwoWayView API.
TwoWayLayoutManager is a simple API on top of LayoutManager that does all the laborious work for you so that you can focus on how the child views are measured, placed, and detached from the RecyclerView.
Follow his site and implementation of the API is relatively simple and might help you through some of the difficult complexities RecyclerView and CardView pose.
http://lucasr.org/2014/07/31/the-new-twowayview/
Set OnClickListener to itemView in RecyclerView.ViewHolder constructor, also you can fetch position of cardView using getAdapterPosition() method which can help you to pass the data to new activity using putExtra method of intent. doc for getAdapterPosition method
`
class ViewHolder extends RecyclerView.ViewHolder
{
public TextView tvSubjectName;
public TextView tvCounts;
public ViewHolder(View itemView)
{
super(itemView);
tvSubjectName = (TextView) itemView.findViewById(R.id.tv_subject_name);
tvCounts = (TextView) itemView.findViewById(R.id.tv_text_counts);
itemView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
int position = getAdapterPosition();
// use position to put extra data
}
});
}
`
If you really want to implement onclick listener then do the following
add a View object in your ViewHolderClass and initialize it to the itemView recieved in the constructor
2.setOnClickListener to the view object you declared(in ViewHolder) onBindViewHolder in the Adapter class
this will work for the entire cardView

Categories

Resources