Related
I am using the ItemTouchHelper class to support drag and drop in my RecyclerView. While I am dragging an item around it visually updates (swaps rows) as expected. Once I drop the item, another **visual** drag occurs. For example (see diagram below) if I drag item "a" from index 0 to index 3, the correct list shows that item "b" is now at index 0. They recycler view repeats the operation and takes the new item at index 0 ("b") and drags it to index 3! This repeated drag happens no matter what index I drag from or to.
I called it a **visual** drag because the list I am submitting to my RecyclerView's ListAdapter is correctly ordered (verified by logs). And if I restart my app the list is in the correct order. Or if I call notifyDataSetChanged(), after the unwanted animation, it will order itself properly. What could be causing this second animation?
EDIT: According to the documentation, if you use equals() method in your areContentsTheSame() method (DiffUtil), "Incorrectly returning false here will result in too many animations." As far as I can tell, I am properly overriding this method in my POJO file below. I am stumped...
MainActivity.java
private void setListObserver() {
viewModel.getAllItems().observe(this, new Observer<List<ListItem>>() {
#Override
// I have verified newList has the correct order through log statements
public void onChanged(List<ListItem> newList) {
adapterMain.submitList(newList);
}
});
}
...
// This method is called when item starts dragging
public void onSelectedChanged(#Nullable RecyclerView.ViewHolder viewHolder, int actionState) {
...
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
currentList = new ArrayList<>(adapterMain.getCurrentList()); // get current list from adapter
}
...
}
// This method is called when item is dropped
public void clearView(#NonNull RecyclerView recyclerView,
#NonNull RecyclerView.ViewHolder viewHolder) {
...
// I have verified that all code in this method is returning correct values through log statements.
// If I restart the app, everything is in the correct order
// this is position of the where the item was dragged to, gets its value from the onMoved method.
// it's the last "toPos" value in onMoved() after the item is dropped
int position = toPosition;
// Skip this code if item was deleted (indicated by -1). Otherwise, update the moved item
if(position != -1) {
ListItem movedItem = currentList.get(position);
// If dragged to the beginning of the list, subtract 1 from the previously lowest
// positionInList value (the item below it) and assign it the moved item. This will ensure
// that it now has the lowest positionInList value and will be ordered first.
if(position == 0) {
itemAfterPos = currentList.get(position + 1).getPositionInList();
movedItemNewPos = itemAfterPos - 1;
// If dragged to the end of list, add 1 to the positionInList value of the previously
// largest value and assign to the moved item so it will be ordered last.
} else if (position == (currentList.size() - 1)) {
itemBeforePos = currentList.get(position - 1).getPositionInList();
movedItemNewPos = itemBeforePos + 1;
// If dragged somewhere in the middle of list, get the positionInList variable value of
// the items before and after it. They are used to compute the moved item's new
// positionInList value.
} else {
itemBeforePos = currentList.get(position - 1).getPositionInList();
itemAfterPos = currentList.get(position + 1).getPositionInList();
// Calculates the moved item's positionInList variable to be half way between the
// item above it and item below it
movedItemNewPos = itemBeforePos + ((itemAfterPos - itemBeforePos) / 2.0);
}
updateItemPosInDb(movedItem, movedItemNewPos);
}
private void updateItemPosInDb(ListItem movedItem, double movedItemNewPos) {
movedItem.setPositionInList(movedItemNewPos);
viewModel.update(movedItem); // this updates the database which triggers the onChanged method
}
public void onMoved(#NonNull RecyclerView recyclerView,
#NonNull RecyclerView.ViewHolder source, int fromPos,
#NonNull RecyclerView.ViewHolder target, int toPos, int x, int y) {
Collections.swap(currentList, toPos, fromPos);
toPosition = toPos; // used in clearView()
adapterMain.notifyItemMoved(fromPos, toPos);
}
}).attachToRecyclerView(recyclerMain);
RecyclerAdapterMain.java
public class RecyclerAdapterMain extends ListAdapter<ListItem, RecyclerAdapterMain.ListItemHolder> {
// Registers MainActivity as a listener to checkbox clicks. Main will update database accordingly.
private CheckBoxListener checkBoxListener;
public interface CheckBoxListener {
void onCheckBoxClicked(ListItem item); // Method implemented in MainActivity
}
public void setCheckBoxListener(CheckBoxListener checkBoxListener) {
this.checkBoxListener = checkBoxListener;
}
public RecyclerAdapterMain() {
super(DIFF_CALLBACK);
}
// Static keyword makes DIFF_CALLBACK variable available to the constructor when it is called
// DiffUtil will compare two objects to determine if updates are needed
private static final DiffUtil.ItemCallback<ListItem> DIFF_CALLBACK =
new DiffUtil.ItemCallback<ListItem>() {
#Override
public boolean areItemsTheSame(#NonNull ListItem oldItem, #NonNull ListItem newItem) {
return oldItem.getId() == newItem.getId();
}
// Documentation - NOTE: if you use equals, your object must properly override Object#equals().
// Incorrectly returning false here will result in too many animations.
// As far as I can tell I am overriding the equals() properly in my POJO below
#Override
public boolean areContentsTheSame(#NonNull ListItem oldItem, #NonNull ListItem newItem) {
return oldItem.equals(newItem);
}
};
#NonNull
#Override
public ListItemHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_item_layout_main, parent, false);
return new ListItemHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ListItemHolder holder, int position) {
ListItem item = getItem(position);
Resources resources = holder.itemView.getContext().getResources();
holder.txtItemName.setText(item.getItemName());
holder.checkBox.setChecked(item.getIsChecked());
// Set the item to "greyed out" if checkbox is checked, normal color otherwise
if(item.getIsChecked()) {
holder.txtItemName.setTextColor(Color.LTGRAY);
holder.checkBox.setButtonTintList(ColorStateList
.valueOf(resources.getColor(R.color.checkedColor, null)));
} else {
holder.txtItemName.setTextColor(Color.BLACK);
holder.checkBox.setButtonTintList(ColorStateList
.valueOf(resources.getColor(R.color.uncheckedColor, null)));
}
}
public class ListItemHolder extends RecyclerView.ViewHolder {
private TextView txtItemName;
private CheckBox checkBox;
public ListItemHolder(#NonNull View itemView) {
super(itemView);
txtItemName = itemView.findViewById(R.id.txt_item_name);
// Toggle checkbox state
checkBox = itemView.findViewById(R.id.checkBox);
checkBox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
checkBoxListener.onCheckBoxClicked(getItem(getAdapterPosition()));
}
});
}
}
public ListItem getItemAt(int position) {
return getItem(position);
}
}
ListItem.java (POJO)
#Entity(tableName = "list_item_table")
public class ListItem {
#PrimaryKey(autoGenerate = true)
private long id;
private String itemName;
private boolean isChecked;
private double positionInList;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public void setChecked(boolean isChecked) {
this.isChecked = isChecked;
}
public boolean getIsChecked() {
return isChecked;
}
public void setPositionInList(double positionInList) {
this.positionInList = positionInList;
}
public double getPositionInList() {
return positionInList;
}
#Override
public boolean equals(#Nullable Object obj) {
ListItem item = new ListItem();
if(obj instanceof ListItem) {
item = (ListItem) obj;
}
return this.getItemName().equals(item.getItemName()) &&
this.getIsChecked() == item.getIsChecked() &&
this.getPositionInList() == item.getPositionInList();
}
}
My problem is how to sum all checked items values, and how to manage their changed at check/uncheck events?
This is my method for check box toggle (check/uncheck) event.
holder.itemCheckBox.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
if (((CheckBox) v).isChecked())
{
double price = Double.parseDouble(catalogDatabases.get(position).getPriceItem());
holder.totalPrice = holder.totalPrice + price;
holder.listener.respond(holder.totalPrice);
}
else {
}
}
});
So first of all don't rely on the view as data.
I'll explain in your code
if (((CheckBox) v).isChecked())
You relying on view's isChecked method.
This may cause a bug because of android list recycling mechanism.
You can read about it in the link below
How ListView's recycling mechanism works
TL;DR views are being recycled so when you scroll your list.
So for example the user checked item number 3 and then scrolled the list down item number 13 for example may also be shown as checked even tho it isn't .
So when onClick triggers we need to save the checked state in some list
After the theoretical intro i'll show it in code.
//Here you'll need to create some boolean array or list to store
//checked not checked positions lets call it checked
boolean[] checkedArr = new boolean[catalogDatabases.size()];
// catalogDatabases.size represents your data set size
// note that all items will be false initially
#Override
public void onBindViewHolder(#NonNull final MyViewHolder holder, final int position) {
/**
* Other views binding.......
*/
holder.itemCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
checkedArr[position] = isChecked;
sumAllCheckedAndNotify();
}
}
);
}
(I decided to do the calculation on every check it more straight forward from making an update on the event)
private void sumAllCheckedAndNotify() {
double sum = 0;
for (int i = 0; i < checkedArr.length; i++) {
if(checkedArr[i]) {
sum += Double.parseDouble(catalogDatabases.get(i).getPriceItem());
}
}
// pudate the listener
listener.respond(sum, selectedCount);
}
Store totalPrice inside adapter and add/subtract value based on CheckBox state like below:
class YourAdapter extends ... {
double totalPrice = 0;
...
#Override
public void onBindViewHolder(#NonNull RecyclerHolder holder, int position) {
holder.itemCheckBox.setChecked(catalogDatabases.get(position).isSelected());
holder.itemCheckBox.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
double price = Double.parseDouble(catalogDatabases.get(position).getPriceItem());
boolean isSelected = !catalogDatabases.get(position).isSelected();
catalogDatabases.get(position).setSelected(isSelected);
if (isSelected) {
totalPrice += price;
} else {
totalPrice -= price;
}
holder.listener.respond(totalPrice);
notifyDataSetChanged();
}
});
}
}
Update your Model to hold checked/ unchecked state to persist data
class CatalogDatabase { //Assume this is your model class
private boolean isSelected
....
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean selected) {
isSelected = selected;
}
....
}
I really need your help. I've searched Google many days with many keywords, but I couldn't get it. So, I decided to ask to you.
So, here it is. Actually, I have one button in RecyclerView, but this button is repeated as much amount of data available, there are: Button with text "Baca 3x", "Baca 4x", and so on. I want, if I click button with text "Baca 3x" 3 times, it will change to "Baca 2x" >> "Baca 1x" >> remove item. Also if I click button with text "Baca 4x" 4 times, it will change to "Baca 3x" >> "Baca 2x" >> "Baca 1x" >> remove item.
But my problem is, I can't treat every button with different treatment, because every time the item has been deleted, position of data changes automatically. Because of this, I can't get specific button. For example: There is two button,
1. Button "Baca 3x" on position 0
2. Button "Baca 4x" on position 1
If button "Baca 3x" on position 0 has been deleted, so button "Baca 4x" changed it's position automatically to 0. The problem lays here.
Until now I just get every button based on their positions, which is a problem for me. Because of this I am thinking about How to Delete Item Without Deleting Position in Recycler View? Can you guys solve my problem? Should I use DiffUtil?And how to use it? Below the complete code I use:
ModelDoa.java
public class ModelDoa {
public static final int DOA_PAGI = 0;
public static final int DOA_SORE = 1;
public static final int DOA_MASJID = 2;
public static final int DOA_BANGUNT = 3;
public static final int DOA_MAU_TIDUR = 4;
private String mName;
private String bName;
private int mType;
public ModelDoa(String name, String butong, int type) {
this.mName = name;
this.bName = butong;
this.mType = type;
}
public String getName() {
return mName;
}
public void setName(String name) {
this.mName = name;
}
public int getType() {
return mType;
}
public void setType(int type) { this.mType = type; }
public String ambilName() {
return bName;
}
public void setNama(String butonk) {
this.bName = butonk;
}
}
AdapterDoa.java
public class AdapterDoa extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public List<ModelDoa> mList;
public AdapterDoa(List<ModelDoa> list) {
this.mList = list;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case DOA_PAGI:
View vieu = LayoutInflater.from(parent.getContext()).inflate(R.layout.content_doa, parent, false);
PagiViewHolder rcv = new PagiViewHolder(vieu, this);
return rcv;
case DOA_SORE:
View doa = LayoutInflater.from(parent.getContext()).inflate(R.layout.content_doa, parent, false);
SoreViewHolder mdoa = new SoreViewHolder(doa);
return mdoa;
case DOA_MASJID:
View dMasjid = LayoutInflater.from(parent.getContext()).inflate(R.layout.content_doa, parent, false);
MasjidViewHolder mMasjid = new MasjidViewHolder(dMasjid);
return mMasjid;
case DOA_BANGUNT:
View dBangunt = LayoutInflater.from(parent.getContext()).inflate(R.layout.content_doa, parent, false);
BanguntViewHolder mBangunt = new BanguntViewHolder(dBangunt);
return mBangunt;
case DOA_MAU_TIDUR:
View regut = LayoutInflater.from(parent.getContext()).inflate(R.layout.content_doa, parent, false);
MauTidurViewHolder turu = new MauTidurViewHolder(regut);
return turu;
}
return null;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
ModelDoa object = mList.get(position);
if (object != null) {
switch (object.getType()) {
case DOA_PAGI:
((PagiViewHolder) holder).mTitle.setText(object.getName());
((PagiViewHolder) holder).tombolbaca.setText(object.ambilName());
break;
case DOA_SORE:
((SoreViewHolder) holder).mTitle.setText(object.getName());
((SoreViewHolder) holder).tombolbaca.setText(object.ambilName());
break;
case DOA_MASJID:
((MasjidViewHolder) holder).mTitle.setText(object.getName());
((MasjidViewHolder) holder).tombolbaca.setText(object.ambilName());
break;
case DOA_BANGUNT:
((BanguntViewHolder) holder).mTitle.setText(object.getName());
((BanguntViewHolder) holder).tombolbaca.setText(object.ambilName());
break;
case DOA_MAU_TIDUR:
((MauTidurViewHolder) holder).mTitle.setText(object.getName());
((MauTidurViewHolder) holder).tombolbaca.setText(object.ambilName());
break;
}
}
}
public void deleteItem(int position) {
mList.remove(position); // hapus list
notifyItemRemoved(position); // hapus tampilan
// notifyItemRangeChanged( position, mList.size());
}
#Override
public int getItemCount() {
if (mList == null)
return 0;
return mList.size();
}
#Override
public int getItemViewType(int position) {
if (mList != null) {
ModelDoa object = mList.get(position);
if (object != null) {
return object.getType();
}
}
return 0;
}
}
PagiViewHolder.java
public class PagiViewHolder extends RecyclerView.ViewHolder {
public TextView mTitle;
public Button tombolbaca;
public Button teksbaca;
public Button tombolshare;
private RelativeLayout rl2;
private int klik10 = 10;
private AdapterDoa myAdapter;
public PagiViewHolder(View itemView, AdapterDoa myAdapter) {
super(itemView);
this.myAdapter = myAdapter;
itemView.setOnClickListener(mainViewClickListener);
mTitle = (TextView) itemView.findViewById(R.id.titleTextView);
tombolbaca = (Button) itemView.findViewById(R.id.buttonbaca);
tombolshare = (Button) itemView.findViewById(R.id.buttonshare);
tombolbaca.setOnClickListener(bacaClickListener);
tombolshare.setOnClickListener(shareClickListener);
rl2 = (RelativeLayout) itemView.findViewById(R.id.relmasjid);
}
private View.OnClickListener bacaClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
teksbaca = (Button) v.findViewById(R.id.buttonbaca);
// Baca 10x
if( getAdapterPosition() ==0 ) {
klik10--;
teksbaca.setText("Baca " + klik10 + "x");
if (klik10 <= 0)
{
// modify listItems however you want... add, delete, shuffle, etc
myAdapter.deleteItem(getAdapterPosition());
}
}
} // onclick
};
private View.OnClickListener shareClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
// Do button click handling here
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, mTitle.getText().toString() + "\n \n download aplikasinya di: http://www.tauhid.or.id" );
sendIntent.setType("text/plain");
Intent.createChooser(sendIntent,"Share via");
v.getContext().startActivity(sendIntent);
}
};
private View.OnClickListener mainViewClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
// Do button click handling here
}
};
}
DoaPagi.java
public class DoaPagi extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_doa_pagi);
// toolbar
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//this line shows back button
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
List<ModelDoa> rowListItem = getData();
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(DoaPagi.this);
RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mRecyclerView.setLayoutManager(linearLayoutManager);
mRecyclerView.setHasFixedSize(true);
AdapterDoa rcAdapter = new AdapterDoa(rowListItem);
mRecyclerView.setAdapter(rcAdapter);
}
private List<ModelDoa> getData() {
String[] data = getResources().getStringArray(R.array.doapagi);
String[] baca = getResources().getStringArray(R.array.bacapagi);
List<ModelDoa> list = new ArrayList<ModelDoa>();
for (int i = 0; i < data.length; i++) {
list.add(new ModelDoa(data[i], baca[i], ModelDoa.DOA_PAGI));
}
return list;
}
// Agar back button pada halaman induk settings berfungsi
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
this.finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}
UPDATE (FIX CODE) By: Krishna Sharma:
https://github.com/seadclark/RecyclerViewWithButtonClicks
Here is the fix. just update the ModelDoa constructor as below. I have verified myself and working as expected now. Also sent you pull request on github.
public ModelDoa(String name, String butong, int type) {
this.mName = name;
this.bName = butong;
this.mType = type;
String[] data = butong.split("\\s");
if (data.length > 0) {
String count = data[1].substring(0, data[1].length() - 1);
read10 = Integer.parseInt(count);
}
}
Instead of removing the item from your list AND updating the interface, have two methods. One of them (deleteItem) will only delete the item and the other (deleteItemAndUpdate) will delete the item and update the interface.
public void deleteItem(int position) {
mList.remove(position); // hapus list
}
public void deleteItemAndUpdate(int position) {
mList.remove(position); // hapus list
notifyItemRemoved(position); // hapus tampilan
}
In the future, you can decide whether you want to only remove the item from your list OR remove the item and update the UI.
EDIT 1:
You need to keep track of the amount of times that each item was clicked. We can call this value readCount. Every time that the item is clicked, we subtract 1 from this value. When this value reaches 0, we remove it from the list.
ModelDoa:
public class ModelDoa {
private int readCount = 10;
public int getReadCount() {
return this.readCount;
}
public void setReadCount(int readCount) {
this.readCount = readCount;
}
}
PagiViewHolder:
private View.OnClickListener bacaClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
teksbaca = (Button) v.findViewById(R.id.buttonbaca);
ModelDoa modelDoa = mAdapter.getItem(getAdapterPosition());
if (modelDoa != null) {
modelDoa.setReadCount(modelDoa.getReadCount() - 1);
if (modelDoa.getReadCount() <= 0) {
myAdapter.deleteItem(getAdapterPosition());
}
teksbaca.setText("Baca " + modelDoa.getReadCount() + "x");
}
}
};
AdapterDoa:
public ModelDoa getItem(int position) {
if (position > -1 && position < getItemCount()) {
return this.mList.get(position);
} else {
return null;
}
}
EDIT 2:
The idea is to set the readCount variable when you instantiate the object. You do not have multiple variables that do the same thing. You just change the single readCount variable to be either 7 or 10 when you are creating it and use the same getItem method when retrieving the model (not variable!) itself.
ModelDoa:
public class ModelDoa {
private String name;
private String butong;
private int type;
private int readCount;
public ModelDoa(String name, String butong, int type, int readCount) {
this.mName = name;
this.bName = butong;
this.mType = type;
this.readCount = readCount;
}
public int getReadCount() {
return this.readCount;
}
public void setReadCount(int readCount) {
this.readCount = readCount;
}
}
DoaPagi:
private List<ModelDoa> getData() {
String[] data = getResources().getStringArray(R.array.doapagi);
String[] baca = getResources().getStringArray(R.array.bacapagi);
List<ModelDoa> list = new ArrayList<ModelDoa>();
for (int i = 0; i < data.length; i++) {
// Here is where you would set the value of readCount.
list.add(new ModelDoa(data[i], baca[i], ModelDoa.DOA_PAGI, i));
}
return list;
}
I am in the process of changing someone else's code from listView to recyclerView, changing code in my activity and my adapter where required - the adapter is a big change, with OnCreateView, onBindViewHolder and so on...
As the question indicates, category, name, phone, address, comment, public_or_private are posting successfully to mySql db, but nothing for checkedContacts. I'd be very grateful if you could tell me how to fix the problem.
I am quite sure it has to do with something around this line:
//get the other data related to the selected contact - name and number
SelectPhoneContact contact = (SelectPhoneContact) checkbox.getTag();
I am not getting any errors but when I run in debug mode it gets to here and then jumps to:
} catch (Exception e) {
System.out.println("there's a problem here unfortunately");
e.printStackTrace();
}
I think it has something to do with setTag but I find it confusing and I'm not sure where it should be set (if, even, that is the problem).
I'm posting the relevant code for my activity, NewContact and the RecyclerViewAdapter, PopulistoCOntactsAdapter. Thanks for any help.
NewContact.java
//for the SAVE button
private void saveContactButton() {
save.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
System.out.println("you clicked it, save");
try {
System.out.println("we're in the try part");
int count = MatchingContactsAsArrayList.size();
for (int i = 0; i < count; i++) {
//for each Matching Contacts row in the listview
LinearLayout itemLayout = (LinearLayout)recyclerView.getChildAt(i); // Find by under LinearLayout
//for each Matching Contacts checkbox in the listview
CheckBox checkbox = (CheckBox)itemLayout.findViewById(R.id.checkBoxContact);
//get the other data related to the selected contact - name and number
SelectPhoneContact contact = (SelectPhoneContact) checkbox.getTag();
//if that checkbox is checked, then get the phone number
if(checkbox.isChecked()) {
Log.d("Item " + String.valueOf(i), checkbox.getTag().toString());
Toast.makeText(NewContact.this, contact.getPhone(), Toast.LENGTH_LONG).show();
// make each checked contact in selectPhoneContacts
// into an individual
// JSON object called checkedContact
JSONObject checkedContact = new JSONObject();
// checkedContact will be of the form {"checkedContact":"+353123456"}
checkedContact.put("checkedContact", contact.getPhone());
// Add checkedContact JSON Object to checkedContacts jsonArray
//The JSON Array will be of the form
// [{"checkedContact":"+3531234567"},{"checkedContact":"+353868132813"}]
//we will be posting this JSON Array to Php, further down below
checkedContacts.put(checkedContact);
System.out.println("NewContact: checkedcontact JSONObject :" + checkedContact);
}
}
} catch (Exception e) {
System.out.println("there's a problem here unfortunately");
e.printStackTrace();
}
//When the user clicks save
//post phoneNoofUserCheck to NewContact.php and from that
//get the user_id in the user table, then post category, name, phone etc...
//to the review table
StringRequest stringRequest = new StringRequest(Request.Method.POST, NewContact_URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
//response, this will show the checked numbers being posted
Toast.makeText(NewContact.this, response, Toast.LENGTH_LONG).show();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
//post these details to the NewContact.php file and do
//stuff with it
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
//post the phone number to php to get the user_id in the user table
params.put("phonenumberofuser", phoneNoofUserCheck);
//the second value, categoryname.getText().toString() etc...
// is the value we get from Android.
//the key is "category", "name" etc.
// When we see these in our php, $_POST["category"],
//put in the value from Android
params.put("category", categoryname.getText().toString());
params.put("name", namename.getText().toString());
params.put("phone", phonename.getText().toString());
params.put("address", addressname.getText().toString());
params.put("comment", commentname.getText().toString());
params.put("public_or_private", String.valueOf(public_or_private));
System.out.println("public_or_private is " + String.valueOf(public_or_private));
//this is the JSON Array of checked contacts
//it will be of the form
//[{"checkedContact":"+3531234567"},{"checkedContact":"+353868132813"}]
params.put("checkedContacts", checkedContacts.toString());
return params;
}
};
// Adding request to request queue
AppController.getInstance().addToRequestQueue(stringRequest);
//when saved, go back to the PopulistoListView class and update with
//the new entry
Intent j = new Intent(NewContact.this, PopulistoListView.class);
j.putExtra("phonenumberofuser", phoneNoofUserCheck);
NewContact.this.startActivity(j);
finish();
}
});
}
}
PopulistoContactsAdapter.java
public class PopulistoContactsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
//make a List containing info about SelectPhoneContact objects
public List<SelectPhoneContact> theContactsList;
Context context_type;
public class MatchingContact extends RecyclerView.ViewHolder {
//In each recycler_blueprint show the items you want to have appearing
public TextView title, phone;
public CheckBox check;
public Button invite;
public MatchingContact(final View itemView) {
super(itemView);
//title is cast to the name id, in recycler_blueprint,
//phone is cast to the id called no etc
title = (TextView) itemView.findViewById(R.id.name);
phone = (TextView) itemView.findViewById(R.id.no);
invite = (Button) itemView.findViewById(R.id.btnInvite);
check = (CheckBox) itemView.findViewById(R.id.checkBoxContact);
}
}
public class nonMatchingContact extends RecyclerView.ViewHolder {
//In each recycler_blueprint show the items you want to have appearing
public TextView title, phone;
public CheckBox check;
public Button invite;
public nonMatchingContact(final View itemView) {
super(itemView);
//title is cast to the name id, in recycler_blueprint,
//phone is cast to the id called no etc
title = (TextView) itemView.findViewById(R.id.name);
phone = (TextView) itemView.findViewById(R.id.no);
invite = (Button) itemView.findViewById(R.id.btnInvite);
check = (CheckBox) itemView.findViewById(R.id.checkBoxContact);
}
}
#Override
public int getItemViewType(int position) {
//for each row in recyclerview, get the getType_row, set in NewContact.java
return Integer.parseInt(theContactsList.get(position).getType_row());
}
public PopulistoContactsAdapter(List<SelectPhoneContact> selectPhoneContacts, Context context) {
//selectPhoneContacts = new ArrayList<SelectPhoneContact>();
theContactsList = selectPhoneContacts;
// whichactivity = activity;
context_type = context;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView;
//if getType_row is 1...
if (viewType == 1)
{
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
itemView = inflater.inflate(R.layout.recycler_blueprint, parent, false);
//itemView.setTag();
return new MatchingContact(itemView);
} else {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
itemView = inflater.inflate(R.layout.recycler_blueprint_non_matching, parent, false);
return new nonMatchingContact(itemView);
}
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) {
//bind the views into the ViewHolder
//selectPhoneContact is an instance of the SelectPhoneContact class.
//We will assign each row of the recyclerview to contain details of selectPhoneContact:
//The number of rows will match the number of phone contacts
final SelectPhoneContact selectPhoneContact = theContactsList.get(position);
if (viewHolder.getItemViewType() == 1)
{
((MatchingContact) viewHolder).title.setText(selectPhoneContact.getName());
((MatchingContact) viewHolder).phone.setText(selectPhoneContact.getPhone());
CheckBox check = ((MatchingContact) viewHolder).check;
//get the number position of the checkbox in the recyclerview
//check.setTag(position);
}
else {
((nonMatchingContact) viewHolder).title.setText(selectPhoneContact.getName());
((nonMatchingContact) viewHolder).phone.setText(selectPhoneContact.getPhone());
}
}
#Override
public int getItemCount() {
return theContactsList.size();
}
}
And here is my SelectPhoneContact class:
public class SelectPhoneContact {
String phone;
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
boolean isMatching;
public boolean isMatching(){return isMatching;}
public void setIsMatchingContact(boolean isMatching){
this.isMatching = isMatching;
}
//*****************************************
//this is for the checkbox
boolean selected = false;
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected){
this.selected = selected;
}
String type_row;
public String getType_row() {
return type_row;
}
public void setType_row(String type_row) {
this.type_row = type_row;
}
}
I figured it out. This tutorial was very helpful: https://demonuts.com/2017/07/03/recyclerview-checkbox-android/
Here is the code I used for NewContact.java:
//for the SAVE button
private void saveContactButton() {
save.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
System.out.println("you clicked it, save");
try {
System.out.println("we're in the try part");
//loop through the matching contacts
int count = MatchingContactsAsArrayList.size();
for (int i = 0; i < count; i++) {
//for matching contacts that are checked...
if (PopulistoContactsAdapter.theContactsList.get(i).getSelected()) {
Toast.makeText(NewContact.this, PopulistoContactsAdapter.theContactsList.get(i).getPhone() + " clicked!", Toast.LENGTH_SHORT).show();
// make each checked contact in selectPhoneContacts
// into an individual
// JSON object called checkedContact
JSONObject checkedContact = new JSONObject();
// checkedContact will be of the form {"checkedContact":"+353123456"}
checkedContact.put("checkedContact", PopulistoContactsAdapter.theContactsList.get(i).getPhone());
// Add checkedContact JSON Object to checkedContacts jsonArray
//The JSON Array will be of the form
// [{"checkedContact":"+3531234567"},{"checkedContact":"+353868132813"}]
//we will be posting this JSON Array to Php, further down below
checkedContacts.put(checkedContact);
System.out.println("NewContact: checkedcontact JSONObject :" + checkedContact);
}
}
} catch (Exception e) {
System.out.println("there's a problem here unfortunately");
e.printStackTrace();
}
//When the user clicks save
//post phoneNoofUserCheck to NewContact.php and from that
//get the user_id in the user table, then post category, name, phone etc...
//to the review table
StringRequest stringRequest = new StringRequest(Request.Method.POST, NewContact_URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
//response, this will show the checked numbers being posted
Toast.makeText(NewContact.this, response, Toast.LENGTH_LONG).show();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
}) {
//post these details to the NewContact.php file and do
//stuff with it
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
//post the phone number to php to get the user_id in the user table
params.put("phonenumberofuser", phoneNoofUserCheck);
//the second value, categoryname.getText().toString() etc...
// is the value we get from Android.
//the key is "category", "name" etc.
// When we see these in our php, $_POST["category"],
//put in the value from Android
params.put("category", categoryname.getText().toString());
params.put("name", namename.getText().toString());
params.put("phone", phonename.getText().toString());
params.put("address", addressname.getText().toString());
params.put("comment", commentname.getText().toString());
params.put("public_or_private", String.valueOf(public_or_private));
System.out.println("public_or_private is " + String.valueOf(public_or_private));
//this is the JSON Array of checked contacts
//it will be of the form
//[{"checkedContact":"+3531234567"},{"checkedContact":"+353868132813"}]
params.put("checkedContacts", checkedContacts.toString());
return params;
}
};
// Adding request to request queue
AppController.getInstance().addToRequestQueue(stringRequest);
//when saved, go back to the PopulistoListView class and update with
//the new entry
Intent j = new Intent(NewContact.this, PopulistoListView.class);
j.putExtra("phonenumberofuser", phoneNoofUserCheck);
NewContact.this.startActivity(j);
finish();
}
});
}
}
And my PopulistoContactsAdapter.java:
public class PopulistoContactsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
//make a List containing info about SelectPhoneContact objects
public static List<SelectPhoneContact> theContactsList;
Context context_type;
public class MatchingContact extends RecyclerView.ViewHolder {
//In each recycler_blueprint show the items you want to have appearing
public TextView title, phone;
public CheckBox check;
public Button invite;
public MatchingContact(final View itemView) {
super(itemView);
//title is cast to the name id, in recycler_blueprint,
//phone is cast to the id called no etc
title = (TextView) itemView.findViewById(R.id.name);
phone = (TextView) itemView.findViewById(R.id.no);
invite = (Button) itemView.findViewById(R.id.btnInvite);
check = (CheckBox) itemView.findViewById(R.id.checkBoxContact);
}
}
public class nonMatchingContact extends RecyclerView.ViewHolder {
//In each recycler_blueprint show the items you want to have appearing
public TextView title, phone;
public CheckBox check;
public Button invite;
public nonMatchingContact(final View itemView) {
super(itemView);
//title is cast to the name id, in recycler_blueprint,
//phone is cast to the id called no etc
title = (TextView) itemView.findViewById(R.id.name);
phone = (TextView) itemView.findViewById(R.id.no);
invite = (Button) itemView.findViewById(R.id.btnInvite);
check = (CheckBox) itemView.findViewById(R.id.checkBoxContact);
}
}
#Override
public int getItemViewType(int position) {
//for each row in recyclerview, get the getType_row, set in NewContact.java
return Integer.parseInt(theContactsList.get(position).getType_row());
}
public PopulistoContactsAdapter(List<SelectPhoneContact> selectPhoneContacts, Context context) {
//selectPhoneContacts = new ArrayList<SelectPhoneContact>();
theContactsList = selectPhoneContacts;
// whichactivity = activity;
context_type = context;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView;
//if getType_row is 1...
if (viewType == 1)
{
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
itemView = inflater.inflate(R.layout.recycler_blueprint, parent, false);
//itemView.setTag();
return new MatchingContact(itemView);
} else {
Context context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
itemView = inflater.inflate(R.layout.recycler_blueprint_non_matching, parent, false);
return new nonMatchingContact(itemView);
}
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) {
//bind the views into the ViewHolder
//selectPhoneContact is an instance of the SelectPhoneContact class.
//We will assign each row of the recyclerview to contain details of selectPhoneContact:
//The number of rows will match the number of phone contacts
final SelectPhoneContact selectPhoneContact = theContactsList.get(position);
if (viewHolder.getItemViewType() == 1)
{
((MatchingContact) viewHolder).title.setText(selectPhoneContact.getName());
((MatchingContact) viewHolder).phone.setText(selectPhoneContact.getPhone());
((MatchingContact) viewHolder).check.setText("Cheeckbox" + position);
((MatchingContact) viewHolder).check.setChecked(theContactsList.get(position).getSelected());
((MatchingContact) viewHolder).check.setTag(position);
((MatchingContact) viewHolder).check.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Integer pos = (Integer) ((MatchingContact) viewHolder).check.getTag();
Toast.makeText(context_type, theContactsList.get(pos).getPhone() + " clicked!", Toast.LENGTH_SHORT).show();
if (theContactsList.get(pos).getSelected()) {
theContactsList.get(pos).setSelected(false);
} else {
theContactsList.get(pos).setSelected(true);
}
}
});
// CheckBox check = ((MatchingContact) viewHolder).check;
//get the number position of the checkbox in the recyclerview
//check.setTag(position);
}
else {
((nonMatchingContact) viewHolder).title.setText(selectPhoneContact.getName());
((nonMatchingContact) viewHolder).phone.setText(selectPhoneContact.getPhone());
}
}
#Override
public int getItemCount() {
return theContactsList.size();
}
}
I'm fetching 3 String values from the database and then I'm converting it to Long and then I'm calculating a difference and then putting this calculated Long value in a method as parameter. I'm using FastAdapter.
The filterRequests(List <Long> l) is a method in MainActivity which do the logic of filtering requests/content based on the long l.
entire adapter:
public class GRModelClass extends AbstractItem<GRModelClass, GRClass.ViewHolder>{
private static final ViewHolderFactory<? extends ViewHolder> FACTORY = new ItemFactory();
String postedBy, postedTime, currentLat, currentLng, utcFormatDateTime, userEmail, userID;
String startDateTimeInEpoch, endDateTimeInEpoch;
DatabaseReference primaryDBKey;
long ms;
String itemID;
public GRModelClass(){}
public GRModelClass(String postedBy, String postedTime, String currentLat, String currentLng, String utcFormatDateTime, String userEmail, String userID, String startDateTimeInEpoch, String endDateTimeInEpoch, DatabaseReference primaryDBKey) {
this.postedBy = " " + postedBy;
this.postedTime = postedTime;
this.currentLat = currentLat;
this.currentLng = currentLng;
this.utcFormatDateTime = utcFormatDateTime;
this.userEmail = userEmail;
this.userID = userID;
this.startDateTimeInEpoch = startDateTimeInEpoch;
this.endDateTimeInEpoch = endDateTimeInEpoch;
this.primaryDBKey = primaryDBKey;
}
#Exclude
public Map<String, Object> toMap() {
HashMap<String, Object> result = new HashMap<>();
result.put("pBy", postedBy);
result.put("cLat", currentLat);
result.put("cLng", currentLng);
result.put("utcFormatDateTime", utcFormatDateTime);
result.put("userEmail", userEmail);
result.put("userID", userID);
result.put("startDateTime", startDateTimeInEpoch);
result.put("endDateTime", endDateTimeInEpoch);
return result;
}
#Override
public int getType() {
return R.id.recycler_view;
}
#Override
public int getLayoutRes() {
return R.layout.sr_layout;
}
#Override
public void bindView(final ViewHolder holder, List list) {
super.bindView(holder, list);
holder.postedBy.setText(postedBy);
holder.postedBy.setTypeface(null, Typeface.BOLD);
holder.startDateTimeInEpoch.setText(startDateTimeInEpoch);
holder.startDateTimeInEpoch.setVisibility(View.INVISIBLE);
holder.endDateTimeInEpoch.setText(endDateTimeInEpoch);
holder.endDateTimeInEpoch.setVisibility(View.INVISIBLE);
MainActivity.filterButton.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem menuItem) {
holder.geoQuery = holder.geoFireReference.queryAtLocation(new GeoLocation(holder.currentLatDouble, holder.currentLngDouble), 5);
holder.geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
#Override
public void onKeyEntered(String key, GeoLocation location) {
primaryDBKey.child(key).child("startDateTimeInEpoch").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() != null) {
holder.startTimeDateInEpochLong2 = Long.parseLong(dataSnapshot.getValue().toString());
holder.now = System.currentTimeMillis() / 1000;
holder.diffNowsdtel.add(holder.startTimeDateInEpochLong2 - holder.now);
Log.d("log1", String.valueOf(holder.diffNowsdtel));
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if(holder.mContext instanceof MainActivity){
((MainActivity)holder.mContext).filterRequests(holder.diffNowsdtel);
Log.d("log2", String.valueOf(holder.diffNowsdtel));
}
}
}, 1500);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public void onKeyExited(String key) {
}
#Override
public void onKeyMoved(String key, GeoLocation location) {
}
#Override
public void onGeoQueryReady() {
}
#Override
public void onGeoQueryError(DatabaseError error) {
}
});
return true;
}
});
}
/**
* our ItemFactory implementation which creates the ViewHolder for our adapter.
* It is highly recommended to implement a ViewHolderFactory as it is 0-1ms faster for ViewHolder creation,
* and it is also many many timesa more efficient if you define custom listeners on views within your item.
*/
protected static class ItemFactory implements ViewHolderFactory<ViewHolder> {
public ViewHolder create(View v) {
return new ViewHolder(v);
}
}
/**
* return our ViewHolderFactory implementation here
*
* #return
*/
#Override
public ViewHolderFactory<? extends ViewHolder> getFactory() {
return FACTORY;
}
// Manually create the ViewHolder class
protected static class ViewHolder extends RecyclerView.ViewHolder {
TextView postedBy, userID, currentLt, currentLn, requestID, postedFrom;
TextView startDateTimeInEpoch, endDateTimeInEpoch, diffNowsdtelTV;
LinearLayout linearLayout;
long difference, differenceCurrentStartTime, handlerGap;
long startTimeDateInEpochLong2;
public static long now;
List<Long> diffNowsdtel;
Context mContext;
DatabaseReference firebaseDatabase;
GeoFire geoFireReference;
GeoQuery geoQuery;
public ViewHolder(final View itemView) {
super(itemView);
postedBy = (TextView) itemView.findViewById(R.id.postedBy);
startDateTimeInEpoch = (TextView) itemView.findViewById(R.id.startTimeDateInEpoch);
endDateTimeInEpoch = (TextView) itemView.findViewById(R.id.endTimeDateInEpoch);
diffNowsdtelTV = (TextView) itemView.findViewById(R.id.diffNowsdtelTV);
this.mContext = itemView.getContext();
private boolean isNetworkAvailable() {
ConnectivityManager connectivityManager
= (ConnectivityManager) itemView.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
}
}
}
The problem is that when I'm logging log1, I'm getting all the 3 values shown in Logcat, but when I'm logging log2 only the last calculated value is getting shown and that is the value using which filterRequests(Long l) is getting called.
Update - after updating the adapter code, log1 and log2 now shows this:
D/log1: [2197]
D/log1: [2197, -1007]
D/log1: [2197, -1007, 4003]
D/log2: [2197, -1007, 4003]
filterRequests() method is the method in which the logic to filter content based on time is done. The parameter which goes in filterRequests() is holder.diffNowsdtel which has 3 long values for now and do it should do the logic based on it.. if the long value is <=900 the content which has the long value -1007 should be shown and when long value is >900, the content which has the long value 2197 and 4003 should be shown.
here's the code:
public void filterRequests(final List<Long> l) {
final int size = l.size();
Log.d("lng", String.valueOf(l));
if (isNetworkAvailable()) {
if (chkBoxLiveRqsts.isChecked()) {
firebaseDatabase.child(key).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() != null) {
for (int i = 0; i < size; i++){
if (l.get(i) <= 900) {
...
} else {
}
}
progressDialogAdding.dismiss();
} else {
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
...
});
} else if (chkBoxSFLRqsts.isChecked()) {
fastItemAdapter.clear();
firebaseDatabase.child(key).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() != null) {
for (int i = 0; i < size; i++) {
if (l.get(i) > 900) {
...
} else {
}
}
progressDialogAdding.dismiss();
} else {
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
...
}
} else {
Snackbar snackbar = Snackbar
.make(coordinatorLayout, "No internet connection", Snackbar.LENGTH_SHORT);
snackbar.show();
progressDialogAdding.dismiss();
}
}
});
dialog = builder.create();
dialog.show();
}
Log value of lng:
D/lng: [2197, -1007, 4003]
What I want is that the filterRequests(Long l) method should use all the values of holder.diffNowsdtelTV.getText().toString() and do the logic using them.
I'm sorry for ambiguous question. Please help me with this issue!
What you are doing
In your ViewHolder diffNowsdtel is declared as long (which saves latest value) whenever you are logging log1 Logger displays (latest value from diffNowsdtel so your log displays different values because bindView calls many times whenever you are updating your row or complete dataset)
D/log1: -22136
D/log1: -22403
D/log1: -25094
Inside onMenuItemClick you are fetching value directly from your TextView which is now -25094 that's why you have only one value in your TextView and log says
D/log2: -25094
What you should do
Set Tag using holder.diffNowsdtelTV.setTag(your database key or row_id) and inside your onMenuItemClick get your tag using
Object someTagName = (Object) holder.diffNowsdtelTV.getTag();
Now fetch your 3 String values from the database using value stored in someTagName and then do your calculations.
Edit
Actually you need 3 values to do your calculation, while in your current logic you only have the latest value stored in diffNowsdtel. So now you need a logic to store trice your values somewhere and inside onMenuItemClick use them but if you will go to save trice your values you have to change your diffNowsdtel to long[] or List<Long> and save your every value in it on every bindView call which needs some logic so simplest way is to pass your unique database column say primary key and save it in your GRModelClass
String primaryDbKey;
public GRModelClass(String primaryDbKey, ...) {
this.primaryDbKey = primaryDbKey;
....
}
Inside your onMenuItemClick use primaryDbKey to fetch your 3 String values from your database table(which you are doing somewhere else) and then do your calculations.
Edit
You made a list with Long, both List and Long are not primitive data types.
In your question you are comparing primitive and non primitive data types, for comparison both sides of a comparator should be of same data types.
What you are doing:
if (l.get(i) <= 900)
Here l.get(i) returns a Long value where as 900 is integer value.
What you should do:
Simply compare your l.get(i) with Long by doing 900L
if (l.get(i) <= 900L)
Or
if (l.get(i) > 900L)
Try to set initialize the value you want within the onBindView
Log.d("diffNowsdtel", holder.diffNowsdtelTV.getText().toString());
final String val = holder.diffNowsdtelTV.getText().toString();
MainActivity.filterButton.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem menuItem) {
if(holder.mContext instanceof MainActivity){
((MainActivity)holder.mContext).filterRequests(Long.parseLong(val));
Log.d("diffNowsdtel2",val);
}
return true;
}
});
You have a statically defined object called filterButton in MainActivity. Every time bindView is called for a new view, it is replacing the menu item listener for that button. So when you click that menu item, it is only calling the handler you set on for the last item that bindView was called for. That's why you only see one log entry when that filter button is pressed.