需求:在 GridView 上手指滑動,經過的 Item 背景顏色實時變化(如日曆選區效果)。開發者嘗試在 SimpleOnGestureListener.onScroll 中嵌套 setOnItemClickListener 並調用 adapter.getDropDownView 強制刷新,結果只有點擊有反應,滑動無效。
GestureDetector 攔截觸摸事件用於識別手勢,ListView/GridView 內部也需要觸摸事件處理滾動、點擊、選中。onScroll 中動態 setOnItemClickListener 違背單一職責,且 getDropDownView 是 Spinner 下拉視圖專用方法,GridView 不調用它。getView 才是列表項繪製入口,背景色必須在數據源變更後 notifyDataSetChanged() 觸發重繪。public class SelectableGridView extends GridView {
private int lastSelectedPos = -1;
private OnItemSelectChangeListener listener;
public SelectableGridView(Context context, AttributeSet attrs) { super(context, attrs); }
@Override public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getActionMasked();
int pos = pointToPosition((int)ev.getX(), (int)ev.getY());
if (pos != INVALID_POSITION && pos != lastSelectedPos) {
lastSelectedPos = pos;
if (listener != null) listener.onItemSelected(pos);
}
return super.onTouchEvent(ev); // 保留原有滾動/點擊行為
}
public void setOnItemSelectChangeListener(OnItemSelectChangeListener l) { listener = l; }
public interface OnItemSelectChangeListener { void onItemSelected(int position); }
}
gridView.setOnItemSelectChangeListener(pos -> {
// 更新數據源標記選中狀態
for (int i = 0; i < dataList.size(); i++) {
dataList.get(i).setSelected(i == pos);
}
adapter.notifyDataSetChanged(); // 觸發 getView 重繪
});
@Override public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) { ... }
ItemData data = getItem(position);
holder.textView.setBackgroundColor(data.isSelected() ? Color.BLUE : Color.WHITE);
return convertView;
}
setOnItemClickListener,職責混亂。notifyDataSetChanged(),而非直接操作 View。RecyclerView + ItemTouchHelper 或成熟庫。掌握「數據驅動視圖」原則,觸摸事件只負責修改數據,UI 自動響應,才是 Android 列表交互的正道。