简体   繁体   English

直接从RecyclerView按位置访问ViewHolder

[英]Accessing a ViewHolder, directly from the RecyclerView, by position

I have a RecyclerView, which displays a series of views derived from a database. 我有一个RecyclerView,它显示一系列从数据库派生的视图。 I wish to confirm that the user has selected one of the views by highlighting its edges, and I have succeeded in doing so. 我希望通过突出显示其边缘来确认用户已选择其中一个视图,并且我已经成功地做到了。 If the user proceeds to select a differant option, I wish to remove the highlight on the original selection. 如果用户继续选择其他选项,则希望删除原始选择中的突出显示。 This is where I have ran into a bit of trouble. 这是我遇到麻烦的地方。

The initial highlight was no trouble, as I was doing it, internally. 最初的亮点是内部工作,就像我在做的一样。 However, I do not know how to access the previous view with only its adapter position. 但是,我不知道如何仅使用其适配器位置访问前一个视图。 I have been searching StackOverflow for about an hour now, as I can not find much to go on in the Android API, or from google. 我一直在搜索StackOverflow大约一个小时,因为在Android API或google中找不到很多东西。 Many users appear to be asking similar questions, but ultimately, subtle differences void any useful answers. 许多用户似乎在问类似的问题,但最终,细微的差别使任何有用的答案都告一段落。

Inside my ViewHolder , which is an internal public class of my RecyclerView , I have an OnClickListener as follows: 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());
    }

This points back to my RecyclerView , and frankly, I am yet to add any working code to my UpdateSelected(int position) method. 这指向我的RecyclerView ,坦率地说,我尚未将任何工作代码添加到我的UpdateSelected(int position)方法中。

If it helps, I intend to have it function like this: 如果有帮助,我打算使其具有以下功能:

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.
    }
}

I have noticed users advising others to use getLayoutPosition() to derive the position for UI purposes, unless they specifically wish to continue working with the internal data, which is what I intend to do. 我注意到用户建议其他用户使用getLayoutPosition()来出于UI的目的获取位置,除非他们特别希望继续使用内部数据,这是我打算做的。

How do I access a particular ViewHolder from my RecyclerView, using its position? 如何使用其位置从RecyclerView访问特定的ViewHolder?

You could do something like this. 你可以做这样的事情。

In your adapter , create a member variable which will keep track of the selected item position. 在您的适配器中,创建一个成员变量,该变量将跟踪所选项目的位置。

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

Then in your onBindViewHolder of recycler view adapter 然后在您的回收站视图适配器的onBindViewHolder

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

Finally in onClick of your viewholder 最后,在您的观看者的onClick中

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

You can access the viewholder by it's position from recyclerview 您可以通过recyclerview的位置访问该viewholder

Here is my implementation 这是我的实现

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) {

            }

        }));

and create the Class 并创建类

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);
    }
}

Instead of swapping the background resource manually, you can instead mark the view as selected or not, and have Android change the background using drawable selectors. 您无需手动交换背景资源,而可以将视图标记为已选中或未选中,并让Android使用可绘制的选择器更改背景。

res/drawable/background.xml : 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 would be set as the android:background in your item's layout XML. background.xml将被设置为项目布局XML中的android:background

Then in your onBindViewHolder(ViewHolder holder, int position) you update the state of the Android View: 然后在您的onBindViewHolder(ViewHolder holder, int position)更新Android View的状态:

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

For me, I would prefer having an Adapter which was dumber and had less logic. 对我来说,我更喜欢使用笨拙且逻辑较少的适配器。 You can do this by keeping a list of ViewModels (data classes that represent what you'll show in the view - no more, no less) where one of the properties could be the selected flag (in each ViewModel). 您可以通过保留ViewModels列表(表示要在视图中显示的数据类的数据类-最多也不少)来实现,其中属性之一可以是selected标志(在每个ViewModel中)。

When the user selects or deselects a View, a callback issues an update to the presenter, which then updates the adapter, and notifies the adapter which positions have been updated. 当用户选择或取消选择视图时,回调将向演示者发出更新,演示者随后更新适配器,并通知适配器哪些位置已更新。 This would mean keeping the current selected item (preferably ID, but could store ViewModel or position) in the presenter so you can update it. 这意味着将当前选定的项(最好是ID,但可以存储ViewModel或位置)保留在演示者中,以便您可以对其进行更新。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM