1

I refresh a ListView with a CursorAdapter with notifyDataSetChanged(). the notify is called few times a second from a CountDownTimer (I display several count down timers, which are stored in DB). The UI updates correctly.

The problem is, that my list view has two buttons in each row, to pause/resume and cancel a timer. I attach the OnClickListeners in getView() of my custom adapter (the adapter itself implements OnClickListener and I handle the click events with a switch on view's ID). This works ok if I do not refresh the list view. When I start calling notifyDataSetChanged(), the click events are random. Clicking one button selects both buttons in a row, or a button in a different row and only some times I receive the correct click events. Those random click targets are not catchable (i.e. i do not receive an onClick()).

I also tried listView.invalidateViews(), getLoaderManager().restartLoader(...) re-adding a new adapter all with similar problem or even worse.

I think this could be a problem caused by recycling the "row views", but this behaviour is also with only one row.

The question is, how can I continuosly refresh a ListView and preserve click events of the buttons in it?

EDIT the adapter code:

public class TimersCursorAdapter extends SimpleCursorAdapter implements OnClickListener {

// helper interface to notify about timer has finished, so we can update the UI
public interface OnTimerFinishedLoaderReloadCallback {
    public void onTimerFinishedLoaderRestart();
}


DecimalFormat m_Format = new DecimalFormat("00");
OnTimerFinishedLoaderReloadCallback m_TimerFinishedCallback = null;

public TimersCursorAdapter(Context context, int layout, Cursor cursor, String[] from, int[] to, int flags) {
    super(context, layout, cursor, from, to, flags);
}

public void setTimerFinishedCallback(OnTimerFinishedLoaderReloadCallback callback) {
    m_TimerFinishedCallback = callback;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View view = super.getView(position, convertView, parent);
    View btn;

    view.setTag(position);

    // pause
    btn = view.findViewById(R.id.timers_list_button_pause_resume);
    btn.setOnClickListener(this);

    // cancel
    btn = view.findViewById(R.id.timers_list_button_cancel);
    btn.setOnClickListener(this);

    return view;
}

@Override 
public void bindView(View view, Context context, Cursor cur) {
    String result = "00:00:00";
    long hours, minutes, seconds;

    final long now = Calendar.getInstance().getTimeInMillis() / 1000;
    long duration = cur.getLong(cur.getColumnIndex("duration"));
    final String name = cur.getString(cur.getColumnIndex("name"));

    long remaining = Math.max(0, cur.getLong(cur.getColumnIndex("start")) + duration - now);

    if (remaining > 0) {
        hours = remaining / 3600;
        remaining %= 3600;
        minutes = remaining / 60;
        remaining %= 60;
        seconds = remaining;

        result = "" + m_Format.format(hours) + ":" + m_Format.format(minutes) + ":" + m_Format.format(seconds);
    }
    else if (m_TimerFinishedCallback != null) {
        // the timer is up, so notify our listener, so the UI can be updated accordingly
        m_TimerFinishedCallback.onTimerFinishedLoaderRestart();
    }

    // remaining time
    TextView tv = (TextView)view.findViewById(R.id.timers_list_remaining_time); 
    tv.setText(result);
    tv.setTypeface(TinyTimerActivity.font);

    // timer's details
    hours = duration / 3600;
    duration %= 3600;
    minutes = duration / 60;
    duration %= 60;
    seconds = duration;

    final String details = name + " timer: " + m_Format.format(hours) + ":" + m_Format.format(minutes) + ":" + m_Format.format(seconds);  
    tv = (TextView)view.findViewById(R.id.timers_list_details);
    tv.setText(details);
}

@Override
public void onClick(View v) {
    final int position = (Integer)((ViewGroup)v.getParent()).getTag();
    final CursorWrapper cw = (CursorWrapper)getItem(position);

    switch (v.getId()) {
        case R.id.timers_list_button_cancel:
            Toast.makeText(TinyTimerActivity.AppContext, "canceling timer " + cw.getLong(cw.getColumnIndex("duration")), Toast.LENGTH_SHORT).show();
            break;

        case R.id.timers_list_button_pause_resume:
            Toast.makeText(TinyTimerActivity.AppContext, "pausing timer " + cw.getLong(cw.getColumnIndex("duration")), Toast.LENGTH_SHORT).show();
            break;
    }

}

}

EDIT 2: i forget to add, that this happens on a real device with pure android 2.3.7 and also on an emulator with 4.0.0

EDIT 3: I also tried to always create a new view in getView, but the bahaviour is the same. the click events are always messed up. I think the only solution is to fill a linear layout from DB and update it.

public View getView(int position, View convertView, ViewGroup parent) {
    View view = m_Inflater.inflate(R.layout.timers_list_item, null);

    ViewHolder holder = new ViewHolder();
    holder.buttonCancel  = (Button)view.findViewById(R.id.timers_list_button_cancel);
    holder.buttonPause   = (Button)view.findViewById(R.id.timers_list_button_pause_resume);
    holder.textDetails   = (TextView)view.findViewById(R.id.timers_list_details);
    holder.textRemaining = (TextView)view.findViewById(R.id.timers_list_remaining_time);
    holder.buttonCancel.setOnClickListener(this);
    holder.buttonPause.setOnClickListener(this);
    holder.position = position;
    view.setTag(holder);

    // bind the view's content
    final Cursor cur = getCursor();
    cur.moveToPosition(position);
    bindView(view, mContext, cur);

    return view;
}
shelll
  • 3,234
  • 3
  • 33
  • 67
  • 1
    Can you post your Adapter here? – Andro Selva Mar 09 '12 at 12:15
  • @AndroSelva I added the adapter's srouce, do you see any problem in it? ...I also tried ViewHolder pattern and the behaviour is more stable, but I still get bad clicks in ~30% of cases. my problem looks similar to this unsolved issue: [link](http://stackoverflow.com/questions/8831043/listview-item-with-clickable-subview-sometime-cant-pass-the-click-event-to-the) – shelll Mar 12 '12 at 11:50
  • @shelll help me with this http://stackoverflow.com/questions/28148618/listview-not-refreshing-after-click-on-button –  Jan 27 '15 at 12:53

0 Answers0