วิธีไฮไลต์รายการที่เลือกอย่างถูกต้องใน 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

อาจ b ความช่วยเหลือนี้ u amolsawant88.blogspot.in/2015/08/ …
Amol Sawant 96 Kuli

การเลือกมุมมองรายการรีไซเคิล: stackoverflow.com/a/38501676/2648035
Alok Omkar

นี่เป็นการยากที่จะติดตามเมื่อคุณไม่มีอะไรทำงานอยู่แล้วซึ่งไม่ดีตั้งแต่คำตอบติดแท็กและไม่ได้ระบุว่าอะไรจะไปที่ไหน
FirstOne

คำตอบ:


61

ฉันเขียนคลาสอะแดปเตอร์พื้นฐานเพื่อจัดการการเลือกรายการโดยอัตโนมัติด้วย RecyclerView เพิ่งได้รับอะแดปเตอร์ของคุณจากมันและใช้รายการสถานะ drawable กับ state_selected เช่นคุณจะทำอย่างไรกับมุมมองรายการ

ฉันมีBlog Post ที่นี่เกี่ยวกับเรื่องนี้ แต่นี่คือรหัส:

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และเก็บไว้ในตัวแปรสมาชิกที่นั่น
เกร็กเอนนิส

1
คำตอบที่ดี แต่ทำไมใช้ getChildPosition () มีอีกวิธีวิธีหนึ่งคือdeveloper.android.com/reference/android/support/v7/widget/…
skyfishjy

ฉันขอโทษคุณหมายความว่าฉันต้องการนำเข้าTrackSelectionAdapterคลาสของคุณและใช้ในรายการของฉันหรือไม่ ฉันจะ "รับการ์ดเชื่อมต่อจากคลาสของคุณ" ได้อย่างไร ได้โปรดตอบคำถามของฉันได้ไหม ฉันติดอยู่ลึกมาก: stackoverflow.com/questions/29695811/ …
Cristiano Colacillo

2
ฉันลองสิ่งนี้และพบว่าการโทรกลับไปที่ NotifyItemChanged () สองรายการนั้นฆ่าการเลื่อนที่ราบรื่นบนฮาร์ดแวร์ที่ช้ากว่า แย่มากโดยเฉพาะใน Fire TV ก่อนการอัพเดท Lollipop
Redshirt

158

นี่เป็นวิธีที่ง่ายมากที่จะทำ

มีprivate int selectedPos = RecyclerView.NO_POSITION;ในคลาส RecyclerView Adaptor และภายใต้วิธี onBindViewHolder ลอง:

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

ทำงานได้อย่างมีเสน่ห์สำหรับ Navigtional Drawer และ RecyclerView Item Adapters

หมายเหตุ: โปรดใช้สีพื้นหลังในเลย์เอาต์ของคุณโดยใช้ตัวเลือกเช่น 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>

มิฉะนั้น setSelected (.. ) จะไม่ทำอะไรเลยการแก้ปัญหาการแสดงผลนี้ไร้ประโยชน์


12
ผมใช้วิธีนี้กับพื้นหลัง drawable / ชุดตัวเลือกในมุมมองของฉันแถว: <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: วิธีที่ดีและเรียบง่าย! "selectedPosition" ในเมธอด onClick ควรเป็น "selectedPos" หรือไม่
AJW

3
getLayoutPosition () ไม่สามารถใช้งานได้โดยฉัน
ka3ak

3
อย่าเพิ่งใช้ -1 ใช้ RecyclerView.NO_POSITION (ซึ่งคือ -1)
Martin Marconcini

8
@ ka3ak: getLayoutPosition เป็นวิธีการของคลาส ViewHolder ซึ่งวัตถุถูกส่งผ่านเป็นพารามิเตอร์แรกในวิธีการดูผูก ดังนั้นจึงสามารถเข้าถึงได้โดยvieHolder.getLayoutPosition
Tushar Kathuria

129

อัปเดต [26 / กรกฎาคม / 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
    }
}

หวังว่านี่จะช่วยได้


ที่น่ากลัว! ฉันสับสนเล็กน้อยคุณสามารถช่วยฉันเพิ่มพูนคำตอบนี้ได้อย่างไรทำสิ่งที่คุณทำในคำสั่ง else เมื่อคุณคลิกที่รายการที่เลือกไว้แล้วเพื่อให้คุณยกเลิกการเลือก: else { holder.itemView.setBackgroundColor (Color.TRANSPARENT); }
iBobb

แน่นอนใช่. @iBobb สำหรับรายการที่ไม่ถูกเลือกคือตำแหน่งที่ไม่เท่ากับตัวแปร 'select_position' ของเราจะต้องไม่มีพื้นหลัง ฉันใช้สิ่งนี้เพราะ RecyclerView ตามชื่อแนะนำรีไซเคิลแต่ละรายการดังนั้นมันจึงกำหนดพื้นหลังสีเขียวสำหรับแถวสุ่มในขณะที่เรา SCROLL นั่นคือเหตุผลที่ฉันวางส่วนอื่น ๆ (คุณสามารถลองโดยการคอมเม้นท์ส่วนอื่น ๆ แล้วเลื่อนมุมมอง Recycler ของคุณ)
Meet Vora

ฉันไม่คิดว่าคุณเข้าใจคำถามของฉัน คุณรู้ในแอพใด ๆ เมื่อคุณเลือกบางสิ่งมันจะถูกเน้น หากคุณถืออีกครั้งมันจะถูกยกเลิกการเลือก เราจะนำไปใช้อย่างไร ตอนนี้เราสามารถเลือกได้โดยใช้รหัสของคุณและการสัมผัสรายการเดิมไม่ได้ทำอะไรเลย
iBobb

เป็นไปได้ แต่สำหรับสิ่งที่คุณต้องแทนที่ onLongClickListener แทนที่จะเป็น onClickListener ตอนนี้ฉันไม่ว่างจึงไม่สามารถให้รหัสเต็ม แต่คุณควรจะต้องทำแบบนั้น
พบ 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 กับฟังก์ชั่นพื้นฐานทั้งหมดที่เราต้องการ (single + multiselection, highlight, ripple คลิกและลบใน multiselection ฯลฯ ... )

นี่คือ -> http://enoent.fr/blog/2015/01/18/recyclerview-basics/

จากนั้นฉันสามารถสร้างไลบรารี "FlexibleAdapter" ซึ่งขยาย SelectableAdapter ฉันคิดว่านี่เป็นความรับผิดชอบของอะแดปเตอร์จริงๆแล้วคุณไม่จำเป็นต้องเขียนฟังก์ชั่นพื้นฐานของอะแดปเตอร์ใหม่ทุกครั้งปล่อยให้ห้องสมุดทำมันเพื่อให้คุณสามารถนำการใช้งานมาใช้ซ้ำได้

อะแดปเตอร์นี้เร็วมากใช้งานได้นอกกรอบ (คุณไม่จำเป็นต้องขยาย) คุณกำหนดรายการเองสำหรับทุกประเภทการดูที่คุณต้องการ ViewHolder ถูกกำหนดไว้ล่วงหน้า: มีการใช้งานเหตุการณ์ทั่วไปแล้ว: คลิกเพียงครั้งเดียวและแบบยาว จะรักษาสถานะหลังจากหมุนและอื่น ๆ อีกมากมาก

โปรดดูและนำไปใช้ในโครงการของคุณได้ฟรี

https://github.com/davideas/FlexibleAdapter

นอกจากนี้ยังมี Wiki


งานเขียนที่ยอดเยี่ยมเขียนอะแดปเตอร์นั้นดูเหมือนว่ามีประโยชน์มาก สิ่งเดียวคือมันต้องการตัวอย่างและเอกสารประกอบพื้นฐานจริง ๆ ฉันพบว่ามันสับสนเล็กน้อยแม้กระทั่งทำให้มันใช้งานได้ อาจเป็นไปได้ว่ายอดเยี่ยม!
Voy

ใช่ฉันไม่พบข้อมูลที่เพียงพอในการเริ่มต้น ไม่พบการอ้างอิง API แม้ว่าจะมีความคิดเห็นเกี่ยวกับรหัสก็ตาม แอปตัวอย่างดู - ในขณะที่กว้างและมีข้อมูล - ยากมากที่จะเข้าใจโดยไม่ต้องมีความรู้มาก่อนในห้องสมุด ทุกกรณีการใช้งานถูกผูกไว้ด้วยกันมีข้อบ่งชี้เล็กน้อยว่าอะไรแสดงให้เห็นว่ามีการนำคลาสกลับมาใช้ใหม่ตลอดทั้งสถานการณ์ที่แตกต่างกันซึ่งส่งผลให้เกิดการโอเวอร์โหลดด้วยข้อมูล ฉันสร้างปัญหาใหม่ด้วยคำแนะนำเหล่านี้ที่นี่: github.com/davideas/FlexibleAdapter/issues/120
Voy

Wikiได้รับการเขียนใหม่อย่างสมบูรณ์และกำลังจะเสร็จสมบูรณ์
Davideas

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

คุณคิดว่า mSelectedPosition สามารถประกาศว่าเป็นแบบคงที่เพื่อรักษาการเปลี่ยนแปลงการกำหนดค่าได้หรือไม่?
Sathesh

No! มันผิดที่จะทำให้เป็นแบบคงที่มันผิดมาก
Konrad Krakowiak

ใช่. มันอันตราย. แต่เพื่อรักษาการเปลี่ยนแปลงการกำหนดค่า (โดยเฉพาะการหมุนหน้าจอ) เราสามารถประกาศตัวแปรนี้ในกิจกรรมและรับมันจากที่นั่นใช่มั้ย
Sathesh

มีทางออกอีกหลายคน ... คุณสามารถใช้ทะเยอทะยานและหมา, คุณสามารถสร้างวิธี saveInstanceState ของคุณเองสำหรับอะแดปเตอร์และเรียกว่าใน saveInstanceState จากกิจกรรม / Fragment
คอนราด Krakowiak

4

ไม่มีตัวเลือกใน RecyclerView เช่น ListView และ GridView แต่คุณลองทำสิ่งที่ฉันต้องการ

สร้างตัวเลือก drawable ดังนี้

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

จากนั้นตั้งค่า drawable นี้เป็นพื้นหลังของเค้าโครงแถว 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 ด้วย Callback (ตัวอย่าง):

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

ฉันมีปัญหาเดียวกันและฉันแก้มันด้วยวิธีต่อไปนี้:

ไฟล์ xml ที่ใช้สำหรับสร้างแถวใน createViewholder เพียงเพิ่มบรรทัดด้านล่าง:

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

หรือถ้าคุณใช้ frameLayout เป็นพาเรนต์ของแถวรายการ:

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

ในรหัสจาวาภายในตัวยึดมุมมองที่คุณเพิ่มในฟังคลิก:

@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 หากคุณมีสีพื้นหลังที่แตกต่างกันฉันเพิ่งใช้มัน (ฉันใช้การผูกข้อมูล);

Set isSelected ที่ฟังก์ชั่น onClick

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) คุณไม่ได้ให้วิธีการยกเลิกการเปิดใช้งานรายการอื่น ๆ ใน recycler 2) มุมมองถูกนำมาใช้ซ้ำ ดังนั้นจึงเป็นไปได้ว่ามุมมองที่เปิดใช้งานเดียวกันจะปรากฏให้เห็นตามลำดับเมื่อเลื่อน
Anup Sharma
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.