รายการที่ขยายได้ด้วย RecyclerView?


95

เป็นไปได้ที่จะใช้รายการที่ขยายได้กับ RecyclerView ใหม่? ชอบ ExpandableListView ไหม


1
คุณสามารถดูนาฬิกา android ในแหล่งที่มาของ Google บน android 6.0
zys

@zys ฉันจะหาซอร์สโค้ดตัวอย่างนาฬิกา Android นี้ได้ที่ไหน
AppiDevo

1
คุณสามารถใช้ viewType ที่แตกต่างกันเพื่อโหลดเลย์เอาต์ที่แตกต่างกันเมื่อคุณคลิกปุ่มขยายโซลูชันนี้ใช้โดยนาฬิกา Android: android.googlesource.com/platform/packages/apps/DeskClock
zys

ดูคำตอบง่ายๆของฉัน stackoverflow.com/a/48092441/5962715
Kiran Benny Joseph

สำหรับสองระดับthoughtbot.com/blog/introducing-expandablerecyclerview สำหรับสามระดับขึ้นไป: blog.usejournal.com/… , github.com/bmelnychuk/AndroidTreeView , karthicandroid.blogspot.com/2016/08/… .
CoolMind

คำตอบ:


124

สิ่งนี้ทำได้ง่ายด้วย LayoutManagers หุ้นทุกอย่างขึ้นอยู่กับวิธีที่คุณจัดการอะแดปเตอร์ของคุณ

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

อัปเดต: Ryan Brooks ได้เขียนบทความเกี่ยวกับวิธีการทำสิ่งนี้แล้ว


ข้อเสนอแนะที่ดี สงสัยว่าทำไมไม่มีใครโหวตคำตอบนี้ !!
x-treme

ฉันจะดูเกี่ยวกับการเพิ่มสิ่งนี้เป็นตัวอย่างใน SuperSLiM เป็นส่วนหนึ่งของรุ่นถัดไป
Tonic Artos

Ryan Brooks ได้เขียนบทความเกี่ยวกับวิธีการทำเช่นนี้แล้ว
Tonic Artos

ข้อเสนอแนะที่ดี ทำไมคำตอบนี้ถึงอยู่ในหน้าเว็บจนต้องเลื่อนลงไปถึงจะเจอ ควรแสดงไว้ที่ด้านบนสุดเพื่อให้ผู้คนจำนวนมากสามารถค้นหาคำตอบที่สวยงามนี้ได้ง่ายขึ้น
RestInPeace

1
Ryan Brooks ทำเครื่องหมายห้องสมุดของเขาว่าเลิกใช้งานแล้ว ฉันสงสัยว่าเป็นเพียงการที่เขาหยุดสนับสนุนหรือปรากฎว่าวิธีนี้ทำลายบางสิ่งหรือสร้างการรั่วไหลของหน่วยความจำหรือ sth ...
Varvara Kalinina

6

รับตัวอย่างการติดตั้งโค้ดจากที่นี่

ตั้งค่า ValueAnimator ภายใน onClick ของ ViewHolder

@Override
public void onClick(final View view) {
    if (mOriginalHeight == 0) {
        mOriginalHeight = view.getHeight();
    }
    ValueAnimator valueAnimator;
    if (!mIsViewExpanded) {
        mIsViewExpanded = true;
        valueAnimator = ValueAnimator.ofInt(mOriginalHeight, mOriginalHeight + (int) (mOriginalHeight * 1.5));
    } else {
        mIsViewExpanded = false;
        valueAnimator = ValueAnimator.ofInt(mOriginalHeight + (int) (mOriginalHeight * 1.5), mOriginalHeight);
    }
    valueAnimator.setDuration(300);
    valueAnimator.setInterpolator(new LinearInterpolator());
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        public void onAnimationUpdate(ValueAnimator animation) {
            Integer value = (Integer) animation.getAnimatedValue();
            view.getLayoutParams().height = value.intValue();
            view.requestLayout();
        }
    });
    valueAnimator.start();

}

นี่คือรหัสสุดท้าย

public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    private TextView mFriendName;
    private int mOriginalHeight = 0;
    private boolean mIsViewExpanded = false;


    public ViewHolder(RelativeLayout v) {
        super(v);
        mFriendName = (TextView) v.findViewById(R.id.friendName);
        v.setOnClickListener(this);
    }

    @Override
    public void onClick(final View view) {
        if (mOriginalHeight == 0) {
            mOriginalHeight = view.getHeight();
        }
        ValueAnimator valueAnimator;
        if (!mIsViewExpanded) {
            mIsViewExpanded = true;
            valueAnimator = ValueAnimator.ofInt(mOriginalHeight, mOriginalHeight + (int) (mOriginalHeight * 1.5));
        } else {
            mIsViewExpanded = false;
            valueAnimator = ValueAnimator.ofInt(mOriginalHeight + (int) (mOriginalHeight * 1.5), mOriginalHeight);
        }
        valueAnimator.setDuration(300);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            public void onAnimationUpdate(ValueAnimator animation) {
                Integer value = (Integer) animation.getAnimatedValue();
                view.getLayoutParams().height = value.intValue();
                view.requestLayout();
            }
        });
        valueAnimator.start();

    }
}

11
สิ่งนี้ใช้ไม่ได้ "like ExpandableListView" เนื่องจากเนื้อหาที่ขยายในกรณีนั้นเป็นรายการที่มีรายการที่มาจากอะแดปเตอร์ นี่เป็นวิธีแก้ปัญหาที่เสื่อมสภาพโดยอนุญาตให้เป็นเด็กในกลุ่มได้เพียง 1 รายการเท่านั้น
TWiStErRob

และไม่ทำงานอย่างถูกต้องกับมุมมองที่นำกลับมาใช้ใหม่เลย
takecare

มันทำงานไม่ถูกต้องเนื่องจากมุมมองใน RecyclerList อาจมีการทำซ้ำหลายครั้งดังนั้นเมื่อคุณขยายหนึ่งรายการคุณจะเห็นหลายรายการในรายการถูกขยาย
Hossein Shahdoost

3

https://github.com/gabrielemariotti/cardslib

ไลบรารีนี้มีการใช้งานรายการที่ขยายได้พร้อมด้วยมุมมองรีไซเคิล (อ้างอิงจากแอปสาธิตใน "CardViewNative" -> "รายการตารางและ RecyclerView" -> "การ์ดที่ขยายได้") นอกจากนี้ยังมีการ์ด / รายการที่น่าสนใจอื่น ๆ อีกมากมาย


2
รายการการ์ดที่ขยายได้นี้ไม่ใช่ลูกของ RecyclerView (ไม่ได้ขยาย RecyclerView แต่เป็นการขยาย ExpandableListVIew เท่านั้น)
whizzzkey

0

มีคนบ่นว่าโซลูชันที่กล่าวถึงข้างต้นใช้กับ listview เป็นเนื้อหาที่ขยายไม่ได้ แต่มีวิธีง่ายๆ: สร้าง ListView และกรอก ListView นี้ด้วยตนเองกับแถวของคุณ

วิธีแก้ปัญหาสำหรับคนขี้เกียจ:มีวิธีง่ายๆหากคุณไม่ต้องการเปลี่ยนรหัสมากนัก เพียงใช้อะแดปเตอร์ของคุณด้วยตนเองเพื่อสร้างมุมมองและเพิ่มลงในไฟล์LinearLayout.

นี่คือตัวอย่าง:

if (mIsExpanded)
{
    // llExpandable... is the expandable nested LinearLayout
    llExpandable.removeAllViews();
    final ArrayAdapter<?> adapter = ... // create your adapter as if you would use it for a ListView
    for (int i = 0; i < adapter.getCount(); i++)
    {
        View item = adapter.getView(i, null, null);
        // if you want the item to be selectable as if it would be in a default ListView, then you can add following code as well:
        item.setBackgroundResource(Functions.getThemeReference(context, android.R.attr.selectableItemBackground));
        item.setTag(i);
        item.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // item would be retrieved with: 
                // adapter.getItem((Integer)v.getTag())
            }
        });
        llExpandable.addView(item);
    }
    ExpandUtils.expand(llExpandable, null, 500);
}
else
{
    ExpandUtils.collapse(llExpandable, null, 500);
}

ฟังก์ชันตัวช่วย: getThemeReference

public static int getThemeReference(Context context, int attribute)
{
    TypedValue typeValue = new TypedValue();
    context.getTheme().resolveAttribute(attribute, typeValue, false);
    if (typeValue.type == TypedValue.TYPE_REFERENCE)
    {
        int ref = typeValue.data;
        return ref;
    }
    else
    {
        return -1;
    }
}

คลาสตัวช่วย: ExpandUtils

Kavin Varnan postet แล้ววิธีทำให้เค้าโครงเคลื่อนไหว ... แต่ถ้าคุณต้องการใช้ชั้นเรียนของฉันอย่าลังเลที่จะทำเช่นนั้นฉันโพสต์ส่วนสำคัญ: https://gist.github.com/MichaelFlisar/738dfa03a1579cc7338a


9
"Lazy Solution" เป็นความคิดที่แย่มาก การเพิ่มมุมมองไปยัง linearlayout ภายในมุมมองแบบเลื่อนได้จึงไม่มีประสิทธิภาพ
Matthew

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

นี่มันเรียบร้อย! ขอบคุณตัน
Pawan Kumar

1
ดังที่ @Matthew กล่าวไว้นี่ไม่ใช่ความคิดที่ดีจริงๆ การมีมุมมองที่เลื่อนได้ขนาดใหญ่เพียงมุมเดียวพร้อม LinearLayouts ไม่ได้ปรับขนาดได้ดี เหตุผลที่ใหญ่ที่สุดประการหนึ่งที่เขียน RecyclerView / ListView และมุมมองอื่น ๆ ที่คล้ายคลึงกันคือการเพิ่มประสิทธิภาพให้มีรายการสำรองข้อมูลขนาดใหญ่ที่ไม่ทราบขนาด การสร้างมุมมองเดียวที่มีการเพิ่มมุมมองจำนวนมากจะช่วยเพิ่มประสิทธิภาพทั้งหมดที่มีให้ การรีไซเคิลมุมมองเป็นประโยชน์อย่างมากและทำให้หน่วยความจำของแอปมีประสิทธิภาพ เว้นแต่จำนวนรายการจะมีขนาดเล็กมีงานจำนวนมากที่บันทึกไว้โดยใช้รายการเพื่อจัดการการเชื่อมต่อมุมมอง
munkay

คุณพูดถูกแน่นอนว่านี่ไม่สมบูรณ์แบบและไม่ได้เพิ่มประสิทธิภาพอะไรเลย ... สำหรับการใช้งานของฉันฉันมักจะมีเพียงไม่กี่รายการ ... ดังนั้นนี่จึงไม่มีปัญหา ... Btw ในระหว่างนี้ฉันก็พบวิธี สำหรับมุมมองรีไซเคิลที่ซ้อนกัน ... เพียงใช้มุมมองรีไซเคิลแนวนอนที่มีความสูงคงที่ (ไม่เหมาะสำหรับทุกกรณี แต่ยังคง) เป็นแบบซ้อนrecyclerviewและคุณสามารถขยาย / ซ่อนอันที่ซ้อนกันนี้และใช้การเพิ่มประสิทธิภาพทั้งหมดของrecyclerview
prom85


0

นี่คือโค้ดตัวอย่างสำหรับสิ่งที่@TonicArtosกล่าวถึงเพื่อเพิ่มและลบรายการและเพื่อทำให้เคลื่อนไหวในขณะที่ทำสิ่งนี้นำมาจากตัวอย่างRecyclerView AnimationsและGitHub

1)เพิ่ม Listener ภายในonCreateViewHolder ()ของคุณเพื่อลงทะเบียน onClick

2)สร้างOnClickListenerที่คุณกำหนดเองภายในอะแดปเตอร์ของคุณ

private View.OnClickListener mItemListener = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        TextView tv = (TextView) v.findViewById(R.id.tvItems);
        String selected = tv.getText().toString();
        boolean checked = itemsList.get(recyclerView.getChildAdapterPosition(v)).isChecked();

        switch (selected){
            case "Item1":
                if(checked){
                    deleteItem(v);
                    itemsList.get(recyclerView.getChildAdapterPosition(v)).setChecked(false);
                }else {
                    addItem(v);
                    itemsList.get(recyclerView.getChildAdapterPosition(v)).setChecked(true);
                }
                break;
            case "Item2":
                if(checked){
                    deleteItem(v);
                    itemsList.get(recyclerView.getChildAdapterPosition(v)).setChecked(false);
                }else {
                    addItem(v);
                    itemsList.get(recyclerView.getChildAdapterPosition(v)).setChecked(true);
                }
                break;                 
            default:
                //In my case I have checkList in subItems,
                //checkItem(v);
                break;
        }
    }
};

3)เพิ่ม addItem ของคุณ () และ deleteItem ()

private void addItem(View view){
    int position = recyclerView.getChildLayoutPosition(view);
    if (position != RecyclerView.NO_POSITION){
        navDrawItems.add(position+1,new mObject());
        navDrawItems.add(position+2,new mObject());
        notifyItemRangeInserted(position+1,2);
    }
}


private void deleteItem(View view) {
    int position = recyclerView.getChildLayoutPosition(view);
    if (position != RecyclerView.NO_POSITION) {
        navDrawItems.remove(position+2);
        navDrawItems.remove(position+1);
        notifyItemRangeRemoved(position+1,2);
    }
}

4) หาก RecyclerViewAdapter ของคุณไม่ได้อยู่ในกิจกรรมเช่นเดียวกับ Recycler ดูผ่านอินสแตนซ์ของrecyclerViewกับอะแดปเตอร์ขณะที่การสร้าง

5) itemList เป็น ArrayList ประเภทmObjectซึ่งช่วยรักษาสถานะของรายการ (เปิด / ปิด) ชื่อประเภทของรายการ (subItems / mainItem) และตั้งค่า Theme ตามค่า

public class mObject{
    private String label;
    private int type;
    private boolean checked;
} 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.