I am witnessing a wierd behavior of RecyclerView. so I have set an OnClickListener in the ViewHolder on a Button which is meant to do 3 things, when the user clicks:
Add the current item's data to an ArrayList [displayed on a different Fragment]
Display a Toast to the user.
modify the UI of the selected item. [i.e setVisibility of Button to View.INVISIBLE etc..]
now, while the first two tasks of the OnClickListener are fulfilled successfully and apply only to the selected item, the third one which is meant to modify the UI of the selected item only, instead gets also applied to each 9th Item on the RecyclerView.
How do I prevent this behavior?
Why is this behavior occurring with the 3rd task only?
i.e from previous answers on this. particular topic that I saw, I got the notion that it's better to insert the OnClickListener in the ViewHolder rather then putting it in onBindViewHolder, which did not solve the issue. I have followed other suggestions on stackOverflow and all failed.
Here is my code:
static class ItemsRecyclerAdapter extends RecyclerView.Adapter<ItemsRecyclerAdapter.ItemsViewHolder>{
FragmentActivity fragmentActivity;
List<ProductItem> data;
LayoutInflater inflater;
private int id;
private int shopCode;
public ItemsRecyclerAdapter(List<ProductItem> data, FragmentActivity fragmentActivity, int id, int shopCode) {
this.fragmentActivity = fragmentActivity;
this.data = data;
this.inflater = LayoutInflater.from(fragmentActivity);
this.id = id;
this.shopCode = shopCode;
}
#Override
public ItemsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = inflater.inflate(R.layout.pricing_item, parent, false);
return new ItemsViewHolder(v);
}
#Override
public void onBindViewHolder(final ItemsViewHolder holder, int position) {
ProductItem itemPrice = data.get(position);
holder.productName.setText(itemPrice.getProductName());
holder.quantity.setText(itemPrice.getProductQuantity());
holder.tvPrice.setText(itemPrice.getproductPrice());
Picasso.with(fragmentActivity).load(itemPrice.getThumbUrl()).into(holder.imgProduct);
}
#Override
public int getItemCount() {
return data.size();
}
class ItemsViewHolder extends RecyclerView.ViewHolder{
TextView productName, quantity, tvPrice;
ImageView imgProduct;
Button btnAddToBasket, btnAdd, btnRemove;
Typeface myCustomFont = Typeface.createFromAsset(fragmentActivity.getAssets(), "fonts/Assistant-SemiBold.ttf");
public ItemsViewHolder(View itemView) {
super(itemView);
productName = (TextView) itemView.findViewById(R.id.productName);
quantity = (TextView) itemView.findViewById(R.id.manufacturer);
tvPrice = (TextView) itemView.findViewById(R.id.tvPrice);
imgProduct = (ImageView) itemView.findViewById(R.id.imgProduct);
btnAddToBasket = (Button) itemView.findViewById(R.id.btnAddToBasket);
btnAdd = itemView.findViewById(R.id.btnAdd);
btnRemove = itemView.findViewById(R.id.btnRemove);
btnAdd.setVisibility(View.INVISIBLE);
btnRemove.setVisibility(View.INVISIBLE);
quantity.setTypeface(myCustomFont);
productName.setTypeface(myCustomFont);
tvPrice.setTypeface(myCustomFont);
Typeface fontAwesomeBasketIcon = Typeface.createFromAsset(fragmentActivity.getAssets(), "fontawesome-webfont.ttf");
btnAddToBasket.setTypeface(fontAwesomeBasketIcon);
btnAdd.setTypeface(fontAwesomeBasketIcon);
btnRemove.setTypeface(fontAwesomeBasketIcon);
btnAddToBasket.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int pos = getAdapterPosition();
MyBasketFragment.myBasketList
.add(new ProductItem(data.get(pos).getProductName(), data.get(pos).getProductQuantity(),
data.get(pos).getproductPrice(), data.get(pos).getThumbUrl(), id, shopCode));
btnAddToBasket.setOnClickListener(null);
Toast.makeText(fragmentActivity, "New product..", Toast.LENGTH_SHORT).show();
btnAdd.setVisibility(View.VISIBLE);
btnRemove.setVisibility(View.VISIBLE);
}
});
btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ItemsFragment.atomic.incrementAndGet();
btnAddToBasket.setText(String.valueOf(atomic.get()));
}
});
}
}
}
On every Click of an item, I assign a boolean in the instantiated object to true and and then I run notifyDataSetChanged() which. updates all items with data objects which have a boolean true. this way I'm ensuring that only the selected object will be updated.
It looks something like this:
ViewHolder
class ItemsViewHolder extends RecyclerView.ViewHolder {
TextView productName, quantity, tvPrice;
ImageView imgProduct;
Button btnAddToBasket, btnAdd, btnRemove;
Typeface myCustomFont = Typeface.createFromAsset(fragmentActivity.getAssets(), "fonts/Assistant-SemiBold.ttf");
public ItemsViewHolder(View itemView) {
super(itemView);
productName = (TextView) itemView.findViewById(R.id.productName);
quantity = (TextView) itemView.findViewById(R.id.manufacturer);
tvPrice = (TextView) itemView.findViewById(R.id.tvPrice);
imgProduct = (ImageView) itemView.findViewById(R.id.imgProduct);
btnAddToBasket = (Button) itemView.findViewById(R.id.btnAddToBasket);
btnAdd = itemView.findViewById(R.id.btnAdd);
btnRemove = itemView.findViewById(R.id.btnRemove);
btnAdd.setVisibility(View.INVISIBLE);
btnRemove.setVisibility(View.INVISIBLE);
quantity.setTypeface(myCustomFont);
productName.setTypeface(myCustomFont);
tvPrice.setTypeface(myCustomFont);
Typeface fontAwesomeBasketIcon = Typeface.createFromAsset(fragmentActivity.getAssets(), "fontawesome-webfont.ttf");
btnAddToBasket.setTypeface(fontAwesomeBasketIcon);
btnAdd.setTypeface(fontAwesomeBasketIcon);
btnRemove.setTypeface(fontAwesomeBasketIcon);
}
public void bind(final ProductItem item) {
btnAddToBasket.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
item.clicked = true;
item.atomic.incrementAndGet();
Toast.makeText(fragmentActivity, "New product..", Toast.LENGTH_SHORT).show();
notifyDataSetChanged();
MyBasketFragment.myBasketList
.add(new ProductItem(item.getProductName(), item.getProductQuantity(),
item.getproductPrice(), item.getThumbUrl(), id, shopCode));
}
});
btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ItemsFragment.atomic.incrementAndGet();
btnAddToBasket.setText(String.valueOf(atomic.get()));
}
});
}
}
onBindViewHolder
#Override
public void onBindViewHolder(final ItemsViewHolder holder, final int position) {
ProductItem itemPrice = data.get(position);
holder.productName.setText(itemPrice.getProductName());
holder.quantity.setText(itemPrice.getProductQuantity());
holder.tvPrice.setText(itemPrice.getproductPrice());
Picasso.with(fragmentActivity).load(itemPrice.getThumbUrl()).into(holder.imgProduct);
holder.bind(itemPrice);
if(itemPrice.clicked){
holder.btnAddToBasket.setOnClickListener(null);
holder.btnAddToBasket.setText(String.valueOf(atomic.get()));
holder.btnAdd.setVisibility(View.VISIBLE);
holder.btnRemove.setVisibility(View.VISIBLE);
} else {
holder.btnAddToBasket.setText("\uf291");
holder.btnAdd.setVisibility(View.INVISIBLE);
holder.btnRemove.setVisibility(View.INVISIBLE);
}
}
I think getAdapterPosition() is causing the problem. Please, try creating a new int attribute position in the viewholder, onBindViewHolder pass the the position to the viewholder, holder.position = position, and in your viewholder use the position, so will be int pos = position instead of getAdapterPosition()
Related
I wanted to show the number of quantities and price where price = (single item price * the total number of quantities). I understand how to show the items but I'm struggling with how to put buttons for each item and displaying their quantity. How to add + and - for each row and on click on the button calculate the price respectively. My concept is like a shopping cart but it shows the quantity and the + and - buttons and the price.
public class myAdapter extends RecyclerView.Adapter<myAdapter.MyViewHolder> {
String data1[],data2[];
int images [];
Context context;
public myAdapter(Context ct, String s1 [], String s2[], int img[] ){
context = ct;
data1 = s1;
data2 = s2;
images = img;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.my_row,parent,false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
holder.mytext1.setText(data1[position]);
holder.mytext2.setText(data2[position]);
holder.myimage.setImageResource(images[position]);
}
#Override
public int getItemCount() {
return images.length;
}
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView mytext1, mytext2 , quantity;
ImageView myimage , plus , minus;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
mytext1 = itemView.findViewById(R.id.Nama_Pakej);
mytext2 = itemView.findViewById(R.id.Harga);
quantity = itemView.findViewById(R.id.quantity);
myimage = itemView.findViewById(R.id.imageView);
plus = itemView.findViewById(R.id.tambah);
minus = itemView.findViewById(R.id.tolak);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if (itemView == plus){
}
}
}
}
This is my adapter. I'm using card view to display my items.
To achieve "price = (single item price * the total number of quantities)" use a simple method -
public double mul(double quantity, double price) {
return quantity * price;
}
Then use increment and decrement and update the quantity and total price . example- here single item price 100 .
private int increment;
private int decrement;
private String totalprice;
plus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//TODO
//Increment
increment = Integer.parseInt(quantity.getText().toString());
increment++;
quantity. setText(String. valueOf(increment));
//update price
totalprice = String.valueOf(mul(increment, 100));
mytext2.setText(totalprice);
}
});
minus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//TODO
//Decrement
decrement = Integer.parseInt(quantity.getText().toString());
decrement--;
quantity. setText(String. valueOf(decrement));
//update price
totalprice = String.valueOf(mul(decrement, 100));
mytext2.setText(totalprice);
}
});
You can check also my answer to avoid losing data while activity lifecycle changes .
I made a small app that shows me a list of all the apps installed to my phone I want to add a check box to every cardview so that I can mark my favorite apps and other classification. The problem is when scrolling through the list the state of the checkbox change ex. I checked chrome and when I scrolled down then scrolled up chrome is not marked and a random app is checked. I searched for other question related to this problem but none of those solutions worked for me.
updated version of appsadapter.java
public class AppsAdapter extends RecyclerView.Adapter<AppsAdapter.ViewHolder>{
private final SparseBooleanArray array=new SparseBooleanArray();
public class ViewHolder extends RecyclerView.ViewHolder{
public CardView cardView;
public ImageView imageView;
public TextView textView_App_Name;
public CheckBox checkBox;
public ViewHolder (View view){
super(view);
checkBox = (CheckBox) view.findViewById(R.id.chckbox);
cardView = (CardView) view.findViewById(R.id.card_view);
imageView = (ImageView) view.findViewById(R.id.imageview);
textView_App_Name = (TextView) view.findViewById(R.id.Apk_Name);
//textView_App_Package_Name = (TextView) view.findViewById(R.id.Apk_Package_Name);
}
}
private Context context1;
private List<String> stringList;
public AppsAdapter(Context context, List<String> list){
context1 = context;
stringList = list;
}
//viewholder initialized
#Override
public AppsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View view2 = LayoutInflater.from(context1).inflate(R.layout.cardview_layout,parent,false);
ViewHolder viewHolder = new ViewHolder(view2);
return viewHolder;
}
//DATA IS BOUND TO VIEWS
private SparseBooleanArray sba = new SparseBooleanArray();
#Override
public void onBindViewHolder(ViewHolder viewHolder,final int position){
viewHolder.setIsRecyclable(false);
ApkInfoExtractor apkInfoExtractor = new ApkInfoExtractor(context1);
final String ApplicationPackageName = (String) stringList.get(position);
//calling apps name and icon
String ApplicationLabelName = apkInfoExtractor.GetAppName(ApplicationPackageName);
Drawable drawable = apkInfoExtractor.getAppIconByPackageName(ApplicationPackageName);
//setting app name and icon for every card
viewHolder.textView_App_Name.setText(ApplicationLabelName);
viewHolder.imageView.setImageDrawable(drawable);
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
sba.put(position, !sba.get(position));
notifyDataSetChanged();
}
});
viewHolder.checkBox.setChecked(sba.get(position));
/*Adding click listener on CardView to open clicked application directly from here
viewHolder.cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = context1.getPackageManager().getLaunchIntentForPackage(ApplicationPackageName);
if(intent != null){
context1.startActivity(intent);
}
else {
Toast.makeText(context1,ApplicationPackageName + " Error, Please Try Again.", Toast.LENGTH_LONG).show();
}
}
});*/
}
#Override
public int getItemCount(){
return stringList.size();
}
}
That is because in the recycler view, item views are re used to show new data. You have to add a variable in your data item that holds the state of the checkbox at a particular position. Then in the onBindViewHolder you can check the value of the tracking variable and set the state of the checkbox like yourCheckbox.setchecked(item.getSelected()) set the tracking variable value in the onCheckChanged method
set view onclicklistener in BindViewholder method so you can use the current position of item as in
private SparseBooleanArray sba = new SparseBooleanArray();
#Override
public void onBindViewHolder(ViewHolder viewHolder,final int position){
viewHolder.setIsRecyclable(false);
ApkInfoExtractor apkInfoExtractor = new ApkInfoExtractor(context1);
final String ApplicationPackageName = (String) stringList.get(position);
//calling apps name and icon
String ApplicationLabelName = apkInfoExtractor.GetAppName(ApplicationPackageName);
Drawable drawable = apkInfoExtractor.getAppIconByPackageName(ApplicationPackageName);
//setting app name and icon for every card
viewHolder.textView_App_Name.setText(ApplicationLabelName);
viewHolder.imageView.setImageDrawable(drawable);
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
sba.put(position,!sba.get(position));
notifyDataSetChanged();
}
});
viewHolder.checkBox.setChecked(sba.get(position));
}
Set checkbox inside checkBox onclickListener
holder.mCheckBox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
CheckBox checkBox = (CheckBox) view;
mDataModel = (DataModel) checkBox.getTag();
mDataModel.setChecked(checkBox.isChecked());
arrayList.get(position).setChecked(checkBox.isChecked());
}
});
I need to use notifyDataSetChanged() and setText a value to a TextView inside onBindViewHolder. I tried to use both inside onClick method,
qtyIncrease.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
quantityText.setText("Good");
toatlPriceText.setText("250");
notifyDataSetChanged();
}
});
My adapter class,
public class CartCardviewAdapter extends RecyclerView.Adapter<CartCardviewAdapter.MyViewHolder> {
private Context context;
private ArrayList<CartCardviewModel> dataSet;
private ShoppingCart shoppingCart;
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView itemNameText, itemTypeText, itemDescText, unitPriceText, toatlPriceText, quantityText;
ImageView itemImage, removeIcon, qtyIncrease, qtyDecrease;
public MyViewHolder(final View itemView) {
super(itemView);
this.itemNameText = (TextView) itemView.findViewById(R.id.item_name_text);
this.itemTypeText = (TextView) itemView.findViewById(R.id.item_type_text);
this.itemDescText = (TextView) itemView.findViewById(R.id.item_desc_text);
this.unitPriceText = (TextView) itemView.findViewById(R.id.item_unit_price_text);
this.toatlPriceText = (TextView) itemView.findViewById(R.id.item_total_price_text);
this.quantityText = (TextView) itemView.findViewById(R.id.item_quantity_text);
this.itemImage = (ImageView) itemView.findViewById(R.id.item_image);
this.removeIcon = (ImageView) itemView.findViewById(R.id.item_remove_icon);
this.qtyIncrease = (ImageView) itemView.findViewById(R.id.cart_quantity_increase);
this.qtyDecrease = (ImageView) itemView.findViewById(R.id.cart_quantity_decrease);
}
}
public CartCardviewAdapter(ArrayList<CartCardviewModel> data) {
this.dataSet = data;
}
#Override
public CartCardviewAdapter.MyViewHolder onCreateViewHolder(final ViewGroup parent,
int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.cart_cardview_layout, parent, false);
CartCardviewAdapter.MyViewHolder myViewHolder = new CartCardviewAdapter.MyViewHolder(view);
context = parent.getContext();
return myViewHolder;
}
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
public void onBindViewHolder(final CartCardviewAdapter.MyViewHolder holder, final int listPosition) {
shoppingCart = MainActivity.getCartInstance().getCart();
TextView itemNameText = holder.itemNameText;
TextView itemTypeText = holder.itemTypeText;
TextView itemDescText = holder.itemDescText;
TextView unitPriceText = holder.unitPriceText;
final TextView toatlPriceText = holder.toatlPriceText;
final TextView quantityText = holder.quantityText;
ImageView itemImage = holder.itemImage;
ImageView removeIcon = holder.removeIcon;
ImageView qtyIncrease = holder.qtyIncrease;
ImageView qtyDecrease = holder.qtyDecrease;
itemNameText.setText(dataSet.get(listPosition).getItemName());
itemTypeText.setText(dataSet.get(listPosition).getItemType());
itemDescText.setText(dataSet.get(listPosition).getItemDescription());
unitPriceText.setText(String.valueOf(dataSet.get(listPosition).getUnitPrice()));
toatlPriceText.setText(String.valueOf(dataSet.get(listPosition).getTotalPrice()));
String qty;
if (dataSet.get(listPosition).getItemQuantity() < 9) {
qty = "0" + String.valueOf(dataSet.get(listPosition).getItemQuantity());
} else {
qty = String.valueOf(dataSet.get(listPosition).getItemQuantity());
}
quantityText.setText(qty);
Picasso.with(context).load(dataSet.get(listPosition).getItemImageUrl()).into(itemImage);
removeIcon.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Toast.makeText(v.getContext(), String.valueOf(listPosition), Toast.LENGTH_SHORT).show();
shoppingCart.removeItem(listPosition);
dataSet.remove(listPosition);
notifyDataSetChanged();
}
});
qtyIncrease.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
shoppingCart.increaseItemQuantity(listPosition);
quantityText.setText(String.valueOf(shoppingCart.getItemQuantity(listPosition)));
toatlPriceText.setText(String.valueOf(shoppingCart.getTotalPrice(listPosition)));
notifyDataSetChanged();
}
});
qtyDecrease.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (shoppingCart.getItemQuantity(listPosition) == 1) {
Toast.makeText(v.getContext(), "Already Minimum Quantity is Reached", Toast.LENGTH_SHORT).show();
} else {
shoppingCart.decreaseItemQuantity(listPosition);
quantityText.setText(String.valueOf(shoppingCart.getItemQuantity(listPosition)));
toatlPriceText.setText(String.valueOf(shoppingCart.getTotalPrice(listPosition)));
notifyDataSetChanged();
}
}
});
}
#Override
public int getItemCount() {
return dataSet.size();
}
}
but both don't work at a click. I need to go back and come to see the affected change. I could't understand the problem. Please help me
First This line #RequiresApi(api = Build.VERSION_CODES.N) is for too restrictive, take into consideration lower levels down the line. There are many ways to approach this problem, first you can use a RecyclerView.OnItemTouchListener to the recycler hosting the adaptor. Here is a detailed tutorial describing single and long click , Link. Another approach is the interface segration principle.
In the adaptor class, create a interface, with one method
method signature (View view, int position), depicts a view clicked with repective position in adaptor.
Declare a instance of this interface in the adaptor
Modify the contructor to accept a interface of that type
Let the class with the recylcler view instance implement above interface
Pass a new instance to the contructor in the implemented class
Then in the overriden method, change your text and call adaptor.notifyItemChanged(position)
oh, also, in the adaptor class where the views gets init , set onClicklistener with the interface instance.
This tutorial describes exactly the above in great detail,link.
Your welcome!
the click won't trigger from there , you need to set the click listener of the Image View "qtyIncrease" inside the ViewHolder not in "OnBindViewHolder"
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView itemNameText, itemTypeText, itemDescText, unitPriceText, toatlPriceText, quantityText;
ImageView itemImage, removeIcon, qtyIncrease, qtyDecrease;
public MyViewHolder(final View itemView) {
super(itemView);
this.itemNameText = (TextView) itemView.findViewById(R.id.item_name_text);
this.itemTypeText = (TextView) itemView.findViewById(R.id.item_type_text);
this.itemDescText = (TextView) itemView.findViewById(R.id.item_desc_text);
this.unitPriceText = (TextView) itemView.findViewById(R.id.item_unit_price_text);
this.toatlPriceText = (TextView) itemView.findViewById(R.id.item_total_price_text);
this.quantityText = (TextView) itemView.findViewById(R.id.item_quantity_text);
this.itemImage = (ImageView) itemView.findViewById(R.id.item_image);
this.removeIcon = (ImageView) itemView.findViewById(R.id.item_remove_icon);
this.qtyIncrease = (ImageView) itemView.findViewById(R.id.cart_quantity_increase);
this.qtyDecrease = (ImageView) itemView.findViewById(R.id.cart_quantity_decrease);
qtyIncrease.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
quantityText.setText("Good");
toatlPriceText.setText("250");
notifyDataSetChanged();
}
});
}
Recyclerview has 4 items i.e; textView1, textView2, EditText1 and Checkbox1.
Recyclerview has got 24 rows as well. EditText is invisible on initial stage thenit will be visible only when the corresponding checkbox checked by the user. EditText accepts only numbers.
The app is working fine so far this much.
Now I need to get value of all EditTexts and need to display it on another Textview which is not in the part of Recyclerview?
Recyclerview Screenshot- Output Link
Code Samples.
ExamFragment.java
public class ExamFragment extends Fragment {
RecyclerView recyclerView;
ExamFragmentAdapter adapter;
ArrayList<tblChapters> datalistChapters = new ArrayList<>();
TextView txtQcount,txtQCounttotal;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v= inflater.inflate(R.layout.fragment_exam, container, false);
txtQCounttotal=(TextView) v.findViewById(R.id.txtQCounttotal);
txtQcount=(TextView) v.findViewById(R.id.txtQCount);
recyclerView=(RecyclerView)v.findViewById(R.id.recycler_view);
conn = new ConnectionClass(); //connection initialisation
datalistChapters = conn.getChaptersAndCount(modeid, subjectid);
adapter = new ExamFragmentAdapter(datalistChapters, getActivity());
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
recyclerView.setAdapter(adapter);
return v;
}
}
ExamFragmentAdapter.java
public class ExamFragmentAdapter extends RecyclerView.Adapter<ExamFragmentAdapter.MyViewHolder> {
private LayoutInflater inflater;
MyViewHolder holder;
Context mContext;
ArrayList<tblChapters> chapterList=new ArrayList<>();
public ExamFragmentAdapter(ArrayList<tblChapters> chapterList, Context context) {
inflater = LayoutInflater.from(context);
this.chapterList = chapterList;
mContext=context;
}
#Override
public ExamFragmentAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.chapter_list_row, parent, false);
holder = new MyViewHolder(view, new MyCustomEditTextListener());
return holder;
}
#Override
public void onBindViewHolder(final ExamFragmentAdapter.MyViewHolder holder, final int position) {
holder.title.setTextColor(Color.BLACK);
holder.slno.setTextColor(Color.BLACK);
holder.noOfQst.setTextColor(Color.BLACK);
holder.noOfQst.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
holder.noOfQst.setGravity(Gravity.CENTER);
holder.title.setText(chapterList.get(position).getTitle()); // Setting Chapter title
holder.slno.setText(String.valueOf(position + 1)+"."); //Setting sl no
holder._myCustomEditTextListener.updatePosition(position);
holder.noOfQst.setText(chapterList.get(position).getNoofQstns()); //Setting no of qstn
if (chapterList.get(position).isVisibled()) {
holder.noOfQst.setVisibility(View.VISIBLE);
} else {
holder.noOfQst.setVisibility(View.INVISIBLE);
}
//in some cases, it will prevent unwanted situations
holder.cbox.setOnCheckedChangeListener(null);
//if true, your checkbox will be selected, else unselected
holder.cbox.setChecked(chapterList.get(position).isSelected());
holder.cbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//set your object's last status
chapterList.get(position).setSelected(isChecked);
chapterList.get(position).setVisibled(isChecked);
//if checkbox checked display EditText(No of qstns), else hide it.
if (holder.cbox.isChecked()) {
holder.noOfQst.setVisibility(View.VISIBLE);
holder.noOfQst.requestFocus();
holder.noOfQst.setText("10");
chapterList.get(position).setNoofQstns(holder.noOfQst.getText().toString());
/* txtQcount.setText("0");
if (txtQcount.getText().toString().equals("")) {
txtQcount.setText("0");
}
txtQcount.setText(Integer.valueOf(txtQcount.getText().toString())+Integer.parseInt(holder.noOfQst.getText().toString()));*/
}
else {
holder.noOfQst.setVisibility(View.INVISIBLE);
holder.noOfQst.setText(""); //remove entered value when uncheck
chapterList.get(position).setNoofQstns("");
}
}
});
}
// we make TextWatcher to be aware of the position it currently works with
// this way, once a new item is attached in onBindViewHolder, it will
// update current position MyCustomEditTextListener, reference to which is kept by ViewHolder
private class MyCustomEditTextListener implements TextWatcher
{
private int position;
private String oldval;
public void updatePosition(int position) {
this.position = position;
}
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
// no op
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
chapterList.get(position).setNoofQstns(charSequence.toString());
int j = i;
j = i2;
j = i3;
}
#Override
public void afterTextChanged(Editable editable) {
}
}
#Override
public int getItemCount() {
return chapterList.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
TextView title;
CheckBox cbox;
TextView slno;
EditText noOfQst;
public MyCustomEditTextListener _myCustomEditTextListener;
public MyViewHolder(View itemView,MyCustomEditTextListener myCustomEditTextListener) {
super(itemView);
title = (TextView) itemView.findViewById(R.id.txtTitle);
cbox = (CheckBox) itemView.findViewById(R.id.cboxChapter);
slno = (TextView) itemView.findViewById(R.id.txtRowSlno);
noOfQst = (EditText) itemView.findViewById(R.id.etNoOfQstns);
this._myCustomEditTextListener = myCustomEditTextListener;
try {
if (noOfQst.getVisibility() == View.VISIBLE) {
holder.noOfQst.setVisibility(View.INVISIBLE);
//adding textchange listener to no of qstn(EditText)
noOfQst.addTextChangedListener(myCustomEditTextListener);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
You can register an AdapterDataObserver for the recycler view adapter.
For each item in the adapter when it's updated (user updates the EditText), the adapter can call notifyItemChanged(int position, Object payload). the observer will receive the payload in its onItemRangeChanged (int positionStart, int itemCount, Object payload) callback, you can pass whatever you want in the payload object so that you accumulate the values of all the EditTexts.
You can also unregister the observer in your Activity/Fragment onStop by calling unregisterAdapterDataObserver
I'm using RecyclerView, I have one ImageButton as Child view, I'm changing the background of that ImageButton on click.
Now my problem is when change Image of ImageButton by clicking and than scroll up again scroll to the top, the state of set to the initial stage. I tried everything but its not happening. help me with that.
My Adapter class
public class ViewAllAdapter extends RecyclerView.Adapter<ProductHolder> {
int[] objects;
Context context;
Boolean flag = false;
public ViewAllAdapter(int[] objects, Context context) {
super();
this.objects = objects;
this.context = context;
}
#Override
public int getItemCount() {
return objects.length;
}
#Override
public void onBindViewHolder(final ProductHolder arg0, int arg1) {
arg0.title.setText("Product" + arg1 + 1);
arg0.aPrice.setPaintFlags(arg0.aPrice.getPaintFlags()
| Paint.STRIKE_THRU_TEXT_FLAG);
arg0.aPrice.setText("\u20B9" + " " + "5000");
arg0.off.setText("56% off");
arg0.price.setText("\u20B9" + " " + "2300");
arg0.mainImage.setImageResource(objects[arg1]);
arg0.ratings.setRating(4f);
arg0.clickme.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this,
ProductDetailsPage.class);
startActivity(i);
}
});
arg0.wish.setBackgroundResource(R.drawable.heart);
arg0.wish.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (!flag) {
arg0.wish.setBackgroundResource(R.drawable.heartfade);
YoYo.with(Techniques.Tada).duration(550)
.playOn(arg0.wish);
Toast.makeText(context, "Product Added to wish list..",
Toast.LENGTH_SHORT).show();
flag = true;
} else {
arg0.wish.setBackgroundResource(R.drawable.heart);
Toast.makeText(context,
"Product Removed to wish list..",
Toast.LENGTH_SHORT).show();
flag = false;
}
}
});
}
#Override
public ProductHolder onCreateViewHolder(ViewGroup arg0, int arg1) {
LayoutInflater inflater = LayoutInflater.from(arg0.getContext());
View view = inflater.inflate(R.layout.viewall_item, arg0, false);
return new ProductHolder(view);
}
public class ProductHolder extends RecyclerView.ViewHolder {
protected TextView title;
protected TextView aPrice;
protected TextView off;
protected TextView price;
protected ImageView mainImage;
protected RatingBar ratings;
protected ImageButton wish;
protected RelativeLayout clickme;
public ProductHolder(View itemView) {
super(itemView);
title = (TextView) itemView.findViewById(R.id.productName);
aPrice = (TextView) itemView
.findViewById(R.id.productActualPrice);
off = (TextView) itemView.findViewById(R.id.offPrice);
price = (TextView) itemView.findViewById(R.id.productPrice);
mainImage = (ImageView) itemView
.findViewById(R.id.productImage);
wish = (ImageButton) itemView.findViewById(R.id.addToWishList);
ratings = (RatingBar) itemView
.findViewById(R.id.productRatings);
clickme = (RelativeLayout) itemView
.findViewById(R.id.clickToGO);
}
}
}
And in My MainActicity I'm doing
rv = (RecyclerView) findViewById(R.id.gvViewAll);
rv.setHasFixedSize(true);
GridLayoutManager glm = new GridLayoutManager(getApplicationContext(),
2);
rv.setLayoutManager(glm);
final ViewAllAdapter adp = new ViewAllAdapter(productImage,
getApplicationContext());
rv.setAdapter(adp);
rvh = (RecyclerViewHeader) findViewById(R.id.header);
rvh.attachTo(rv, true);
Thanks for any help.
As per your Custom RecyclerView adapter, I suggest you to use HashMap to store Values. After all Data Model is best option to load data in to RecyclerView.
HashMap<Integer, Integer> hashMapTest = new HashMap<>();
When user click on ImageButton then add following code:
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(hashMapTest.get(position) != null && hashMapTest.get(position) != false) {
// Code of Product Added to wish list..
hashMapTest.put(getPosition(), 1);
} else {
// Code of Product Removed to wish list..
hashMapTest.put(getPosition(), 0);
}
}
});
In your onBindViewHolder(ProductHolder holder, int position) add following code
if(hashMapTest.get(position) != null && hashMapTest.get(position) != false) {
// Show your ImageButton color Product Added to wish list..
} else {
// Show your ImageButton color Product Removed to wish list..
}
Hope it helps you.