Android 使用频繁更改的数据处理RecyclerView的触摸事件
场景:Android 使用频繁更改的数据处理RecyclerView的触摸事件,android,android-recyclerview,onclicklistener,ontouchlistener,custom-adapter,Android,Android Recyclerview,Onclicklistener,Ontouchlistener,Custom Adapter,场景: RecyclerView带有RecyclerView.Adapter。notifyDataSetChanged()-方法每10毫秒调用一次,因为会显示时间敏感信息,因此ViewHolder(类扩展为RecyclerView.ViewHolder)会频繁更新 底层数据来自本机C库,因此将重写适配器方法以从本机库查询数据 在适配器类中实现了从接口派生的,以将onTouch事件从适配器传递给活动。因此,ViewHolder类实现了View.OnTouchListener,并通过接口将所有触摸
带有RecyclerView
。RecyclerView.Adapter
-方法每10毫秒调用一次,因为会显示时间敏感信息,因此ViewHolder(类扩展为notifyDataSetChanged()
)会频繁更新RecyclerView.ViewHolder
- 底层数据来自本机C库,因此将重写适配器方法以从本机库查询数据
- 在适配器类中实现了从接口派生的,以将onTouch事件从适配器传递给活动。因此,ViewHolder类实现了
,并通过接口将所有触摸事件和listItem位置(由View.OnTouchListener
查询)传递给活动getAdapterPosition()
- 活动中应识别每个列表项的各种onTouch事件(单击、长单击、滑动等)
notifyDataSetChanged()
后不久触摸列表项时,收到的值为:
getAdapterPosition()
:-1
motionEvent.getAction()
:ACTION\u DOWN
直接后跟ACTION\u CANCEL
报告说:
请注意,如果调用了notifyDataSetChanged(),直到下一个
布局过程中,此方法的返回值将为NO_POSITION
因此,我猜每次触摸listItem时都会发生错误,而notifyDatasetChanges()
刷新视图持有者:getAdapterPosition()
根据文档返回-1。而且,可能是因为viewHolder中的元素被刷新,onTouchEvent会抛出一个操作\u CANCEL
我的尝试:
ACTION\u DOWN
上停止刷新RecyclerView,但由于ACTION\u CANCEL
事件,我没有收到任何ACTION\u UP
事件,无法重新启动数据刷新RecyclerView.OnItemTouchListener()
,以接收onInterceptTouchEvent()
中的TouchEvent,然后将其传递给listItem及其onTouchListener并停止刷新那里的RecyclerView。
但是,因为我仍然需要关于单击的项目的信息,所以我仍然需要TouchListener上的项目,它仍然返回-1和ACTION\u CANCEL
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;
}
}
公共类DataStoreDataAdapter扩展了RecyclerView.Adapter{
私有OnListItemTouchListener OnListItemTouchListener;
静态类ViewHolder扩展了RecyclerView.ViewHolder实现了View.OnTouchListener{
视图;
文本视图名称;
// ...
ViewHolder(@NonNull视图项视图,OnListItemTouchListener OnListItemTouchListener){
超级(项目视图);
this.Name=itemView.findviewbyd(R.id.NameListItem);
// ...
this.onListItemTouchListener=onListItemTouchListener;
setOnTouchListener(这个);
}
@凌驾
公共布尔onTouch(视图、运动事件、运动事件){
onListItemTouchListener.onListItemTouch(getAdapterPosition(),motionEvent);
返回true;
}
}
公共接口OnListItemTouchListener{
void onListItemTouch(int位置,MotionEvent);
}
公共数据存储数据适配器(上下文上下文,OnListItemTouchListener OnListItemTouchListener){
超级();
this.onListItemTouchListener=onListItemTouchListener;
}
@非空
@凌驾
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,int viewType){
LayoutInflater充气器=LayoutInflater.from(parent.getContext());
视图=充气机。充气(右布局。列表项,父项,false);
返回新的ViewHolder(视图,onListItemTouchListener);
}
@凌驾
public void onBindViewHolder(@NonNull ViewHolder,int位置){
//获取此位置的存储数据
//…从本机库获取资料
//将数据放入视图元素。。。
}
@凌驾
public int getItemCount(){
从本机库返回//…itemCount;
}
}
发生的情况我认为是notifyDataSetChanged仍在进行中,而触摸在进行中,项目没有位置,我认为这无法修复
在我看来,侦听器很好,您真正应该更改的是notifyDataSetChanged()部分
您可以改为使用DiffUtil,以便仅更新实际更改的项目(位置或内容)
有关如何实现此功能的更多信息,请转到此处:
我自己想出来的。 以下是答案:
getAdapterPosition():-1
:
正如我在问题中已经提到的,这是因为notifyDataSetChanged()
重建了整个布局。因此,如果在布局重建和布局过程之间触及列表项,函数将返回-1。Finn Marquart给出了一个解决方案:使用DiffUtils将列表中每个项目的刷新量降至最低。这对我不起作用,因为我需要经常刷新项目。因此,我的解决方案是使用notifyItemRangeChanged(0,DeviceDataAdapter.getItemCount())
而不是notifyDataSetChanged()
,因为这只刷新viewholder中的元素,而不会重新创建整个列表