I have a RecyclerView
with a counter in every list item, as the counter callback is called, it updated the relevant fields and call nottifyDataSetChanged
it works fine but the problem is, I also have a long press listener attached to it, as the data is updated while user was pressing on item, the press is lost
how can I handle it?
updater code:
public void setCountDown(int countDown){
mCountDown = countDown;
notifyDataSetChanged();
}
long click listener:
holder.container.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
FragmentManager fm = ((AppCompatActivity)activity).getSupportFragmentManager();
EditAccountFragment editAccountFragmentDialog = EditAccountFragment.newInstance(_id);
editAccountFragmentDialog.show(fm, "fragment_edit_account");
return false;
}
});
Adding Adapter on Request
public class AccountAdapter extends RecyclerView.Adapter<AccountAdapter.ItemViewHolder> {
private static final String TAG = "ITEM_ADAPTER";
private Cursor itemCursor;
private Activity activity;
private ItemColumnIds feedColumnIds;
private int mCountDown;
private boolean ignoreDatasetChange = false;
/**
* initilaizer for recycler view adapter, takes in activity although better way is to handle
* activity related tasks within activiyt, but this is just done to speed up the overall process
* @param activity
*/
public AccountAdapter(Activity activity){
this.activity = activity;
}
@Override
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(activity).inflate(R.layout.account_item,
parent, false);
feedColumnIds = new ItemColumnIds();
if (itemCursor != null){
// initilize column indices information
feedColumnIds.colId = itemCursor.getColumnIndex(
AuthContract.AccountEntry._ID);
feedColumnIds.colAcctId = itemCursor.getColumnIndex(
AuthContract.AccountEntry.COLUMN_USERNAME);
feedColumnIds.colPin = itemCursor.getColumnIndex(
AuthContract.AccountEntry.COLUMN_PIN);
feedColumnIds.colIssuer = itemCursor.getColumnIndex(
AuthContract.AccountEntry.COLUMN_ISSUER);
}
return new ItemViewHolder(view);
}
@Override
public void onBindViewHolder(final ItemViewHolder holder, int position) {
if(itemCursor == null)
return;
if(!itemCursor.moveToPosition(position))
return;
//get values from cursor
String pin = itemCursor.getString(feedColumnIds.colPin);
String email = itemCursor.getString(feedColumnIds.colAcctId);
String issuer = itemCursor.getString(feedColumnIds.colIssuer);
final long _id = itemCursor.getLong(feedColumnIds.colId);
//set view values
holder.tvEmail.setText(email);
holder.tvPin.setText(pin);
holder.tvIssuer.setText(issuer);
holder.apCountDown.setProgress(mCountDown);
holder.container.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String pin = holder.tvPin.getText().toString();
ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("Secret Key", pin);
clipboard.setPrimaryClip(clip);
Toast.makeText(activity, "OTP code "+ pin +" copied to clipboard", Toast.LENGTH_SHORT).show();
}
});
holder.container.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
FragmentManager fm = ((AppCompatActivity)activity).getSupportFragmentManager();
EditAccountFragment editAccountFragmentDialog = EditAccountFragment.newInstance(_id);
editAccountFragmentDialog.show(fm, "fragment_edit_account");
return false;
}
});
}
@Override
public int getItemCount() {
if(itemCursor == null)
return 0;
return itemCursor.getCount();
}
/**
* close previurous cursor if any and replace that with the new on from host class
* @param cursor
*/
public void swapCursor(Cursor cursor) {
if(itemCursor != null) {
itemCursor.close();
itemCursor = null;
}
itemCursor = cursor;
notifyDataSetChanged();
}
public void setCountDown(int countDown){
mCountDown = countDown;
if(!ignoreDatasetChange)
notifyDataSetChanged();
}
/**
* View golder for auction item entry
*/
public class ItemViewHolder extends RecyclerView.ViewHolder {
TextView tvEmail;
TextView tvPin;
TextView tvIssuer;
// TextView tvCountDown;
ArcProgress apCountDown;
View container;
public ItemViewHolder(View itemView) {
super(itemView);
tvEmail = (TextView) itemView.findViewById(R.id.tv_email);
tvPin = (TextView) itemView.findViewById(R.id.tv_pin);
tvIssuer = (TextView) itemView.findViewById(R.id.tv_issuer);
apCountDown = (ArcProgress) itemView.findViewById(R.id.arc_progress);
container = itemView.findViewById(R.id.container);
}
}
/**
* databse column id holder for auction item
*/
class ItemColumnIds {
public int colId;
public int colPin;
public int colAcctId;
public int colIssuer;
}
}
Fixed it!
So basically the issue was, every time there happens to be an update to counter the whole of adapter was populating whole of the view including the listeners as well, even setting listeners in ViewHolder did not work.
What I did, I extended my layout manager and added a method to update only the counter widget of view leaving the rest untouched.
Here is TimedGridLayoutManager:
public class TimedGridLayoutManager extends GridLayoutManager {
public TimedGridLayoutManager(Context context, int spanCount) {
super(context, spanCount);
}
//updates the counter every second
public void setCountDown(int countDown) {
//return null if there are no items and avoid crash
if(this.getItemCount() <= 0)
return;
//get first and last visible child
int firstPos = this.findFirstVisibleItemPosition();
int lastPos = this.findLastVisibleItemPosition();
//loop through the range and set counter
for(int i = firstPos; i <= lastPos; i++) {
View itemView = this.findViewByPosition(i);
ArcProgress arcCounter = (ArcProgress) itemView.findViewById(R.id.arc_counter);
arcCounter.setProgress(countDown);
}
}
}
It don't know if its the most efficient solution out there, but it worked like a charm for me plus I did not had to touch my adapter code at all.