我使用带有notifyDataSetChanged()的CursorAdapter刷新ListView。从CountDownTimer每秒调用几次通知(我显示几个倒计时器,存储在DB中)。用户界面正确更新。
问题是,我的列表视图每行有两个按钮,用于暂停/恢复和取消计时器。我在我的自定义适配器的getView()中附加了OnClickListeners(适配器本身实现了OnClickListener,我通过切换视图的ID来处理单击事件)。如果我不刷新列表视图,这可以正常工作。当我开始调用notifyDataSetChanged()时,单击事件是随机的。单击一个按钮可选择一行中的两个按钮,或选择另一行中的按钮,并且只会选择一些正确的点击事件。那些随机点击目标不可捕获(即我没有收到onClick())。
我也尝试过listView.invalidateViews(),getLoaderManager()。restartLoader(...)重新添加一个新的适配器,但都有类似的问题,甚至更糟。
我认为这可能是回收“行视图”引起的问题,但这种行为也只有一行。
问题是,如何连续刷新ListView并保留其中按钮的点击事件?
编辑适配器代码:
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;
}
}
}
编辑2: 我忘了添加,这发生在一个真正的设备上纯机器人2.3.7和4.0.0模拟器上
编辑3: 我也试图在getView中创建一个新视图,但是bahaviour是一样的。点击事件总是搞砸了。我认为唯一的解决方案是从DB填充线性布局并更新它。
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;
}