【发布时间】:2019-10-30 15:34:55
【问题描述】:
场景:
-
RecyclerView和RecyclerView.Adapter。notifyDataSetChanged()-方法每 10 毫秒调用一次,因为显示时间敏感信息,因此 ViewHolders(类正在扩展RecyclerView.ViewHolder)会经常更新。 - 基础数据来自本机 C 库,因此适配器方法被覆盖以从本机库中查询数据。
- 派生自this best practice video 在适配器类中实现了一个接口,以将onTouch-Events 从适配器传递到活动。因此 ViewHolder 类实现了
View.OnTouchListener,并通过接口将所有触摸事件和 listItem-position(由getAdapterPosition()查询)传递给 Activity。 - 应在活动中识别每个 ListItem 的各种 onTouch 事件(单击、LongClick、Swipe 等)。
问题:
在调用notifyDataSetChanged() 后不久触摸 ListItem 时,接收到的值为:
1getAdapterPosition():-1
2motionEvent.getAction():ACTION_DOWN直接跟在ACTION_CANCEL之后
注意,如果你调用了 notifyDataSetChanged(),直到下一个 布局传递,此方法的返回值为 NO_POSITION。
所以我猜每次在notifyDatasetChanges() 刷新viewHolders 时触摸listItem 都会发生错误:getAdapterPosition() 根据文档返回-1。而且,可能是因为 viewHolder 中的元素被刷新了,所以 onTouchEvent 会抛出一个ACTION_CANCEL。
我尝试了什么:
- 我尝试在
ACTION_DOWN上停止RecyclerView的刷新,但是因为ACTION_CANCEL事件,我没有收到任何ACTION_UP事件,无法重新开始刷新数据。 - 我在活动中的recyclerView 中添加了
RecyclerView.OnItemTouchListener(),以便在onInterceptTouchEvent()中接收TouchEvent,然后将其传递给listItem 及其onTouchListener,并在此处停止recyclerview 的刷新。 但是,因为我仍然需要有关单击了哪些项目的信息,所以我仍然需要仍然返回 -1 和ACTION_CANCEL的 onTouchListener 项目。
问题:
在频繁更新其数据的 RecyclerView 中处理 onTouch 事件的最佳做法是什么?
活动类
public class Activity extends AppCompatActivity implements DataStoreDataAdapter.OnListItemTouchListener {
Handler dataUpdateHandler = new Handler();
Runnable timerRunnable = new Runnable() {
@Override
public void run() {
dataStoreDataAdapter.notifyDataSetChanged();
dataUpdateHandler.postDelayed(this, 10);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
dataStoreDataAdapter = new DataStoreDataAdapter(getApplicationContext(),this);
recyclerView = findViewById(R.id.list_view);
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(dataStoreDataAdapter);
recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
@Override
public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
Log.i(TAG, "onInterceptTouchEvent: " + " touched by type " + e);
int action = e.getAction();
if (action == MotionEvent.ACTION_DOWN) {
dataUpdateHandler.removeCallbacks(timerRunnable);
} else if (action == MotionEvent.ACTION_UP) {
dataUpdateHandler.post(timerRunnable);
}
return false;
}
@Override
public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
Log.i(TAG, "onTouchEvent: " + " touched by type " + e);
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
});
//set update Handler
dataUpdateHandler.post(timerRunnable);
}
@Override
public void onListItemTouch(int position, MotionEvent motionEvent) {
Log.i(TAG, "onListItemTouch: ListItem position " + position + " motionEvent: " + motionEvent);
}
}
适配器类
public class DataStoreDataAdapter extends RecyclerView.Adapter<DataStoreDataAdapter.ViewHolder> {
private OnListItemTouchListener onListItemTouchListener;
static class ViewHolder extends RecyclerView.ViewHolder implements View.OnTouchListener{
View view;
TextView Name;
// ...
ViewHolder(@NonNull View itemView, OnListItemTouchListener onListItemTouchListener) {
super(itemView);
this.Name = itemView.findViewById(R.id.NameListItem);
// ...
this.onListItemTouchListener = onListItemTouchListener;
itemView.setOnTouchListener(this);
}
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
onListItemTouchListener.onListItemTouch(getAdapterPosition(), motionEvent);
return true;
}
}
public interface OnListItemTouchListener{
void onListItemTouch(int position, MotionEvent motionEvent);
}
public DataStoreDataAdapter(Context context, OnListItemTouchListener onListItemTouchListener) {
super();
this.onListItemTouchListener = onListItemTouchListener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.list_item,parent,false);
return new ViewHolder(view, onListItemTouchListener);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
// get the stored data for this position
// ...get stuff from native library
// put data into view elements...
}
@Override
public int getItemCount() {
return // ... itemCount from native library;
}
}
【问题讨论】:
标签: android android-recyclerview onclicklistener ontouchlistener custom-adapter