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
    notifyDataSetChanged()
    -方法每10毫秒调用一次,因为会显示时间敏感信息,因此ViewHolder(类扩展为
    RecyclerView.ViewHolder
    )会频繁更新
  • 底层数据来自本机C库,因此将重写适配器方法以从本机库查询数据
  • 在适配器类中实现了从接口派生的,以将onTouch事件从适配器传递给活动。因此,ViewHolder类实现了
    View.OnTouchListener
    ,并通过接口将所有触摸事件和listItem位置(由
    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中添加了一个
    RecyclerView.OnItemTouchListener()
    ,以接收
    onInterceptTouchEvent()
    中的TouchEvent,然后将其传递给listItem及其onTouchListener并停止刷新那里的RecyclerView。 但是,因为我仍然需要关于单击的项目的信息,所以我仍然需要TouchListener上的项目,它仍然返回-1和
    ACTION\u CANCEL
  • 问题:

    在经常更新数据的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;
        }
    
    }
    
    公共类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中的元素,而不会重新创建整个列表
  • 避免在视图保持架中拦截触摸事件: 某个较高的视图截获了触摸手势*,因此