RecyclerView ขยาย / ยุบรายการ


145

ฉันต้องการขยาย / ยุบรายการ recyclerView ของฉันเพื่อแสดงข้อมูลเพิ่มเติม ฉันต้องการที่จะบรรลุผลเช่นเดียวกันของSlideExpandableListView

โดยทั่วไปในมุมมองของฉันโฟลเดอร์ฉันมีมุมมองที่มองไม่เห็นและฉันต้องการที่จะทำภาพเคลื่อนไหวขยาย / ยุบอย่างราบรื่นมากกว่าตั้งค่าการมองเห็นเป็น VISIBLE / GONE เท่านั้น ฉันต้องการเพียงแค่รายการที่จะขยายในเวลาและมันจะเจ๋งที่จะมีระดับความสูงเพื่อแสดงว่ารายการที่เลือก

มันเป็นผลเช่นเดียวกันของรายการประวัติการโทรล่าสุดของ Android ตัวเลือก "การโทรกลับ" และ "รายละเอียด" จะปรากฏให้เห็นเฉพาะเมื่อมีการเลือกรายการ

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


ฉันมีคำถามที่เกี่ยวข้อง ในแอพนาฬิกากิจกรรมของชุดสัญญาณเตือนมีคุณสมบัติที่คล้ายกัน มันทำแบบเดียวกันหรือไม่?
2731584

เฮ้คุณมีทางออกหรือไม่ ฉันกำลังมองหาโซลูชันเดียวกันสำหรับแอปของฉัน
Arpit Patel

คุณพบทางออกหรือไม่?
Armin Ghoreishi

1
ฉันได้อัปเดตวิธีที่แนะนำในการทำตาม Google I / O 2016 ดูคำตอบของฉันstackoverflow.com/questions/27203817//?hl=th
Heisenberg

ตรวจสอบคำตอบของฉันที่นี่โซลูชันที่เรียบง่ายและสวยงามstackoverflow.com/a/48092441/5962715
Kiran Benny Joseph

คำตอบ:


192

โปรดอย่าใช้ไลบรารีใด ๆ สำหรับเอฟเฟกต์นี้แทนที่จะใช้วิธีที่แนะนำในการทำตาม Google I / O ในวิธีการonBindViewHolderของ recyclerView ให้ทำดังนี้

final boolean isExpanded = position==mExpandedPosition;
holder.details.setVisibility(isExpanded?View.VISIBLE:View.GONE);
holder.itemView.setActivated(isExpanded);
holder.itemView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mExpandedPosition = isExpanded ? -1:position;
        TransitionManager.beginDelayedTransition(recyclerView);
        notifyDataSetChanged();
    }
});
  • ที่ไหนรายละเอียดคือมุมมองของฉันที่จะปรากฏบน touch (รายละเอียดการโทรในกรณีของคุณ. เริ่มต้น Visibility.GONE)
  • m ขยายedPositionเป็นตัวแปร int เริ่มต้นที่ -1

และสำหรับเอฟเฟกต์เจ๋ง ๆ ที่คุณต้องการให้ใช้สิ่งเหล่านี้เป็นคุณลักษณะ list_item ของคุณ:

android:background="@drawable/comment_background"
android:stateListAnimator="@animator/comment_selection"

โดยที่ comment_background คือ:

<selector
xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize="true"
android:enterFadeDuration="@android:integer/config_shortAnimTime"
android:exitFadeDuration="@android:integer/config_shortAnimTime">

<item android:state_activated="true" android:drawable="@color/selected_comment_background" />
<item android:drawable="@color/comment_background" />
</selector>

และ comment_selection คือ:

<selector xmlns:android="http://schemas.android.com/apk/res/android">

<item android:state_activated="true">
    <objectAnimator
        android:propertyName="translationZ"
        android:valueTo="@dimen/z_card"
        android:duration="2000"
        android:interpolator="@android:interpolator/fast_out_slow_in" />
</item>

<item>
    <objectAnimator
        android:propertyName="translationZ"
        android:valueTo="0dp"
        android:duration="2000"
        android:interpolator="@android:interpolator/fast_out_slow_in" />
</item>
</selector>

22
เมื่อใช้รหัสนี้ภาพเคลื่อนไหวแบบขยายจะดูแปลก ๆ ทุกครั้งที่รายการบนสุดหายไปและทุกอย่างเลื่อนตำแหน่งขึ้นไปหนึ่งตำแหน่ง คุณเคยเจออะไรที่คล้ายกันบ้างไหม?
chris-pollux

6
ฉันพยายามทำแอนิเมชั่น แต่ฉันใช้สิ่งนี้กับอะแดปเตอร์ แต่สิ่งที่จะเป็นทางออกTransitionManager.beginDelayedTransition(recyclerView);ถ้าฉันไม่สามารถซ่อนการอ้างอิงมุมมองของผู้รีไซเคิล
Martín Serrano

2
@Ranveer มันใช้งานได้ดี ฉันมี recyclerViews ที่คล้ายกันสองรายการ แต่ทำงานได้อย่างถูกต้องกับหนึ่งรายการ ไม่รู้ว่าทำไม @ MartínDanielคุณจำเป็นที่จะต้องผ่านการrecyclerViewเป็นพารามิเตอร์ในการสร้างสำหรับอะแดปเตอร์ที่
คริสพอลลักซ์-

26
นี่เป็นวิธีที่น่ากลัว ฉันไม่อยากจะเชื่อเลยว่าเป็นคำแนะนำที่ Google I / O แม้หลังจากการตรวจสอบโครงการ Plaid และการดำเนินการแก้ไขปัญหาที่จำเป็นทั้งหมดแล้วภาพเคลื่อนไหวยังคงทำให้เกิดข้อบกพร่องที่สำคัญบางอย่างใน RecyclerView เช่นรายการแรก / รายการสุดท้ายที่มองเห็นหายไปก่อนกำหนด นอกจากนี้การเลื่อนในขณะที่อนิเมชั่นทำให้ทุกอย่างยุ่ง โดยทั่วไปฉันไม่คิดว่าการแก้ไขปัญหาทั้งหมดที่เกิดจากวิธีการนี้อาจเรียกว่า "ใส่เพียงบรรทัดเดียวและทุกอย่างจะทำงาน" ตามที่โฆษณาไว้
Vitali Olshevski

6
เรามีวิธีที่สดใหม่และง่ายขึ้นในปี 2019 สำหรับปัญหานี้หรือไม่?
Ponomarenko Oleh

76

สำหรับสิ่งนี้ต้องการเพียงแค่เส้นเรียบง่ายไม่ซับซ้อน

ในวิธีการ onBindViewHolder ของคุณเพิ่มรหัสด้านล่าง

final boolean isExpanded = position==mExpandedPosition;
holder.details.setVisibility(isExpanded?View.VISIBLE:View.GONE);
holder.itemView.setActivated(isExpanded);
holder.itemView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mExpandedPosition = isExpanded ? -1:position;
        notifyItemChanged(position);
    }
});

m ขยายedPositionเป็นตัวแปรโกลบอลเริ่มต้นที่ -1

สำหรับผู้ที่ต้องการเพียงหนึ่งรายการที่ขยายและอื่น ๆ ได้รับการยุบ ใช้สิ่งนี้

ก่อนประกาศตัวแปรระดับโลกที่มีก่อนหน้านี้ ExpandedPosition = -1

แล้วก็

    final boolean isExpanded = position==mExpandedPosition;
    holder.details.setVisibility(isExpanded?View.VISIBLE:View.GONE);
    holder.itemView.setActivated(isExpanded);

    if (isExpanded)
       previousExpandedPosition = position;

    holder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mExpandedPosition = isExpanded ? -1:position;
            notifyItemChanged(previousExpandedPosition);
            notifyItemChanged(position);
        }
    });

เสร็จสิ้น !!! เรียบง่ายและอ่อนน้อมถ่อมตน .. :)


ทางออกที่ดี นอกจากนี้ผมอยากแนะนำที่จะย้ายการตั้งค่าOnClickListenerเพื่อonCreateViewHolderแทนonBindViewHolder
YTerle

1
ทำงานได้ดีมากในเวลาที่มีทางออกที่ดีที่ช่วยให้RecyclerViewทำงานได้ฉันหมายถึงอะไรเป็นLayoutManagerอย่างอื่นถ้ามันไม่จัดการเลย์เอาต์! โซลูชันนี้ดีกว่าโซลูชันที่ได้รับคะแนนสูงสุดเนื่องจากมีรหัสน้อยลงให้การRecyclerViewจัดการสิ่งต่างๆและทำงานได้ดีขึ้น!
Mark Keen

1
ไม่เข้าใจวิธีเพิ่มมุมมอง "รายละเอียด" มันควรจะคงที่สำหรับผู้ถือแต่ละคน? จะทำอย่างไรหากฉันต้องการเพิ่มแต่ละมุมมอง
BekaBot

@BekaBot เป็นมุมมองที่คุณต้องการซ่อนในระหว่างการยุบรายการ
Kiran Benny Joseph

@ KiranBennyJoseph ใช่ฉันเข้าใจ ในกรณีนี้มุมมองควรเหมือนกันทุกรายการหรือไม่ ถ้าฉันต้องการมุมมองที่แตกต่างกันสำหรับรายการต่าง ๆ
BekaBot

72

ไม่ได้พูดว่านี่เป็นวิธีการที่ดีที่สุด แต่ดูเหมือนจะได้ผลสำหรับฉัน

รหัสเต็มสามารถดูได้ที่: ตัวอย่างรหัสที่: https://github.com/dbleicher/recyclerview-grid-quickreturn

ก่อนอื่นให้เพิ่มพื้นที่ที่ขยายลงในโครงร่างเซลล์ / ไอเท็มของคุณและสร้างโครงร่างเซลล์ล้อมรอบ animateLayoutChanges = "true" สิ่งนี้จะทำให้มั่นใจได้ว่าการขยาย / ยุบเป็นแบบเคลื่อนไหว:

<LinearLayout
    android:id="@+id/llCardBack"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"
    android:animateLayoutChanges="true"
    android:padding="4dp"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center|fill_horizontal"
        android:padding="10dp"
        android:gravity="center"
        android:background="@android:color/holo_green_dark"
        android:text="This is a long title to show wrapping of text in the view."
        android:textColor="@android:color/white"
        android:textSize="16sp" />

    <TextView
        android:id="@+id/tvSubTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center|fill_horizontal"
        android:background="@android:color/holo_purple"
        android:padding="6dp"
        android:text="My subtitle..."
        android:textColor="@android:color/white"
        android:textSize="12sp" />

    <LinearLayout
        android:id="@+id/llExpandArea"
        android:visibility="gone"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="6dp"
            android:text="Item One" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="6dp"
            android:text="Item Two" />

    </LinearLayout>
</LinearLayout>

จากนั้นให้คลาสตัวปรับต่อ RV ของคุณใช้ ViewOnClickListener เพื่อให้คุณสามารถดำเนินการกับรายการที่ถูกคลิก เพิ่มฟิลด์ int เพื่อคงตำแหน่งของมุมมองที่ขยายและเริ่มต้นเป็นค่าลบ:

private int expandedPosition = -1;

สุดท้ายใช้วิธีการของคุณ ViewHolder, onBindViewHolder () และแทนที่วิธีการ onClick () คุณจะขยายมุมมองใน onBindViewHolder หากตำแหน่งนั้นเท่ากับ "expandPosition" และซ่อนถ้าไม่ได้ คุณตั้งค่าของ expandPosition ในตัวฟัง onClick:

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

    int colorIndex = randy.nextInt(bgColors.length);
    holder.tvTitle.setText(mDataset.get(position));
    holder.tvTitle.setBackgroundColor(bgColors[colorIndex]);
    holder.tvSubTitle.setBackgroundColor(sbgColors[colorIndex]);

    if (position == expandedPosition) {
        holder.llExpandArea.setVisibility(View.VISIBLE);
    } else {
        holder.llExpandArea.setVisibility(View.GONE);
    }
}

@Override
public void onClick(View view) {
    ViewHolder holder = (ViewHolder) view.getTag();
    String theString = mDataset.get(holder.getPosition());

    // Check for an expanded view, collapse if you find one
    if (expandedPosition >= 0) {
        int prev = expandedPosition;
        notifyItemChanged(prev);
    }
    // Set the current position to "expanded"
    expandedPosition = holder.getPosition();
    notifyItemChanged(expandedPosition);

    Toast.makeText(mContext, "Clicked: "+theString, Toast.LENGTH_SHORT).show();
}

/**
 * Create a ViewHolder to represent your cell layout
 * and data element structure
 */
public static class ViewHolder extends RecyclerView.ViewHolder {
    TextView tvTitle;
    TextView tvSubTitle;
    LinearLayout llExpandArea;

    public ViewHolder(View itemView) {
        super(itemView);

        tvTitle = (TextView) itemView.findViewById(R.id.tvTitle);
        tvSubTitle = (TextView) itemView.findViewById(R.id.tvSubTitle);
        llExpandArea = (LinearLayout) itemView.findViewById(R.id.llExpandArea);
    }
}

สิ่งนี้ควรขยายครั้งละหนึ่งไอเท็มเท่านั้นโดยใช้แอนิเมชันเริ่มต้นระบบสำหรับการเปลี่ยนโครงร่าง อย่างน้อยมันก็ใช้งานได้สำหรับฉัน หวังว่ามันจะช่วย


ฉันรู้วิธีเลือกรายการเดียวเท่านั้น ฉันไม่รู้วิธีการทำแอนิเมชั่นในรายการนั้นจริงๆ ภาพเคลื่อนไหวขยาย / ยุบพร้อมภาพเคลื่อนไหวระบบเริ่มต้นนั้นดูไม่ราบรื่นนัก
stanete

1
ฉันยังไม่ได้ลองทำสิ่งต่อไปนี้ แต่ดูเหมือนว่าจะใช้ได้ ดูคำตอบได้ที่: stackoverflow.com/a/26443762/2259418
MojoTosh

2
@ user2731584 - ฉันไม่คิดอย่างนั้น ดูที่สิ่งที่ฉันเชื่อว่าเป็นแหล่งที่มาของพวกเขาปรากฏว่านาฬิกา Lollipop ใช้ ListView เพื่อแสดงรายการสัญญาณเตือน เลย์เอาต์: android.googlesource.com/platform/packages/apps/DeskClock/+/ ......ดูเหมือนว่าจะมีรหัสที่กำหนดเองเพื่อทำให้รายการขยาย / ยุบในรายการนี้ ดูแหล่งที่มาต่อไปนี้: android.googlesource.com/platform/packages/apps/DeskClock/+/…
MojoTosh

1
@ MojoTosh คุณพูดถูก พวกเขาใช้ ListView และทำแอนิเมชั่นโดยใช้รหัส expandAlarmฟังก์ชั่นในAlarmClockFragment.javaไฟล์ดำเนินการส่วนขยาย
2731584

4
ซึ่งonClick()ผมควรแทนที่? ไม่มีonClickในอะแดปเตอร์
Vicky Leong

15

มีความง่ายในการใช้งานห้องสมุดด้วยการสนับสนุน gradle คือhttps://github.com/cachapa/ExpandableLayout

ได้จากเอกสารห้องสมุด:

<net.cachapa.expandablelayout.ExpandableLinearLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:el_duration="1000"
    app:el_expanded="true">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Click here to toggle expansion" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="Fixed height"
        app:layout_expandable="true" />

 </net.cachapa.expandablelayout.ExpandableLinearLayout>

หลังจากที่คุณทำเครื่องหมายของคุณขยายมุมมองเพียงแค่เรียกร้องใด ๆ ของวิธีการเหล่านี้บนบรรจุภัณฑ์: expand(), collapse()หรือtoggle()


นี่เป็นวิธีที่ดีที่สุดที่จะทำหรือไม่? ฉันพยายามติดตามสิ่งที่คุณเขียน ฉันมีมุมมองรีไซเคิลที่ฉันต้องการขยายเพื่อแสดงกราฟเมื่อคลิก
sourlemonaid

10

ฉันรู้ว่าเป็นเวลานานแล้วตั้งแต่คำถามเดิมถูกโพสต์ แต่ฉันคิดว่าสำหรับคนที่ช้าเช่นฉันคำอธิบายเล็กน้อยของคำตอบของ @ Heisenberg จะช่วยได้

ประกาศตัวแปรสองตัวในคลาสอะแด็ปเตอร์เป็น

private int mExpandedPosition= -1;
private RecyclerView recyclerView = null;

จากนั้นในonBindViewHolderต่อไปนี้ตามที่กำหนดในคำตอบเดิม

      // This line checks if the item displayed on screen 
      // was expanded or not (Remembering the fact that Recycler View )
      // reuses views so onBindViewHolder will be called for all
      // items visible on screen.
    final boolean isExpanded = position==mExpandedPosition;

        //This line hides or shows the layout in question
        holder.details.setVisibility(isExpanded?View.VISIBLE:View.GONE);

        // I do not know what the heck this is :)
        holder.itemView.setActivated(isExpanded);

        // Click event for each item (itemView is an in-built variable of holder class)
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

 // if the clicked item is already expaned then return -1 
//else return the position (this works with notifyDatasetchanged )
                mExpandedPosition = isExpanded ? -1:position;
    // fancy animations can skip if like
                TransitionManager.beginDelayedTransition(recyclerView);
    //This will call the onBindViewHolder for all the itemViews on Screen
                notifyDataSetChanged();
            }
        });

และสุดท้ายจะได้รับวัตถุ recyclerView ในการแทนที่อะแดปเตอร์

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

    this.recyclerView = recyclerView;
}

หวังว่าสิ่งนี้จะช่วย


3
ทางออกที่ดีและเรียบง่าย การปรับปรุงเล็กน้อยใช้ RecyclerView.NO_POSITION แทนที่จะใช้เลขเวทย์มนตร์ -1
Casper Bang

8

ไม่จำเป็นต้องใช้ห้องสมุดบุคคลที่สาม ปรับแต่งเล็ก ๆ น้อย ๆในวิธีการที่แสดงให้เห็นในGoogle I / O 2016และไฮเซนเบิร์กในหัวข้อนี้ไม่หลอกลวง

ตั้งแต่notifyDataSetChanged() Redraws สมบูรณ์RecyclerView , notifyDataItemChanged()เป็นตัวเลือกที่ดีกว่า (ไม่ได้ดีที่สุด) เพราะเรามีตำแหน่งและViewHolderที่จำหน่ายของเราและnotifyDataItemChanged()เพียงวาดโดยเฉพาะอย่างยิ่งViewHolderในตำแหน่งที่กำหนด

แต่ปัญหาคือการหายไปก่อนกำหนดของการViewHolderคลิกและการเกิดขึ้นจะไม่ถูกกำจัดแม้ว่าnotifyDataItemChanged()จะถูกใช้

รหัสต่อไปนี้ไม่ได้หันไปใช้notifyDataSetChanged()หรือnotifyDataItemChanged()และถูกทดสอบบน API 23 และใช้งานได้อย่างมีเสน่ห์เมื่อใช้กับ RecyclerView โดยที่ ViewHolder แต่ละรายการมีCardViewรูทองค์ประกอบ:

holder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            final boolean visibility = holder.details.getVisibility()==View.VISIBLE;

            if (!visibility)
            {
                holder.itemView.setActivated(true);
                holder.details.setVisibility(View.VISIBLE);
                if (prev_expanded!=-1 && prev_expanded!=position)
                {
                    recycler.findViewHolderForLayoutPosition(prev_expanded).itemView.setActivated(false);
                    recycler.findViewHolderForLayoutPosition(prev_expanded).itemView.findViewById(R.id.cpl_details).setVisibility(View.GONE);
                }
                prev_expanded = position;
            }
            else
            {
                holder.itemView.setActivated(false);
                holder.details.setVisibility(View.GONE);
            }
            TransitionManager.beginDelayedTransition(recycler);              
        }
});

prev_positionเป็นจำนวนเต็มทั่วโลกเริ่มต้นที่ -1 detailsเป็นมุมมองที่สมบูรณ์ซึ่งจะแสดงเมื่อขยายและปิดบังเมื่อยุบ

ดังที่ได้กล่าวแล้วองค์ประกอบรากของViewHolderคือCardViewด้วยforegroundและstateListAnimatorแอตทริบิวต์ที่กำหนดไว้อย่างที่ Heisenberg กล่าวไว้ในหัวข้อนี้

UPDATE:การสาธิตด้านบนจะยุบรายการที่ขยายออกไปก่อนหน้าหากหนึ่งในรายการนั้นขยาย ในการปรับเปลี่ยนพฤติกรรมนี้และเก็บรายการที่ขยายไว้เหมือนเดิมแม้ว่าจะมีการขยายรายการอื่นคุณจะต้องใช้รหัสต่อไปนี้

if (row.details.getVisibility()!=View.VISIBLE)
    {
        row.details.setVisibility(View.VISIBLE);
        row.root.setActivated(true);
        row.details.animate().alpha(1).setStartDelay(500);
    }
    else
    {
        row.root.setActivated(false);
        row.details.setVisibility(View.GONE);
        row.details.setAlpha(0);
    }
    TransitionManager.beginDelayedTransition(recycler);

UPDATE:เมื่อขยายรายการสุดท้ายในรายการรายการนั้นอาจไม่สามารถมองเห็นได้อย่างสมบูรณ์เนื่องจากส่วนที่ขยายจะอยู่ด้านล่างหน้าจอ ในการรับไอเท็มเต็มหน้าจอให้ใช้รหัสต่อไปนี้

LinearLayoutManager manager = (LinearLayoutManager) recycler.getLayoutManager();
    int distance;
    View first = recycler.getChildAt(0);
    int height = first.getHeight();
    int current = recycler.getChildAdapterPosition(first);
    int p = Math.abs(position - current);
    if (p > 5) distance = (p - (p - 5)) * height;
    else       distance = p * height;
    manager.scrollToPositionWithOffset(position, distance);

สำคัญ:สำหรับการสาธิตการทำงานข้างต้นเราจะต้องเก็บโค้ดไว้ในอินสแตนซ์ของ RecyclerView & เป็น LayoutManager (ภายหลังเพื่อความยืดหยุ่น)


ขอบคุณวิธีนี้ใช้ได้สำหรับฉัน ฉันพยายามหลีกเลี่ยงการใช้notifyItemChangedเพราะฉันมีรูปImageViewที่โหลดจาก URL notifyItemChangedโหลดภาพของฉันอีกครั้งทำให้ดูแปลก ๆ
Tam Huynh

บรรทัดที่เล็บมันคือการTransitionManager.beginDelayedTransition(recycler);ค้นหาที่ดี The specified child already has a parent. You must call removeView() on the child's parent firstแต่ผมพบว่าโดยการใช้วิธีการนี้ถ้าผมสแปมคลิกหลายแถวตามลำดับในที่สุดมันก็ก่อให้เกิดความผิดพลาดภายใน ฉันคิดว่านี่เป็นเพราะมุมมองการรีไซเคิลบนขอบเขตมุมมองของนักรีไซเคิล แต่ฉันไม่สามารถบอกได้อย่างแน่นอน ใครบ้างมีปัญหาเดียวกัน
Roger Oba

นี่เป็นวิธีที่ดีที่สุดที่จะทำหรือไม่? ฉันพยายามติดตามสิ่งที่คุณเขียน ฉันมีมุมมองรีไซเคิลที่ฉันต้องการขยายเพื่อแสดงกราฟเมื่อคลิก ฉันมีเวลายากที่จะเข้าใจว่าจะต้องทำอะไร
sourlemonaid

ใครบ้างมีตัวอย่างที่สมบูรณ์ของการใช้งานนี้ ฉันหมายถึงตัวอย่างโค้ดที่ใช้งานได้ กรุณาช่วย. ฉันไม่เข้าใจวิธีการทำ
Munazza

7

หลังจากใช้วิธีที่แนะนำในการใช้รายการที่ขยายได้ / ยุบได้ซึ่งอยู่ใน RecyclerViewบนRecyclerView ที่ตอบโดยHeisenBergฉันได้เห็นสิ่งประดิษฐ์ที่สังเกตเห็นได้ทุกครั้งที่RecyclerViewรีเฟรชด้วยการเรียกใช้TransitionManager.beginDelayedTransition(ViewGroup)และต่อมาnotifyDatasetChanged()และต่อมา

คำตอบเดิมของเขา:

final boolean isExpanded = position==mExpandedPosition;
holder.details.setVisibility(isExpanded?View.VISIBLE:View.GONE);
holder.itemView.setActivated(isExpanded);
holder.itemView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mExpandedPosition = isExpanded ? -1 : position;
        TransitionManager.beginDelayedTransition(recyclerView);
        notifyDataSetChanged();
    }
});

Modified:

final boolean isExpanded = position == mExpandedPosition;
holder.details.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
holder.view.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (mExpandedHolder != null) {
            mExpandedHolder.details.setVisibility(View.GONE);
            notifyItemChanged(mExpandedPosition);
        }
        mExpandedPosition = isExpanded ? -1 : holder.getAdapterPosition();
        mExpandedHolder = isExpanded ? null : holder;
        notifyItemChanged(holder.getAdapterPosition());
    }
}
  • รายละเอียดคือมุมมองที่คุณต้องการแสดง / ซ่อนระหว่างการขยาย / ยุบรายการ
  • m ขยายedPositionเป็นเครื่องมือintที่ติดตามรายการที่ขยาย
  • mExpandedHolderถูกViewHolderใช้ระหว่างการยุบรายการ

ขอให้สังเกตว่าวิธีการ TransitionManager.beginDelayedTransition(ViewGroup)และnotifyDataSetChanged()จะถูกแทนที่ด้วยnotifyItemChanged(int)เพื่อกำหนดเป้าหมายรายการที่เฉพาะเจาะจงและปรับแต่งเล็กน้อย

หลังจากการแก้ไขผลกระทบที่ไม่พึงประสงค์ก่อนหน้านี้ควรจะหายไป อย่างไรก็ตามนี่อาจไม่ใช่โซลูชันที่สมบูรณ์แบบ มันทำในสิ่งที่ฉันต้องการเท่านั้น

:: แก้ไข ::

เพื่อความกระจ่างทั้งmExpandedPositionและmExpandedHolderกลม


โปรดอธิบายว่าจะประกาศและใช้ m ขยายedHolderได้อย่างไรและที่ไหน
เวอร์เนอร์

1
@Werer ฉันได้แก้ไขคำตอบเพื่อแสดงตำแหน่งที่ประกาศไว้ การใช้งานอยู่ในข้อมูลโค้ดเองเช่นเดียวกับวัตถุประสงค์ มันถูกใช้เพื่อสร้างการย่อขยายและขยายรายการ
Chan Teck Wei

ฉันชอบวิธีการนี้มากที่สุดเท่าที่ภาพเคลื่อนไหวเกี่ยวข้อง แต่ฉันมี "จาง" ตัวเล็ก ๆ นี้เมื่อคลิกที่รายการ คุณมีสิ่งที่ดีหรือแตกต่างกันเป็นเพราะการใช้ alertItemChanged () หรือไม่? drive.google.com/file/d/1GaU1fcjHEfSErYF50otQGLGsIWkSyQSe//
kazume

ใช่. notifyItemChanged()เช่นคุณจะเดาได้ว่ามันมีสาเหตุมาจาก คุณสามารถเคลื่อนไหวและเปลี่ยนความสูงของรายการด้วยตนเองและเพิ่มมุมมองได้
Chan Teck Wei

@ChanTeckWei: โซลูชันของคุณทำงานได้ดี แต่ภาพแถวกะพริบเมื่อใดก็ตามที่ฉันยุบหรือขยายแถวรายการ recyclerview .. มีวิธีแก้ไขปัญหานี้หรือไม่
Pragya Mendiratta

3

ทำสิ่งต่อไปนี้หลังจากที่คุณตั้งค่าผู้ฟัง onClick เป็นคลาส ViewHolder:

@Override
    public void onClick(View v) {
        final int originalHeight = yourLinearLayout.getHeight();
        animationDown(YourLinearLayout, originalHeight);//here put the name of you layout that have the options to expand.
    }

    //Animation for devices with kitkat and below
    public void animationDown(LinearLayout billChoices, int originalHeight){

        // Declare a ValueAnimator object
        ValueAnimator valueAnimator;
        if (!billChoices.isShown()) {
            billChoices.setVisibility(View.VISIBLE);
            billChoices.setEnabled(true);
            valueAnimator = ValueAnimator.ofInt(0, originalHeight+originalHeight); // These values in this method can be changed to expand however much you like
        } else {
            valueAnimator = ValueAnimator.ofInt(originalHeight+originalHeight, 0);

            Animation a = new AlphaAnimation(1.00f, 0.00f); // Fade out

            a.setDuration(200);
            // Set a listener to the animation and configure onAnimationEnd
            a.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {

                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    billChoices.setVisibility(View.INVISIBLE);
                    billChoices.setEnabled(false);
                }

                @Override
                public void onAnimationRepeat(Animation animation) {

                }
            });
            // Set the animation on the custom view
            billChoices.startAnimation(a);
        }
        valueAnimator.setDuration(200);
        valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            public void onAnimationUpdate(ValueAnimator animation) {
                Integer value = (Integer) animation.getAnimatedValue();
                billChoices.getLayoutParams().height = value.intValue();
                billChoices.requestLayout();
            }
        });


        valueAnimator.start();
    }
}

ฉันคิดว่าน่าจะช่วยได้นั่นเป็นวิธีที่ฉันใช้และทำเช่นเดียวกันกับ google ในมุมมองการโทรล่าสุด


2

ฉันประหลาดใจที่ยังไม่มีคำตอบที่รัดกุม แต่การเคลื่อนไหวแบบขยาย / ยุบนั้นทำได้ง่ายมากด้วยรหัสเพียง 2 บรรทัด:

(recycler.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false // called once

พร้อมกับ

notifyItemChanged(position) // in adapter, whenever a child view in item's recycler gets hidden/shown

ดังนั้นสำหรับฉันคำอธิบายในลิงค์ด้านล่างมีประโยชน์จริง ๆ : https://medium.com/@nikola.jakshic/how-to-expand-collapse-items-in-recyclerview-49a648a403a6


1
//Global Variable

private int selectedPosition = -1;

 @Override
    public void onBindViewHolder(final CustomViewHolder customViewHolder, final int i) {

        final int position = i;
        final GetProductCatalouge.details feedItem = this.postBeanses.get(i);
        customViewHolder.lly_main.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                selectedPosition = i;
                notifyDataSetChanged();
            }
        });
        if (selectedPosition == i) {

          if (customViewHolder.lly_hsn_code.getVisibility() == View.VISIBLE) {

            customViewHolder.lly_hsn_code.setVisibility(View.GONE);
            customViewHolder.lly_sole.setVisibility(View.GONE);
            customViewHolder.lly_sole_material.setVisibility(View.GONE);

        } else {

            customViewHolder.lly_hsn_code.setVisibility(View.VISIBLE);
            customViewHolder.lly_sole.setVisibility(View.VISIBLE);
            customViewHolder.lly_sole_material.setVisibility(View.VISIBLE);
        }


        } else {
            customViewHolder.lly_hsn_code.setVisibility(View.GONE);
            customViewHolder.lly_sole.setVisibility(View.GONE);
            customViewHolder.lly_sole_material.setVisibility(View.GONE);
        }
}

ป้อนคำอธิบายรูปภาพที่นี่


0

RVAdapterใช้ทั้งสองประเภทในมุมมองของคุณ หนึ่งสำหรับเค้าโครงขยายและอื่น ๆ สำหรับยุบ และความมหัศจรรย์ก็เกิดขึ้นกับการตั้งค่าandroid:animateLayoutChanges="true"สำหรับRecyclerView Checkout เอฟเฟกต์ที่ทำได้เมื่อใช้ 0:42 ในวิดีโอนี้


จิตใจอธิบายเรื่องนี้ดีขึ้นไหม? ฉันดูเหมือนจะไม่สามารถทำงานได้
sourlemonaid

@sourlemonaid โดยทั่วไปคุณสร้างมุมมองหลายประเภทสำหรับรายการตามที่อธิบายไว้ในstackoverflow.com/a/26245463/3731795 ... คุณติดตามดัชนีซึ่งมีการขยายรายการและส่วนที่เหลือจะถูกยุบในเวลานั้น . ตอนนี้ถ้า (current_item_index == expand_item_index) {return VIEW_TYPE_EXPANDED} อื่น {reutrn VIEW_TYPE_COLLAPSED}
penduDev
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.