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


146

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

}

포커스 가능한보기를 사용하는 것은 선택한 항목을 추적하는 데 좋지 않습니다. 완전한 해결책에 대한 내 대답을 확인하십시오
Greg Ennis

u는이 도움말 ㄱ 있습니다 amolsawant88.blogspot.in/2015/08/...
아몰 Sawant 96 Kuli

재활용보기 항목 선택 : stackoverflow.com/a/38501676/2648035
Alok Omkar

이미 작동 중인 것이 없으면 따라하기가 어렵습니다 . 답변 태그가 붙어 있기 때문에 나쁘고 어디로 갈지에 대해 많이 지정하지 않습니다.
FirstOne

답변:


61

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

mRecyclerView어디에도 선언되지 않았습니다. 생성자에서 매개 변수로 전달하여 필드에 저장해야합니까?
Pedro

1
미안합니다. 내 어댑터는 프래그먼트의 내부 클래스이므로 리사이클 러보기 필드에 액세스 할 수 있습니다. 그렇지 않으면 예를 매개 변수로 전달할 수 있습니다. 또는 더 잘 처리 onRecyclerViewAttached하고 멤버 변수에서 저장하십시오.
Greg Ennis

1
좋은 대답이지만 왜 getChildPosition ()을 사용합니까? 또 다른 방법이 있습니다 developer.android.com/reference/android/support/v7/widget/…
skyfishjy

죄송합니다. 가져 오기만하면됩니다. TrackSelectionAdapter 수업 내 목록에 사용해야 입니까? "클래스에서 어댑터를 파생 시키려면 어떻게합니까?" 내 질문에 대답 해 주시겠습니까? 너무 깊이 갇혀 있습니다 : stackoverflow.com/questions/29695811/…
Cristiano Colacillo

2
나는 이것을 시도하고 NotifyItemChanged ()에 대한 두 번의 연속 호출이 느린 하드웨어에서 부드러운 스크롤의 유사성을 죽였다는 것을 발견했다. 롤리팝 업데이트 전 Fire TV에서 특히 나빴습니다.
Redshirt

158

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

가지고 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 (..)는 아무것도하지 않으므로이 솔루션을 쓸모 없게 만듭니다.


12
행보기에서 배경 드로어 블 / 선택기를 설정하여이 솔루션을 사용했습니다. <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>
colabug

1
@zIronManBox : 멋지고 간단한 방법! onClick 메소드의 "selectedPosition"이 "selectedPos"로되어 있습니까?
AJW

3
getLayoutPosition ()을 사용할 수 없습니다.
ka3ak

3
-1 만 사용하지 말고 RecyclerView를 사용하십시오 .NO_POSITION; (-1)
Martin Marconcini

8
@ ka3ak : getLayoutPosition은 ViewHolder 클래스의 메소드이며 바인드 뷰 메소드에서 첫 번째 매개 변수로 객체가 전달됩니다. vieHolder.getLayoutPosition
Tushar Kathuria가

129

업데이트 [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
    }
}

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


대단해! 조금 혼란 스럽습니다.이 답변을 확장하고 이미 선택된 항목을 클릭 할 때 else 문에서 수행중인 작업을 수행하는 방법을 구현하는 데 도움이 될 수 있습니까? holder.itemView.setBackgroundColor (Color.TRANSPARENT); }
iBobb

물론 예. @iBobb. NOT SELECTED 항목의 경우, 즉 위치가 'selected_position'변수와 같지 않은 경우 백그라운드가 없어야합니다. RecyclerView는 이름에서 알 수 있듯이 각 항목을 재활용하므로 스크롤하는 동안 임의의 행에 대해 녹색 배경을 설정하므로 ELSE 부분을 배치했기 때문에 이것을 사용했습니다. (ELSE 부분을 주석으로 처리 한 다음 Recycler-view를 스크롤하면됩니다)
Vora 만나기

나는 당신이 내 질문을 이해했다고 생각하지 않습니다. 어떤 앱이든 무언가를 선택하면 강조 표시됩니다. 다시 누르고 있으면 선택이 해제됩니다. 어떻게 구현할까요? 지금은 코드를 사용하여 선택할 수 있으며 동일한 항목을 길게 터치해도 아무런 효과가 없습니다.
iBobb

가능할 수도 있지만 onClickListener 대신 onLongClickListener를 재정의해야하므로 지금은 바빠서 전체 코드를 제공 할 수는 없지만 기본적으로 그렇게해야합니다.
Vora

어떤 이유로 클릭 후 약 100 ~ 200 밀리 초를 강조 표시하는 데 시간이 걸립니다. 왜 그런지 알아? 나는 에뮬레이터에서 이것을 경험하고 내 롤리팝 폰
suku

14

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

다음 파일 스 니펫이 나를 위해 일하고 있습니다. 내 구현은 다중 선택을 목표로했지만 단일 선택을 강제로 해킹했습니다. (* 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에서 수행해야합니다. 나는 이것을 이전에 언급하는 것을 잊었다.


6

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

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

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

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

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

https://github.com/davideas/FlexibleAdapter

위키도 제공됩니다.


그 어댑터를 작성하는 것이 훌륭합니다. 매우 유용합니다. 단지 기본적인 예제와 문서가 실제로 필요하다는 것만으로도 시작하고 실행하는 것이 약간 혼란 스럽습니다. 그래도 위대하다!
Voy

예, 시작하기에 충분한 정보를 찾지 못했습니다. 코드를 염두에두고있는 것으로 보이지만 API 참조를 찾을 수 없습니다. 샘플 앱은 광범위하고 유익하지만 라이브러리에 대한 사전 지식 없이는 이해하기가 매우 어렵습니다. 모든 유스 케이스가 하나로 묶여 있으며, 다양한 시나리오에서 클래스가 재사용되어 정보가 과부하되는 클래스를 보여줍니다. github.com/davideas/FlexibleAdapter/issues/120
Voy

위키 는 완전히 재작 성되었으며 완성되고 있습니다.
Davideas

6

내 해결책을보십시오. 홀더에서 선택한 위치를 설정하고 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);
    }
}

구성 변경을 유지하기 위해 mSelectedPosition을 정적으로 선언 할 수 있다고 생각하십니까?
Sathesh

아니! 정적으로 만드는 것은 잘못이다
Konrad Krakowiak

네. 이건 위험 해. 그러나 구성 변경 (esp. 화면 회전)을 유지하려면이 변수를 활동으로 선언하고 거기에서 가져올 수 있습니다.
Sathesh

많은 해결책이 있습니다 ... getter 및 setter를 사용할 수 있습니다. 어댑터에 대해 고유 한 메소드 saveInstanceState를 작성하고 활동 / 조각의 saveInstanceState에서 호출 할 수 있습니다.
Konrad Krakowiak

4

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"

2
이것은 유용하지 않습니다. 프레스 타임을 수행하는 색상 만 변경됩니다.
Leo Droidcoder

4

인터페이스 및 콜백을 통한 결정. 선택 및 선택 해제 상태로 인터페이스를 만듭니다.

public interface ItemTouchHelperViewHolder {
    /**
     * Called when the {@link ItemTouchHelper} first registers an item as being moved or swiped.
     * Implementations should update the item view to indicate it's active state.
     */
    void onItemSelected();


    /**
     * Called when the {@link ItemTouchHelper} has completed the move or swipe, and the active item
     * state should be cleared.
     */
    void onItemClear();
}

ViewHolder에서 인터페이스를 구현하십시오.

   public static class ItemViewHolder extends RecyclerView.ViewHolder implements
            ItemTouchHelperViewHolder {

        public LinearLayout container;
        public PositionCardView content;

        public ItemViewHolder(View itemView) {
            super(itemView);
            container = (LinearLayout) itemView;
            content = (PositionCardView) itemView.findViewById(R.id.content);

        }

               @Override
    public void onItemSelected() {
        /**
         * Here change of item
         */
        container.setBackgroundColor(Color.LTGRAY);
    }

    @Override
    public void onItemClear() {
        /**
         * Here change of item
         */
        container.setBackgroundColor(Color.WHITE);
    }
}

콜백에서 상태 변경 실행 :

public class ItemTouchHelperCallback extends ItemTouchHelper.Callback {

    private final ItemTouchHelperAdapter mAdapter;

    public ItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
        this.mAdapter = adapter;
    }

    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
        return true;
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        int swipeFlags = ItemTouchHelper.END;
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        ...
    }

    @Override
    public void onSwiped(final RecyclerView.ViewHolder viewHolder, int direction) {
        ...
    }

    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            if (viewHolder instanceof ItemTouchHelperViewHolder) {
                ItemTouchHelperViewHolder itemViewHolder =
                        (ItemTouchHelperViewHolder) viewHolder;
                itemViewHolder.onItemSelected();
            }
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        if (viewHolder instanceof ItemTouchHelperViewHolder) {
            ItemTouchHelperViewHolder itemViewHolder =
                    (ItemTouchHelperViewHolder) viewHolder;
            itemViewHolder.onItemClear();
        }
    }   
}

콜백으로 RecyclerView 생성 (예) :

mAdapter = new BuyItemsRecyclerListAdapter(MainActivity.this, positionsList, new ArrayList<BuyItem>());
positionsList.setAdapter(mAdapter);
positionsList.setLayoutManager(new LinearLayoutManager(this));
ItemTouchHelper.Callback callback = new ItemTouchHelperCallback(mAdapter);
mItemTouchHelper = new ItemTouchHelper(callback);
mItemTouchHelper.attachToRecyclerView(positionsList);

iPaulPro의 기사에서 더 많은 것을보십시오 : https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-6a6f0c422efd#.6gh29uaaz


3

이것은 내 솔루션입니다. 항목 (또는 그룹)을 설정하고 다른 클릭으로 선택을 취소 할 수 있습니다.

 private final ArrayList<Integer> seleccionados = new ArrayList<>();
@Override
    public void onBindViewHolder(final ViewHolder viewHolder, final int i) {
        viewHolder.san.setText(android_versions.get(i).getAndroid_version_name());
        if (!seleccionados.contains(i)){ 
            viewHolder.inside.setCardBackgroundColor(Color.LTGRAY);
        }
        else {
            viewHolder.inside.setCardBackgroundColor(Color.BLUE);
        }
        viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (seleccionados.contains(i)){
                    seleccionados.remove(seleccionados.indexOf(i)); 
                    viewHolder.inside.setCardBackgroundColor(Color.LTGRAY);
                } else { 
                    seleccionados.add(i);
                    viewHolder.inside.setCardBackgroundColor(Color.BLUE);
                }
            }
        });
    }

2

나는 같은 문제가 있었고 다음과 같이 해결했습니다.

createViewholder 내부에 행을 만드는 데 사용되는 xml 파일은 아래 행을 추가하십시오.

 android:clickable="true"
 android:focusableInTouchMode="true"
 android:background="?attr/selectableItemBackgroundBorderless"

또는 frameLayout을 행 항목의 부모로 사용하는 경우 :

android:clickable="true"
android:focusableInTouchMode="true"
android:foreground="?attr/selectableItemBackgroundBorderless"

클릭 리스너에 추가 한 뷰 홀더 내부의 Java 코드에서 :

@Override
   public void onClick(View v) {

    //ur other code here
    v.setPressed(true);
 }

1

웹 에서이 문제에 대한 좋은 해결책을 찾지 못하여 직접 해결했습니다. 이 문제로 고통받는 사람들이 많이 있습니다. 따라서 여기에서 내 솔루션을 공유하고 싶습니다.

스크롤하는 동안 행이 재활용됩니다. 따라서 확인란과 강조 표시된 행이 제대로 작동하지 않습니다. 아래 어댑터 클래스를 작성 하여이 문제를 해결했습니다.

또한 전체 프로젝트를 구현합니다. 이 프로젝트에서는 여러 개의 확인란을 선택할 수 있습니다. 선택한 확인란을 포함하는 행이 강조 표시됩니다. 더 중요한 것은 스크롤하는 동안 손실되지 않습니다. 링크에서 다운로드 할 수 있습니다.

https://www.dropbox.com/s/ssm58w62gw32i29/recyclerView_checkbox_highlight.zip?dl=0

    public class RV_Adapter extends RecyclerView.Adapter<RV_Adapter.ViewHolder> {
        public ArrayList<String> list;
        boolean[] checkBoxState;
        MainActivity mainActivity;
        MyFragment myFragment;
        View firstview;

        private Context context;

        FrameLayout framelayout;

        public RV_Adapter() {

      }

        public RV_Adapter(Context context, MyFragment m, ArrayList<String> list ) {
          this.list = list;
          myFragment = m;
          this.context = context;
          mainActivity = (MainActivity) context;
          checkBoxState = new boolean[list.size()];
          // relativeLayoutState = new boolean[list.size()];
        }

        public class ViewHolder extends RecyclerView.ViewHolder  {
            public TextView textView;
            public CheckBox checkBox;
            RelativeLayout relativeLayout;
            MainActivity mainActivity;
            MyFragment myFragment;
            public ViewHolder(View v,MainActivity mainActivity,MyFragment m) {
                super(v);
                textView = (TextView) v.findViewById(R.id.tv_foodname);
                /**/
                checkBox= (CheckBox) v.findViewById(R.id.checkBox);
                relativeLayout = (RelativeLayout)v.findViewById(R.id.relativelayout);
                this.mainActivity = mainActivity;
                this.myFragment = m;
                framelayout = (FrameLayout) v.findViewById(R.id.framelayout);
                framelayout.setOnLongClickListener(m);
            }

        }

        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            firstview = inflater.inflate(R.layout.row, parent, false);
            return new ViewHolder(firstview,mainActivity, myFragment);
        }

        @Override
        public void onBindViewHolder( final ViewHolder holder,  final int position) {

            holder.textView.setText(list.get(position));

            holder.itemView.setOnClickListener(new View.OnClickListener() {
              @Override
              public void onClick(View v) {

              }
            });

            // When action mode is active, checkboxes are displayed on each row, handle views(move icons) on each row are disappered.
            if(!myFragment.is_in_action_mode)
            {

              holder.checkBox.setVisibility(View.GONE);
            }
            else
            {
              holder.checkBox.setVisibility(View.VISIBLE);
              holder.checkBox.setChecked(false);
            }

              holder.checkBox.setTag(position);

              holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){
                @Override
                public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                  if(compoundButton.isPressed()) // ekrandan kaybolan checkbox'lar otomatik olarak state degistiriyordu ve bu listener method cagiriliyordu, bunu onlemek icin isPressed() method'u ile kullanici mi basmis diye kontrol ediyorum.
                  {
                    int getPosition = (Integer) compoundButton.getTag();  // Here we get the position that we have set for the checkbox using setTag.
                    checkBoxState[getPosition] = compoundButton.isChecked(); // Set the value of checkbox to maintain its state.
                    //relativeLayoutState[getPosition] = compoundButton.isChecked();

                  if(checkBoxState[getPosition] && getPosition == position )
                    holder.relativeLayout.setBackgroundResource(R.color.food_selected); /** Change background color of the selected items in list view  **/
                  else
                    holder.relativeLayout.setBackgroundResource(R.color.food_unselected); /** Change background color of the selected items in list view  **/
                    myFragment.prepareselection(compoundButton, getPosition, holder.relativeLayout);

                  }
                }
              });
              holder.checkBox.setChecked(checkBoxState[position]);

              if(checkBoxState[position]  )
                holder.relativeLayout.setBackgroundResource(R.color.food_selected); /** Change background color of the selected items in list view  **/
              else
                holder.relativeLayout.setBackgroundResource(R.color.food_unselected);
        }



        @Override
        public int getItemCount() {
            return list.size();
        }

        public void updateList(ArrayList<String> newList){
          this.list = newList;
          checkBoxState = new boolean[list.size()+1];
        }

      public void resetCheckBoxState(){
        checkBoxState = null;
        checkBoxState = new boolean[list.size()];
      }

    }

앱의 스크린 샷 :

screen1 screen2 screen3 screen4


-1

private int selected_position = -1;시작할 때 항목이 선택되지 않도록 설정하십시오 .

 @Override
 public void onBindViewHolder(final OrdersHolder holder, final int position) {
    final Order order = orders.get(position);
    holder.bind(order);
    if(selected_position == position){
        //changes background color of selected item in RecyclerView
        holder.itemView.setBackgroundColor(Color.GREEN);
    } else {
        holder.itemView.setBackgroundColor(Color.TRANSPARENT);
        //this updated an order property by status in DB
        order.setProductStatus("0");
    }
    holder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //status switch and DB update
            if (order.getProductStatus().equals("0")) {
                order.setProductStatus("1");
                notifyItemChanged(selected_position);
                selected_position = position;
                notifyItemChanged(selected_position);
             } else {
                if (order.getProductStatus().equals("1")){
                    //calls for interface implementation in
                    //MainActivity which opens a new fragment with 
                    //selected item details 
                    listener.onOrderSelected(order);
                }
             }
         }
     });
}

-1

그냥 추가 android:background="?attr/selectableItemBackgroundBorderless" 하면 배경색이 없지만하는 setSelected 방법을 사용하는 것을 잊지하지 않는 경우 작동합니다. 배경색이 다른 경우 방금 이것을 사용했습니다 (데이터 바인딩을 사용하고 있습니다).

onClick 기능에서 isSelected 설정

b.setIsSelected(true);

그리고 이것을 xml에 추가하십시오;

android:background="@{ isSelected ? @color/{color selected} : @color/{color not selected} }"

-1

선택기 만들기 :

 <?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@color/Green_10" android:state_activated="true" />
    <item android:drawable="@color/Transparent" />
</selector>

목록 항목 레이아웃에서 배경으로 설정

   <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:background="@drawable/selector_attentions_list_item"
        android:layout_width="match_parent"
        android:layout_height="64dp">

어댑터에서보기에 OnClickListener 추가 (onBind 메소드)

 @Suppress("UNCHECKED_CAST")
    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        fun bindItems(item: T) {
            initItemView(itemView, item)
            itemView.tag = item
            if (isClickable) {
                itemView.setOnClickListener(onClickListener)
            }
        }
    }

onClick 이벤트에서보기를 활성화하십시오.

 fun onItemClicked(view: View){
        view.isActivated = true
    }

2
이 문제에는 2 가지 문제가 있습니다. 1) 리사이클 러에서 다른 항목의 활성화를 취소 할 수있는 방법이 없습니다. 2) 뷰가 재사용됩니다. 따라서 스크롤 할 때 동일한 활성화 된보기가 순서대로 표시 될 수 있습니다.
Anup Sharma
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.