【问题标题】:Accessing a ViewHolder, directly from the RecyclerView, by position按位置直接从 RecyclerView 访问 ViewHolder
【发布时间】:2016-10-03 10:48:53
【问题描述】:

我有一个 RecyclerView,它显示了从数据库派生的一系列视图。我希望通过突出显示其边缘来确认用户已选择其中一个视图,并且我已经成功地这样做了。如果用户继续选择不同的选项,我希望删除原始选择上的突出显示。这是我遇到了一些麻烦的地方。

最初的亮点并不麻烦,因为我在内部这样做。但是,我不知道如何仅使用其适配器位置访问上一个视图。我已经搜索 StackOverflow 大约一个小时了,因为我在 Android API 或谷歌中找不到太多内容。许多用户似乎都在问类似的问题,但最终,细微的差异会导致任何有用的答案无效。

在我的ViewHolder 内部,这是我的RecyclerView 的内部public class,我有一个OnClickListener,如下所示:

@Override
    public void onClick(View view) {
        if(!selected) {
            selected = true;
            view.setBackgroundResource(R.drawable.default_selection_background);
        } else {
            selected = false;
            view.setBackgroundResource(0);
        }

        UpdateSelected(getAdapterPosition());
    }

这又指向了我的RecyclerView,坦率地说,我还没有将任何工作代码添加到我的UpdateSelected(int position) 方法中。

如果有帮助,我打算让它像这样运行:

void UpdateSelected(int position) {
    if(position != currentlySelected) {
        ViewHolder[currentlySelected].Deselect();
    }
}

public class ViewHolder extends RecyclerView.ViewHolder {
    ...

    public void Deselect() {
        // and from here, I can go on as normal.
    }
}

我注意到用户建议其他人使用getLayoutPosition() 来获取用于 UI 目的的位置,除非他们特别希望继续使用内部数据,这正是我打算做的。

如何从我的 RecyclerView 访问特定的 ViewHolder,使用它的位置?

【问题讨论】:

    标签: java android android-recyclerview android-viewholder


    【解决方案1】:

    你可以这样做。

    在您的适配器中,创建一个成员变量来跟踪所选项目的位置。

     int selectedPosition = -1; // -1 some default value for nothing selected
    

    然后在你的 onBindViewHolder 的回收器视图适配器中

      int backgroundRes = (position == selectedPosition)? R.drawable.default_selection_background : 0;
      view.setBackgroundResource(backgroundRes);
    

    终于在你的viewholder的onClick中

     @Override
     public void onClick(View view) {
       selectedPosition = getAdapterPosition();
       notifyDataSetChanged();
     }
    

    【讨论】:

    • 太棒了!奇迹般有效。我必须做的唯一更改是在onBindViewHolder 中,它可以立即访问view。我只是使用viewHolder.itemView.setBackgroundResource(backgroundRes)。为了效率,我也只有notifyItemChangedpreviousSelectioncurrentSelection,但非常感谢。
    • 我也面临同样的问题。所以我尝试了那个解决方案,但在我的情况下不起作用。 @Gnemlock
    • 我只是偶然发现了这个答案,虽然很好,但我建议将notifyDataSetChanged() 更改为notifyItemChanged(selectedPosition)。当您可以准确地告诉适配器哪些视图需要更新时,最好避免调用notifyDataSetChanged()
    【解决方案2】:

    您可以通过 recyclerview 中的位置访问查看器

    这是我的实现

    recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(this,
                    recyclerView, new RecyclerItemClickListener.OnItemClickListener() {
                @Override
                public void onItemClick(View view, int position) {
                   /*your coding */
                }
    
                @Override
                public void onItemLongClick(View view, int position) {
    
                }
    
            }));
    

    并创建类

    public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
    
        private OnItemClickListener mListener;
        private GestureDetector mGestureDetector;
        public RecyclerItemClickListener(Context context, final RecyclerView recyclerView, OnItemClickListener listener) {
            mListener = listener;
    
            mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onSingleTapUp(MotionEvent e) {
                    return true;
                }
                @Override
                public void onLongPress(MotionEvent e) {
                    View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());
    
                    if (childView != null && mListener != null) {
                        mListener.onItemLongClick(childView, recyclerView.getChildPosition(childView));
                    }
                }
            });
        }
    
        @Override
        public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
            View childView = view.findChildViewUnder(e.getX(), e.getY());
            if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
                mListener.onItemClick(childView, view.getChildPosition(childView));
            }
            return false;
        }
    
        @Override
        public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
        }
    
        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    
        }
        public interface OnItemClickListener {
            void onItemClick(View view, int position);
            void onItemLongClick(View view, int position);
        }
    }
    

    【讨论】:

      【解决方案3】:

      您可以将视图标记为选中或未选中,并让 Android 使用可绘制选择器更改背景,而不是手动交换背景资源。

      res/drawable/background.xml

      <?xml version="1.0" encoding="utf-8"?>
      <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_selected="true" android:drawable="@drawable/default_selection_background" />
        <item android:drawable="@null" />
      </selector>
      

      background.xml 将在您的项目布局 XML 中设置为 android:background

      然后在您的onBindViewHolder(ViewHolder holder, int position) 中更新 Android 视图的状态:

      @Override
      public void onBindViewHolder(ViewHolder holder, int position) {
          boolean isSelected = position == selectedPosition;
          holder.itemView.setSelected(isSelected);
      }
      

      对我来说,我更喜欢有一个更笨且逻辑更少的适配器。您可以通过保留 ViewModel 列表(代表您将在视图中显示的内容的数据类 - 不多也不少)来做到这一点,其中一个属性可以是选定的标志(在每个 ViewModel 中)。

      当用户选择或取消选择 View 时,回调会向 Presenter 发出更新,然后 Presenter 会更新适配器,并通知适配器哪些位置已更新。这意味着将当前选定的项目(最好是 ID,但可以存储 ViewModel 或位置)保留在 Presenter 中,以便您可以更新它。

      【讨论】:

        猜你喜欢
        • 2020-12-09
        • 2020-05-30
        • 2017-08-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-02-17
        相关资源
        最近更新 更多