RecyclerView에서 선택한 항목을 올바르게 강조 표시하는 방법은 무엇입니까?

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

여기에 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);

}


답변

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

답변

이것은 매우 간단한 방법입니다.

가지고 private int selectedPos = RecyclerView.NO_POSITION;, 그리고 onBindViewHolder 방법 시도 아래 RecyclerView 어댑터 클래스를 :

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

}

그리고 OnClick 이벤트에서 다음을 수정하십시오.

@Override
public void onClick(View view) {
     notifyItemChanged(selectedPos);
     selectedPos = getLayoutPosition();
     notifyItemChanged(selectedPos);
}

탐색 드로어 및 기타 RecyclerView 항목 어댑터의 매력처럼 작동합니다.

참고 : colabug clarified와 같은 선택기를 사용하여 레이아웃에서 배경색을 사용해야합니다.

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

그렇지 않으면 setSelected (..)는 아무것도하지 않으므로이 솔루션을 쓸모 없게 만듭니다.


답변

업데이트 [26 / Jul / 2017] :

는 AS 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
    }
}

이것이 도움이되기를 바랍니다.


답변

내용을 화면 밖으로 스크롤 한 다음 다시 화면으로 돌리면 구현이 작동 할 수 있습니다. 귀하의 질문에 대해 비슷한 문제가 발생했습니다.

다음 파일 스 니펫이 나를 위해 일하고 있습니다. 내 구현은 다중 선택을 목표로했지만 단일 선택을 강제로 해킹했습니다. (* 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;
}

이 링크 된 질문에서 언급했듯이 viewHolders에 대한 리스너 설정은 onCreateViewHolder에서 수행해야합니다. 나는 이것을 이전에 언급하는 것을 잊었다.


답변

필자는 필요한 모든 기본 기능 (단일 + 다중 선택, 강조 표시, 잔물결, 다중 선택에서 클릭 및 제거 등)과 함께 RecyclerView를 사용하는 방법에 대한 최고의 자습서를 찾았습니다.

여기 있습니다-> http://enoent.fr/blog/2015/01/18/recyclerview-basics/

이를 기반으로 SelectableAdapter를 확장하는 라이브러리 “FlexibleAdapter”를 만들 수있었습니다. 필자는 이것이 어댑터의 책임이어야한다고 생각합니다. 실제로 매번 어댑터의 기본 기능을 다시 작성할 필요가 없으며 라이브러리에서 할 수 있으므로 동일한 구현을 재사용 할 수 있습니다.

이 어댑터는 매우 빠르며 기본적으로 작동합니다 (확장 할 필요는 없습니다). 필요한 모든보기 유형에 맞게 항목을 사용자 정의합니다. ViewHolder는 미리 정의되어 있습니다. 일반적인 이벤트는 이미 구현되어 있습니다. 단일 및 긴 클릭; 그것은 회전 후 상태를 유지하고 훨씬 더 .

프로젝트에서 모양과 모양을 자유롭게 구현하십시오.

https://github.com/davideas/FlexibleAdapter

위키도 제공됩니다.


답변

내 해결책을보십시오. 홀더에서 선택한 위치를 설정하고 View of View로 전달해야한다고 가정합니다. 뷰는 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);
    }
}

답변

RecyclerView에는 ListView 및 GridView와 같은 선택기가 없지만 아래에서 작동하는 것을 시도해보십시오.

아래와 같이 선택기 드로어 블 생성

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
   <shape>
         <solid android:color="@color/blue" />
   </shape>
</item>

<item android:state_pressed="false">
    <shape>
       <solid android:color="@android:color/transparent" />
    </shape>
</item>
</selector>

그런 다음이 드로어 블을 RecyclerView 행 레이아웃의 배경으로 설정하십시오.

android:background="@drawable/selector"