เลื่อน RecyclerView เพื่อแสดงรายการที่เลือกไว้ด้านบน


216

ฉันกำลังมองหาวิธีการเลื่อนRecyclerViewเพื่อแสดงรายการที่เลือกไว้ด้านบน

ในแบบที่ListViewฉันสามารถทำได้โดยใช้scrollTo(x,y)และรับส่วนบนสุดขององค์ประกอบที่ต้องมีศูนย์กลาง

สิ่งที่ต้องการ:

@Override
public void onItemClick(View v, int pos){
    mylistView.scrollTo(0, v.getTop());
}

ปัญหาคือว่าRecyclerViewส่งกลับข้อผิดพลาดเมื่อใช้มันเป็นscrollToวิธีการพูด

RecyclerView ไม่รองรับการเลื่อนไปยังตำแหน่งที่แน่นอน

ฉันจะเลื่อน a RecyclerViewเพื่อวางรายการที่เลือกไว้ที่ด้านบนของมุมมองได้อย่างไร

คำตอบ:


406

หากคุณกำลังใช้งานLinearLayoutManagerหรือ Staggered GridLayoutManagerพวกเขาแต่ละคนมีเมธอดscrollToPositionWithOffsetที่รับทั้งตำแหน่งและออฟเซ็ตของการเริ่มต้นรายการตั้งแต่เริ่มต้นRecyclerViewซึ่งดูเหมือนว่าจะบรรลุสิ่งที่คุณต้องการ (การตั้งค่าออฟเซตเป็น 0 ควรจัดชิดด้านบน)

ตัวอย่างเช่น

//Scroll item 2 to 20 pixels from the top
linearLayoutManager.scrollToPositionWithOffset(2, 20);

34
หากใครต้องการสิ่งนี้สำหรับการกู้คืนตำแหน่งการเลื่อนของ RecyclerView นี่คือวิธีการบันทึกตำแหน่งการเลื่อน (สองอาร์กิวเมนต์สำหรับเมธอด scrollToPositionWithOffset): int index = linearLayoutManager.findFirstVisibleItemPosition (); ดู v = linearLayoutManager.getChildAt (0); int top = (v == null)? 0: (v.getTop () - linearLayoutManager.getPaddingTop ());
DominicM

สิ่งที่ฉันไม่ได้รับเกี่ยวกับสิ่งนี้คือเมื่อเรียก scrollToPosition ถ้า listener การเลือกอยู่บนมุมมองแต่ละมุมวัตถุ RecyclerView (ซึ่งมีการเข้าถึงตัวจัดการโครงร่างที่สามารถเรียกใช้ scrolToPosition) จะได้รับแจ้งว่ามีการเลือกรายการอย่างไร
Nonos

@Nonos คุณสามารถจัดเก็บ LayoutManager ที่คุณใช้ใน RecylerView.setLayoutManager () ก่อนแล้วจึงเข้าถึงได้โดยตรง เช่นนี้: LinearLayoutManager llm = new LinearLayoutManager (สิ่งนี้); mRecyclerView.setLayoutManager (LLM); ... (ในภายหลังในรหัส) ... llm.scrollToPositionWithOffset (2, 20);
Niraj

19
ถ้าฉันต้องการเลื่อนไปที่ "ราบรื่น" ด้านบน
Tiago

@ ติอาโก้ฉันไม่ได้เล่นกับมัน แต่บทความ 3 ส่วนนี้อาจช่วยได้: blog.stylingandroid.com/scrolling-recyclerview-part-1
Niraj

93

หากคุณกำลังมองหา LinearLayout Manager แนวตั้งคุณสามารถเลื่อนได้อย่างราบรื่นโดยใช้แบบกำหนดเองLinearSmoothScroller:

import android.content.Context;
import android.graphics.PointF;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSmoothScroller;
import android.support.v7.widget.RecyclerView;

public class SnappingLinearLayoutManager extends LinearLayoutManager {

    public SnappingLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                                       int position) {
        RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext());
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

    private class TopSnappedSmoothScroller extends LinearSmoothScroller {
        public TopSnappedSmoothScroller(Context context) {
            super(context);

        }

        @Override
        public PointF computeScrollVectorForPosition(int targetPosition) {
            return SnappingLinearLayoutManager.this
                    .computeScrollVectorForPosition(targetPosition);
        }

        @Override
        protected int getVerticalSnapPreference() {
            return SNAP_TO_START;
        }
    }
}

ใช้อินสแตนซ์ของ layoutmanager ในมุมมองรีไซเคิลแล้วการโทร recyclerView.smoothScrollToPosition(pos);จะเลื่อนอย่างราบรื่นไปยังตำแหน่งที่เลือกไว้ด้านบนของมุมมองรีไซเคิล


1
คำตอบที่มีประโยชน์จริงๆ! ฉันยังแทนที่getHorizontalSnapPreference()ใน TopSnappedSmoothScroller สำหรับรายการแนวนอน
Juan Saravia

6
ฉันถูกต้องในการสังเกตว่ามันใช้งานได้เฉพาะสำหรับรายการที่ยังมีเพียงพอหลังจาก / ต่ำกว่าพวกเขาเพื่อเติมเต็มส่วนที่เหลือRecyclerView? ซึ่งหมายความว่า: สิ่งนี้ดูเหมือนจะไม่สามารถใช้ได้กับไอเท็มสุดท้าย - โดยไม่คำนึงถึงSNAP_TO_STARTการRecyclerViewเลื่อนเพียงอย่างเดียวจนกระทั่งไอเท็มปรากฏที่ด้านล่าง แต่ไม่เลื่อนไปจนสุด ความคิดใด ๆ ของวิธีการบรรลุสิ่งนี้? (ฉันได้หลีกเลี่ยงปัญหานี้ด้วยการตั้งค่าช่องว่างภายในที่กำหนดเองRecyclerViewแต่ดูเหมือนจะไม่ถูกต้องจริง ๆ ... )
david.mihola


บางครั้งปริมาณของรายการที่ได้รับการมุ่งเน้นฉันมีโฟกัสที่สามารถวาดได้สำหรับการดูรายการ
YLS

1
@ david.mihola คุณพบวิธีที่ 'ถูกต้อง' หรือยัง? ฉันได้กำหนดช่องว่างภายในที่กำหนดเองให้กับ recyclerview ของฉันแล้ว แต่ฉันมีปัญหาแปลก ๆ ...
bernardo.g


28

recyclerview.scrollToPosition(position)คุณเพียงแค่ต้องโทร ไม่เป็นไร!

ถ้าคุณต้องการเรียกมันในอะแดปเตอร์เพียงแค่ปล่อยให้อะแดปเตอร์ของคุณมีอินสแตนซ์ของ recyclerview หรือกิจกรรมหรือส่วนที่มี recyclerview กว่าใช้วิธีgetRecyclerview()ในพวกเขา

ฉันหวังว่ามันจะช่วยคุณได้


2
สิ่งนี้ใช้ได้สำหรับฉัน - recyclerView.scrollToPosition (0); วางฉันที่ด้านบน
Ray Hunter

2
ที่ไม่ทำงานคุณต้องใช้ linearLayoutManager.scrollToPositionWithOffset (pos, 0);
Anil Ugale

@Ailil Ugale นั่นเป็นเรื่องไร้สาระ แน่นอนมันใช้งานได้ คุณสามารถใช้ LayoutManager ใด ๆ สำหรับ RecyclerView เหตุใดคุณจึงควรสนใจในภายหลังเมื่อคุณต้องการเลื่อน ฉันคิดว่าคุณกำลังทำอะไรผิด Recyclerview.scrollToPosition (ตำแหน่ง) เป็นวิธีการอำนวยความสะดวกที่เรียก LayoutManager.scrollToPosition (ตำแหน่ง)
เหลือเชื่อ Jan

1
@TheincredibleJan มันไม่สามารถทำงานได้ในทุกกรณี layoutManager โดยทั่วไปจะรับผิดชอบในการเลื่อนเพื่อข้อมูลของคุณ
โมฮัมเหม็ด

11

เดียวกันกับตัวควบคุมความเร็ว

public class SmoothScrollLinearLayoutManager extends LinearLayoutManager {
private static final float MILLISECONDS_PER_INCH = 110f;
private Context mContext;

public SmoothScrollLinearLayoutManager(Context context,int orientation, boolean reverseLayout) {
    super(context,orientation,reverseLayout);
    mContext = context;
}

@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
                                   int position) {
    RecyclerView.SmoothScroller smoothScroller = new TopSnappedSmoothScroller(recyclerView.getContext()){
        //This controls the direction in which smoothScroll looks for your view
        @Override
        public PointF computeScrollVectorForPosition(int targetPosition) {
            return new PointF(0, 1);
        }

        //This returns the milliseconds it takes to scroll one pixel.
        @Override
        protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
            return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
        }
    };
    smoothScroller.setTargetPosition(position);
    startSmoothScroll(smoothScroller);
}


private class TopSnappedSmoothScroller extends LinearSmoothScroller {
    public TopSnappedSmoothScroller(Context context) {
        super(context);

    }

    @Override
    public PointF computeScrollVectorForPosition(int targetPosition) {
        return SmoothScrollLinearLayoutManager.this
                .computeScrollVectorForPosition(targetPosition);
    }

    @Override
    protected int getVerticalSnapPreference() {
        return SNAP_TO_START;
    }
}
}

8

ลองสิ่งที่ได้ผลสำหรับฉันเจ๋ง!

สร้างตัวแปร private static int displayedposition = 0;

ตอนนี้สำหรับตำแหน่ง RecyclerView ของคุณในกิจกรรมของคุณ

myRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);

                LinearLayoutManager llm = (LinearLayoutManager) myRecyclerView.getLayoutManager();


                displayedposition = llm.findFirstVisibleItemPosition();


            }
        });

วางคำสั่งนี้ในตำแหน่งที่คุณต้องการวางเว็บไซต์เก่าที่แสดงในมุมมองของคุณ

LinearLayoutManager llm = (LinearLayoutManager) mRecyclerView.getLayoutManager();
        llm.scrollToPositionWithOffset(displayedposition , youList.size());

นั่นมันทำงานได้ดีสำหรับฉัน \ o /


7

เพียงเรียกวิธีนี้เพียง:

((LinearLayoutManager)recyclerView.getLayoutManager()).scrollToPositionWithOffset(yourItemPosition,0);

แทน:

recyclerView.scrollToPosition(yourItemPosition);

"แทน"? Distwo ไม่ได้พูดถึง "scrollToPosition" หากเขาใช้มันจะไม่มีปัญหาใด ๆ scrollToPosition () ตามที่ตั้งใจไว้
เหลือเชื่อ Jan

1
แปลกพอการใช้ scrollToPositionWithOffset ใช้งานได้สำหรับฉันและ recyclerview.scrollToPosition ไม่ได้ แม้ว่าฉันจะขอ Amir เปลี่ยน RecyclerView เป็น recyclerview เพราะมันให้ความรู้สึกว่าวิธีการนั้นเป็นแบบสแตติกและไม่ใช่
Mohit

6

สิ่งที่ฉันทำเพื่อกู้คืนตำแหน่งการเลื่อนหลังจากรีเฟรชปุ่ม RecyclerView ที่คลิก:

if (linearLayoutManager != null) {

    index = linearLayoutManager.findFirstVisibleItemPosition();
    View v = linearLayoutManager.getChildAt(0);
    top = (v == null) ? 0 : (v.getTop() - linearLayoutManager.getPaddingTop());
    Log.d("TAG", "visible position " + " " + index);
}

else{
    index = 0;
}

linearLayoutManager = new LinearLayoutManager(getApplicationContext());
linearLayoutManager.scrollToPositionWithOffset(index, top);

รับ offset ของรายการแรกที่มองเห็นได้จากด้านบนก่อนที่จะสร้างวัตถุ linearLayoutManager และหลังจาก instantiating มัน scrollToPositionWithOffset ของวัตถุ LinearLayoutManager ถูกเรียก


6

ฉันไม่รู้ว่าทำไมฉันไม่พบคำตอบที่ดีที่สุด แต่มันง่ายจริงๆ

recyclerView.smoothScrollToPosition(position);

ไม่มีข้อผิดพลาด

สร้างภาพเคลื่อนไหว


คุณจะใส่สิ่งนี้ไว้ในกิจกรรมหรืออะแดปเตอร์
อาร์โนลด์บราวน์

3

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

layoutmanager.scrollToPosition(int position);

3
If you want to scroll automatic without show scroll motion then you need to write following code:

**=> mRecyclerView.getLayoutManager().scrollToPosition(position);**

If you want to display scroll motion then you need to add following code.
=>Step 1: You need to declare SmoothScroller.

RecyclerView.SmoothScroller smoothScroller = new
                LinearSmoothScroller(this.getApplicationContext()) {
                    @Override
                    protected int getVerticalSnapPreference() {
                        return LinearSmoothScroller.SNAP_TO_START;
                    }
                };

=>step 2: You need to add this code any event you want to perform scroll to specific position.
=>First you need to set target position to SmoothScroller.

**smoothScroller.setTargetPosition(position);**

=>Then you need to set SmoothScroller to LayoutManager.
                        **mRecyclerView.getLayoutManager().startSmoothScroll(smoothScroller);**

3

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

RecyclerView - องค์ประกอบ 56 ถูกเลือก เราสามารถจัดการกับกรณีดังกล่าวโดยใช้linearLayoutManager.scrollToPositionWithOffset(pos, 0)และตรรกะเพิ่มเติมลงAdapterของRecyclerView- โดยการเพิ่มอัตรากำไรขั้นต้นที่กำหนดเองด้านล่างรายการสุดท้าย (ถ้ารายการสุดท้ายที่ไม่สามารถมองเห็นแล้วมันหมายความว่ามีการเติมพื้นที่เพียงพอRecyclerView) ระยะขอบที่กำหนดเองอาจเป็นความแตกต่างระหว่างความสูงของมุมมองรูทและความสูงของรายการ ดังนั้น AdapterสำหรับคุณRecyclerViewจะมีลักษณะดังนี้:

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

    int bottomHeight = 0;
    int itemHeight = holder.itemView.getMeasuredHeight();
    // if it's the last item then add a bottom margin that is enough to bring it to the top
    if (position == mDataSet.length - 1) {
        bottomHeight = Math.max(0, mRootView.getMeasuredHeight() - itemHeight);
    }
    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)holder.itemView.getLayoutParams();
    params.setMargins(0, 0, params.rightMargin, bottomHeight);
    holder.itemView.setLayoutParams(params);

    ...
} 
...

2

ในกรณีของฉันฉันRecyclerViewมีช่องว่างภายในแบบนี้

<android.support.v7.widget.RecyclerView
     ...
     android:paddingTop="100dp"
     android:clipToPadding="false"
/>

จากนั้นเพื่อเลื่อนรายการไปด้านบนฉันต้อง

recyclerViewLinearLayoutManager.scrollToPositionWithOffset(position, -yourRecyclerView.getPaddingTop());

1

หากคุณLayoutManagerเป็นLinearLayoutManagerคุณใช้scrollToPositionWithOffset(position,0);มันและมันจะทำให้รายการของคุณรายการแรกที่มองเห็นในรายการ มิฉะนั้นคุณสามารถใช้smoothScrollToPositionบนRecyclerViewโดยตรง

ฉันลงเอยด้วยการใช้โค้ดด้านล่าง

 RecyclerView.LayoutManager layoutManager = mainList.getLayoutManager();
        if (layoutManager instanceof LinearLayoutManager) {
            // Scroll to item and make it the first visible item of the list.
            ((LinearLayoutManager) layoutManager).scrollToPositionWithOffset(position, 0);
        } else {
            mainList.smoothScrollToPosition(position);
        }

-2

ฉันใช้รหัสด้านล่างเพื่อเลื่อนรายการ (thisView) ไปด้านบนให้เรียบ
มันยังใช้งานได้สำหรับ GridLayoutManager ด้วยมุมมองของความสูงที่แตกต่างกัน:

View firstView = mRecyclerView.getChildAt(0);
int toY = firstView.getTop();
int firstPosition = mRecyclerView.getChildAdapterPosition(firstView);
View thisView = mRecyclerView.getChildAt(thisPosition - firstPosition);
int fromY = thisView.getTop();

mRecyclerView.smoothScrollBy(0, fromY - toY);

ดูเหมือนว่าจะทำงานได้ดีพอสำหรับการแก้ปัญหาอย่างรวดเร็ว


1
คุณค่าของthisPositionคืออะไร?
pRaNaY

เป็นตำแหน่งที่คุณต้องการเลื่อนไปอย่างราบรื่น
Jan Rabe

ทำไมไม่ใช้ smoothScrollToPosition (ตำแหน่ง int) โดยไม่มีการคำนวณใด ๆ ไม่สำคัญว่าคุณจะเริ่มจากที่ใด
เหลือเชื่อ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.