如何在RecyclerView上正确突出显示所选项目?

use*_*721 131 android recycler-adapter android-recyclerview

我正在尝试使用a RecyclerView作为横向ListView.我试图弄清楚如何突出显示所选项目.当我点击其中一个项目时,它会被选中并正确突出显示,但是当我点击另一个项目时,第二个项目会被旧项目突出显示.

这是我的onClick函数:

@Override
public void onClick(View view) {

    if(selectedListItem!=null){
        Log.d(TAG, "selectedListItem " + getPosition() + " " + item);
        selectedListItem.setBackgroundColor(Color.RED);
    }
    Log.d(TAG, "onClick " + getPosition() + " " + item);
    viewHolderListener.onIndexChanged(getPosition());
    selectedPosition = getPosition();
    view.setBackgroundColor(Color.CYAN); 
    selectedListItem = view;
}
Run Code Online (Sandbox Code Playgroud)

这是onBindViewHolder:

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {   
    viewHolder.setItem(fruitsData[position]);
    if(selectedPosition == position)
        viewHolder.itemView.setBackgroundColor(Color.CYAN);    
    else
        viewHolder.itemView.setBackgroundColor(Color.RED);

}
Run Code Online (Sandbox Code Playgroud)

zIr*_*Box 143

这是一种非常简单的方法.

private int selectedPos = RecyclerView.NO_POSITION;在RecyclerView Adapter类中有一个,并在onBindViewHolder方法下尝试:

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {   
    viewHolder.itemView.setSelected(selectedPos == position);

}
Run Code Online (Sandbox Code Playgroud)

并在您的OnClick事件中修改:

@Override
public void onClick(View view) {
     notifyItemChanged(selectedPos);
     selectedPos = getLayoutPosition();
     notifyItemChanged(selectedPos); 
}
Run Code Online (Sandbox Code Playgroud)

就像Navigtional Drawer和其他RecyclerView物品适配器的魅力一样.

注意:务必使用像colabug澄清的选择器在布局中使用背景颜色:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:drawable="@color/pressed_color" android:state_pressed="true"/>
  <item android:drawable="@color/selected_color" android:state_selected="true"/>
  <item android:drawable="@color/focused_color" android:state_focused="true"/>
</selector>
Run Code Online (Sandbox Code Playgroud)

否则setSelected(..)将不执行任何操作,使此解决方案无效.

  • 我在我的行视图中使用了这个带有背景drawable/selector设置的解决方案:```<selector xmlns:android ="http://schemas.android.com/apk/res/android"> <item android:drawable =" @ color/pressed_color"android:state_pressed ="true"/> <item android:drawable ="@ color/selected_color"android:state_selected ="true"/> <item android:drawable ="@ color/focused_color"android:state_focused ="true"/> </ selector>``` (12认同)
  • @ ka3ak:getLayoutPosition是ViewHolder类的方法,其对象作为绑定视图方法中的第一个参数传递.因此可以通过`vieHolder.getLayoutPosition`访问它 (7认同)
  • 我没有getLayoutPosition(). (3认同)
  • 不要只使用-1,使用RecyclerView.NO_POSITION; (-1) (3认同)

Mee*_*eet 119

更新[26/Jul/2017]:

正如Pawan在评论中提到的关于不使用该固定位置的IDE警告,我刚刚修改了我的代码如下.点击监听器被移动到ViewHolder,在那里我得到位置使用getAdapterPosition()方法

int selected_position = 0; // You have to set this globally in the Adapter class

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    Item item = items.get(position);

    // Here I am just highlighting the background
    holder.itemView.setBackgroundColor(selected_position == position ? Color.GREEN : Color.TRANSPARENT);
}

public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    public ViewHolder(View itemView) {
        super(itemView);
        itemView.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        // Below line is just like a safety check, because sometimes holder could be null,
        // in that case, getAdapterPosition() will return RecyclerView.NO_POSITION
        if (getAdapterPosition() == RecyclerView.NO_POSITION) return;

        // Updating old as well as new positions
        notifyItemChanged(selected_position);
        selected_position = getAdapterPosition();
        notifyItemChanged(selected_position);

        // Do your another stuff for your onClick
    }
}
Run Code Online (Sandbox Code Playgroud)

希望这会有所帮助.


Gre*_*nis 57

我写了一个基础适配器类来自动处理使用RecyclerView的项目选择.只需从中派生适配器并使用state_selected的可绘制状态列表,就像使用列表视图一样.

我有一篇关于它的博客文章,但这里是代码:

public abstract class TrackSelectionAdapter<VH extends TrackSelectionAdapter.ViewHolder> extends RecyclerView.Adapter<VH> {
    // Start with first item selected
    private int focusedItem = 0;

    @Override
    public void onAttachedToRecyclerView(final RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);

        // Handle key up and key down and attempt to move selection
        recyclerView.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                RecyclerView.LayoutManager lm = recyclerView.getLayoutManager();

                // Return false if scrolled to the bounds and allow focus to move off the list
                if (event.getAction() == KeyEvent.ACTION_DOWN) {
                    if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
                        return tryMoveSelection(lm, 1);
                    } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
                        return tryMoveSelection(lm, -1);
                    }
                }

                return false;
            }
        });
    }

    private boolean tryMoveSelection(RecyclerView.LayoutManager lm, int direction) {
        int tryFocusItem = focusedItem + direction;

        // If still within valid bounds, move the selection, notify to redraw, and scroll
        if (tryFocusItem >= 0 && tryFocusItem < getItemCount()) {
            notifyItemChanged(focusedItem);
            focusedItem = tryFocusItem;
            notifyItemChanged(focusedItem);
            lm.scrollToPosition(focusedItem);
            return true;
        }

        return false;
    }

    @Override
    public void onBindViewHolder(VH viewHolder, int i) {
        // Set selected state; use a state list drawable to style the view
        viewHolder.itemView.setSelected(focusedItem == i);
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(View itemView) {
            super(itemView);

            // Handle item click and set the selection
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // Redraw the old selection and the new
                    notifyItemChanged(focusedItem);
                    focusedItem = getLayoutPosition();
                    notifyItemChanged(focusedItem);
                }
            });
        }
    }
} 
Run Code Online (Sandbox Code Playgroud)

  • 我尝试了这个,发现对 NotifyItemChanged() 的两次背靠背调用消除了在较慢的硬件上平滑滚动的任何表面现象。在 Lollipop 更新之前,Fire TV 上的表现尤其糟糕 (2认同)

小智 13

如果您将内容滚出视图然后返回视图,则您的实现可能会有效.当我提出你的问题时,我遇到了类似的问题.

以下文件片段对我有用.我的实现是针对多种选择,但我在那里投入了一个强制单一选择.(*1)

// an array of selected items (Integer indices) 
private final ArrayList<Integer> selected = new ArrayList<>();

// items coming into view
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
    // each time an item comes into view, its position is checked
    // against "selected" indices
    if (!selected.contains(position)){
        // view not selected
        holder.parent.setBackgroundColor(Color.LTGRAY);
    }
    else
        // view is selected
        holder.parent.setBackgroundColor(Color.CYAN);
}

// selecting items
@Override
public boolean onLongClick(View v) {

        // set color immediately.
        v.setBackgroundColor(Color.CYAN);

        // (*1)
        // forcing single selection here
        if (selected.isEmpty()){
            selected.add(position);
        }else {
            int oldSelected = selected.get(0);
            selected.clear();
            selected.add(position);
            // we do not notify that an item has been selected
            // because that work is done here.  we instead send
            // notifications for items to be deselected
            notifyItemChanged(oldSelected);
        }
        return false;
}
Run Code Online (Sandbox Code Playgroud)

正如指出的在这个链接的问题,为viewHolders设置听众应该onCreateViewHolder完成.我以前忘了提这个.


Kon*_*iak 6

看看我的解决方案.我想你应该在持有人中设置选定的位置并将其作为标记传递.应该在onCreateViewHolder(...)方法中设置视图.还有正确的位置来设置侦听器的视图,如OnClickListener或LongClickListener.

请查看下面的示例并阅读代码注释.

public class MyListAdapter extends RecyclerView.Adapter<MyListAdapter.ViewHolder> {
    //Here is current selection position
    private int mSelectedPosition = 0;
    private OnMyListItemClick mOnMainMenuClickListener = OnMyListItemClick.NULL;

    ...

    // constructor, method which allow to set list yourObjectList

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        //here you prepare your view 
        // inflate it
        // set listener for it
        final ViewHolder result = new ViewHolder(view);
        final View view =  LayoutInflater.from(parent.getContext()).inflate(R.layout.your_view_layout, parent, false);
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //here you set your current position from holder of clicked view
                mSelectedPosition = result.getAdapterPosition();

                //here you pass object from your list - item value which you clicked
                mOnMainMenuClickListener.onMyListItemClick(yourObjectList.get(mSelectedPosition));

                //here you inform view that something was change - view will be invalidated
                notifyDataSetChanged();
            }
        });
        return result;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        final YourObject yourObject = yourObjectList.get(position);

        holder.bind(yourObject);
        if(mSelectedPosition == position)
            holder.itemView.setBackgroundColor(Color.CYAN);
        else
            holder.itemView.setBackgroundColor(Color.RED);
    }

    // you can create your own listener which you set for adapter
    public void setOnMainMenuClickListener(OnMyListItemClick onMyListItemClick) {
        mOnMainMenuClickListener = onMyListItemClick == null ? OnMyListItemClick.NULL : onMyListItemClick;
    }

    static class ViewHolder extends RecyclerView.ViewHolder {


        ViewHolder(View view) {
            super(view);
        }

        private void bind(YourObject object){
            //bind view with yourObject
        }
    }

    public interface OnMyListItemClick {
        OnMyListItemClick NULL = new OnMyListItemClick() {
            @Override
            public void onMyListItemClick(YourObject item) {

            }
        };

        void onMyListItemClick(YourObject item);
    }
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*eas 6

我想,我已经找到了关于如何使用RecyclerView以及我们需要的所有基本功能的最佳教程(单选+多选,高亮,涟漪,点击和删除多选等等).

这是 - > http://enoent.fr/blog/2015/01/18/recyclerview-basics/

基于此,我能够创建一个库"FlexibleAdapter",它扩展了SelectableAdapter.我认为这必须是Adapter的责任,实际上你不需要每次都重写Adapter的基本功能,让库来做,所以你可以重用相同的实现.

这个适配器非常快,它开箱即用(你不需要扩展它); 您可以为所需的每种视图类型自定义项目; ViewHolder是预定义的:已经实现了常见事件:单击和长击; 它在旋转后保持状态,并且更多.

请在您的项目中实现它.

https://github.com/davideas/FlexibleAdapter

Wiki也可用.