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();
Related
I have an app which has a recyclerview. I want to give an opportunity to the users to switch between Night and Day mode theme. I know how to change text color and background color, but in this case I can't. Actually I am unable to find the item layout variables from MainActivity. How to create an object of ViewHolder class from MainActivity? Can anyone please help me?
Here is my Adapter class:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
private List<ListItemModel> listItems;
private Context context;
public MyAdapter(List<ListItemModel> listItems, Context context) {
this.listItems = listItems;
this.context = context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_model, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
final ListItemModel listItem = listItems.get(position);
holder.index_number.setText(listItem.getIndexNumber());
holder.title_name.setText(listItem.getTitle());
}
#Override
public int getItemCount() {
return listItems.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView index_number, title_name;
public ViewHolder(View itemView) {
super(itemView);
index_number = (TextView) itemView.findViewById(R.id.model_text_index_id);
title_name = (TextView) itemView.findViewById(R.id.model_text_title_id);
}
}
}
If its a single choice list, you can define an int parameter in your list and define a method in adapter
....
private int selectedPosition = -1;
public void setSelectedPosition(int index){
selectedPosition = index;
notifyItemChanged(selectedPosition)
}
then in OnBindViewHolder do this:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
final ListItemModel listItem = listItems.get(position);
holder.index_number.setText(listItem.getIndexNumber());
holder.title_name.setText(listItem.getTitle());
if(position == selectedPosition){
holder.index_number.setTextColor(MY_COLOR)
holder.title_name.setTextColor(MY_COLOR)
} else {
holder.index_number.setTextColor(NORMAL_COLOR)
holder.title_name.setTextColor(NORMAL_COLOR)
}
}
For multi selected list, you can define a parameter like isChosen in your ListItemModel and change that parameter to true and false and in your OnBindViewHolder check that parameter
I found the solution of your problem
Create a method inside your activity and call when click on item in adapter and send position
public void click(int position) {
TestAdapter.ViewHolder viewHolder = (TestAdapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(position);
viewHolder.text_color.setTextColor(Color.parseColor("#245251"));
}
Hope this will help you.
I've implemented a recyclerview with staggered gridLayout containing about 31 items in the arrayList, recyclerview is working correctly, but I faced issue relating to single item selection.
When I select the value till "26" as shown in figure, its working fine
But, when I select the value after "26", the values from top most item are also selected, as shown in this next figure.
I require to only select one item at a time.
I've implemented the following code in my adapter class
public class DialogAdapter extends
RecyclerView.Adapter<DialogAdapter.DialogHolder>
{
// components
public Context context;
public ArrayList<AlertDialogModel> dialogArrayList = new
ArrayList<AlertDialogModel>();
private final ArrayList<Integer> selected = new ArrayList<>();
private int lastCheckedPosition = -1;
public Interface interface;
// parameterized constructor
public DialogAdapter(Context context, ArrayList<AlertDialogModel>
dialogArrayList,Interface interface)
{
this.context = context;
this.dialogArrayList = dialogArrayList;
this.interface = interface;
}
#NonNull
#Override
public DialogHolder onCreateViewHolder(#NonNull ViewGroup parent, int
viewType)
{
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.custom_cardview,parent,false);
DialogHolder dialogHolder = new DialogHolder(view);
return dialogHolder;
}
#Override
public void onBindViewHolder(#NonNull final DialogHolder holder, final int position)
{
final AlertDialogModel alertDialogModel = dialogArrayList.get(position);
holder.textView.setText(alertDialogModel.getDisplayValue());
if(lastCheckedPosition == position)
{
holder.textView.setTextColor(context.getResources().getColor(R.color.white));
holder.textView.setBackground(context.getResources().getDrawable(R.drawable.circular_shape_selection));
}
else
{
}
holder.textView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
lastCheckedPosition = position;
notifyDataSetChanged();
holder.textView.setTextColor(context.getResources().getColor(R.color.white));
holder.textView.setBackground(context.getResources().getDrawable(R.drawable.circular_shape_selection));
interface.getSelectedValue(alertDialogModel.getDisplayValue());
}
});
}
#Override
public int getItemCount()
{
return dialogArrayList.size();
}
public static class DialogHolder extends RecyclerView.ViewHolder
{
public TextView textView;
public DialogHolder(View itemView)
{
super(itemView);
textView = (TextView)itemView.findViewById(R.id.textView);
}
}
}
Can anyone relate my code and identify the issue ?
holder.textView.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
lastCheckedPosition = position;
notifyDataSetChanged();
holder.textView.setTextColor(context.getResources().getColor(R.color.white));
holder.textView.setBackground(context.getResources().getDrawable(R.drawable.circular_shape_selection));
interface.getSelectedValue(alertDialogModel.getDisplayValue());
//below line is important to remove previous selected position from the variable
lastCheckedPosition = -1;
}
});
you should put the text view to the original state:
if(lastCheckedPosition == position)
{
holder.textView.setTextColor(context.getResources().getColor(R.color.white));
holder.textView.setBackground(context.getResources().getDrawable(R.drawable.circular_shape_selection));
}
else
{
holder.textView.setTextColor(context.getResources().getColor(R.color.transparent));
holder.textView.setBackground(null));
}
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
Imagine a CardView with a button and text field. When I press on a button, text should change in the textfield. Code for my recyclerView is given below, where I am setting the text and a listener for a button:
#Override
public void onBindViewHolder(CategoriesDetailedViewHolder holder, int position) {
final CategoryDetailedModel model = categoriesDetailedModels.get(position);
holder.description.setText(model.getQuestion());
holder.btnPlus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
holder.description.setText("some text");
}
});
}
The main problem is with the string:
holder.description.setText("some text");
It cannot be accessed from an inner class.
Is anyone have any idea how to change text in card view from itself.
Update 1
public class CategoriesDetailedViewHolder extends RecyclerView.ViewHolder{
CardView cv;
TextView description;
TextView positiveCounter;
TextView negativeCounter;
TextView btnPlus;
View negativeCounterWrapper;
View positiveCounterWrapper;
CategoriesDetailedViewHolder(View itemView) {
super(itemView);
cv = (CardView)itemView.findViewById(R.id.cv);
description = (TextView)itemView.findViewById(R.id.main_text);
positiveCounter = (TextView)itemView.findViewById(R.id.tv_positive_count_in_circle);
negativeCounter = (TextView)itemView.findViewById(R.id.tv_negative_count_in_circle);
btnPlus = (TextView)itemView.findViewById(R.id.plus_button);
positiveCounterWrapper = itemView.findViewById(R.id.positive_count_wrapper);
negativeCounterWrapper = itemView.findViewById(R.id.negative_count_wrapper);
cv.setPreventCornerOverlap(false);
}
}
#Override
public CategoriesDetailedViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.categories_detailed_list_row_view, parent, false);
CategoriesDetailedViewHolder pvh = new CategoriesDetailedViewHolder(v);
context = parent.getContext();
return pvh;
}
Instead of,
TextView description;
Make it,
public TextView description;
EDIT:
Change the following,
#Override
public void onBindViewHolder(CategoriesDetailedViewHolder holder, int position) { ... }
to,
#Override
public void onBindViewHolder( final CategoriesDetailedViewHolder holder, int position) { .... }
I use this code and it works fine, you just need to use onBind method.
This is one example, I use this in my project and you can just change it to suit your needs:
public void onBindViewHolder(final TipsViewHolder TipsViewHolder, final int i) {
TipsViewHolder.cardText.setText("Old text");
TipsViewHolder.txtBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TipsViewHolder.cardText.setText("New Text");
}
});
}
I have a recyclerview in which onLongClick() of an item, I am showing a button. But when scroll down the recycler view and scrolling back, that button is showing on top of another item or sometimes it is not showing at all.
Here is my code
public static class TextViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,View.OnLongClickListener{
public LinearLayout enq_layout;
public LinearLayout item_layout;
public TextView enquire;
public int position;
public TextViewHolder(View itemView) {
super(itemView);
item_layout= (LinearLayout) itemView.findViewById(R.id.item_layout);
enq_layout= (LinearLayout) itemView.findViewById(R.id.enq_layout);
enquire=(TextView) itemView.findViewById(R.id.enquire);
//position=getLayoutPosition();
}
}
#Override
public int getItemViewType(int position) {
return product.get(position)!=null? VIEW_ITEM: VIEW_PROG;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.cardlayout_product, parent, false);
RecyclerView.ViewHolder vh = new TextViewHolder(v);
return vh;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// Toast.makeText(act, "onBindViewHolder" +position, Toast.LENGTH_LONG).show();
final ProductDetails item = product.get(position);
final TextViewHolder hold=((TextViewHolder)holder);
//hold.position=position;
// hold.item_layout.setTag(position);
hold.item_layout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// go to next activity
}
});
hold.item_layout.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
// show enquiry button
hold.enq_layout.setVisibility(View.VISIBLE);
}
});
hold.enquire.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//do some operation
int productid = Integer.parseInt(product.get(item.getPosition()).getProduct_id());
}
});
}
#Override
public int getItemCount() {
return product.size();
}
I tried this way Why doesn't RecyclerView have onItemClickListener()? And how RecyclerView is different from Listview?, but I am not able to access the views inside onCreateViewHolder's onclick methods.
I had the same problem and solved this. You need to use the method of RecyclerView and set the items cache size:
mRecyclerView.setItemViewCacheSize(300);
You are going to have to create a global variable called for instance boolean shouldShowView like this:
public static class TextViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,View.OnLongClickListener{
public LinearLayout enq_layout;
private boolean shouldShowView;
Then in onBindViewHolder use can use the following code:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// Toast.makeText(act, "onBindViewHolder" +position, Toast.LENGTH_LONG).show();
final ProductDetails item = product.get(position);
final TextViewHolder hold=((TextViewHolder)holder);
if (shouldShowView) {
hold.enq_layout.setVisibility(View.VISIBLE);
} else {
hold.enq_layout.setVisibility(View.INVISIBLE);
}
Then in your onLongClick method just set the shouldShowView variable to true if there was a long click. You should also set it to false somewhere else when you want to hide the button. You might also have to keep track of which item was selected in global variable state and check whether the current position is equal to the selected position.
You might also want to move the shouldShowView check below the click listeners.
Let me know if you have any questions.
you can try this
public View getViewByPosition(int position, ListView lv1) {
final int firstListItemPosition = lv1.getFirstVisiblePosition();
final int lastListItemPosition = firstListItemPosition +
lv1.getChildCount() - 1;
if (position < firstListItemPosition || position >
lastListItemPosition ) {
return lv1.getAdapter().getView(position, null, lv1);
} else {
final int childIndex = position - firstListItemPosition;
return lv1.getChildAt(childIndex);
}
}
And in your declare it like this
selectedids ="";
for (int i = 0; i < len; i++){
//View childv = lv1.getChildAt(i);
View childv=getViewByPosition(i, lv1);