RecyclerView - एक निश्चित स्थिति पर आइटम के शीर्ष पर स्क्रॉल कैसे करें?


125

RecyclerView पर, मैं अचानक चयनित आइटम के शीर्ष पर स्क्रॉल करके उपयोग करने में सक्षम हूं:

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

हालाँकि, यह अचानक आइटम को शीर्ष स्थान पर ले जाता है। मैं एक आइटम के शीर्ष पर आसानी से जाना चाहता हूं ।

मैंने भी कोशिश की है:

recyclerView.smoothScrollToPosition(position);

लेकिन यह अच्छी तरह से काम नहीं करता है क्योंकि यह आइटम को शीर्ष पर चयनित स्थिति में नहीं ले जाता है। यह केवल सूची को स्क्रॉल करता है जब तक कि स्थिति पर आइटम दिखाई नहीं देता।

जवाबों:


225

RecyclerViewको एक्स्टेंसिबल करने के लिए डिज़ाइन किया गया है, इसलिए केवल स्क्रॉलिंग करने के लिए LayoutManager(जैसा कि ड्रिडेव ने सुझाव दिया है ) उप-वर्ग की आवश्यकता नहीं है ।

इसके बजाय, केवल SmoothScrollerवरीयता के साथ बनाएं SNAP_TO_START:

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

अब आप उस स्थिति को सेट करते हैं जहाँ आप स्क्रॉल करना चाहते हैं:

smoothScroller.setTargetPosition(position);

और पास करें कि SmoothScroller से LayoutManager:

layoutManager.startSmoothScroll(smoothScroller);

9
इस छोटे से समाधान के लिए धन्यवाद। बस दो और चीजों पर मुझे अपने कार्यान्वयन में विचार करना था: जैसा कि मेरे पास एक क्षैतिज स्क्रॉलिंग दृश्य है जिसे मुझे सेट करना था protected int getHorizontalSnapPreference() { return LinearSmoothScroller.SNAP_TO_START; }। इसके अलावा मुझे अमूर्त पद्धति को लागू करना था public PointF computeScrollVectorForPosition(int targetPosition) { return layoutManager.computeScrollVectorForPosition(targetPosition); }
ऑस्ट्रियाई

2
RecyclerView को एक्स्टेंसिबल होने के लिए डिज़ाइन किया जा सकता है, लेकिन इस तरह की एक साधारण चीज बस गायब होने के लिए आलसी महसूस करती है। अच्छा जवाब! धन्यवाद।
माइकल

8
क्या इसे शुरू करने के लिए स्नैप करने का कोई तरीका है, लेकिन एक्स डीपी की भरपाई के साथ?
मार्क बुईकेमा

5
क्या इसकी गति को धीमा करना संभव है?
अलीरज़ा नूराली

3
यह भी सोच रहा था कि जिस गति से स्क्रॉल होता है उसे संशोधित किया जा सकता है (धीमा) @AlirezaNoorali
vikzilla

112

इसके लिए आपको एक Custom LayoutManager बनाना होगा

public class LinearLayoutManagerWithSmoothScroller extends LinearLayoutManager {

    public LinearLayoutManagerWithSmoothScroller(Context context) {
        super(context, VERTICAL, false);
    }

    public LinearLayoutManagerWithSmoothScroller(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 LinearLayoutManagerWithSmoothScroller.this
                    .computeScrollVectorForPosition(targetPosition);
        }

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

अपने RecyclerView के लिए इसका उपयोग करें और smoothScrollToPosition को कॉल करें।

उदाहरण :

 recyclerView.setLayoutManager(new LinearLayoutManagerWithSmoothScroller(context));
 recyclerView.smoothScrollToPosition(position);

यह निर्दिष्ट स्थिति के RecyclerView आइटम के ऊपर स्क्रॉल करेगा।

उम्मीद है की यह मदद करेगा।


मुझे सुझाए गए लेआउट प्रबंधक को लागू करने में परेशानी हुई। यहाँ इस जवाब ने मेरे लिए बहुत आसान काम किया: stackoverflow.com/questions/28025425/…
arberg

3
केवल दर्द के घंटों के बाद उपयोगी उत्तर, यह डिज़ाइन के अनुसार काम करता है!
व्लाबशको

1
@droidev मैंने आपके उदाहरण का उपयोग सहज स्क्रॉलिंग के साथ किया है और आम तौर पर SNAP_TO_START को मेरी आवश्यकता है लेकिन यह मेरे लिए कुछ मुद्दों के साथ काम करता है। कभी-कभी स्क्रॉल करना बंद हो जाता है 1,2,3 या 4 स्थिति जो पहले मैं पास करता हूं smoothScrollToPosition। यह समस्या क्यों हो सकती है? धन्यवाद।
नताशा

क्या आप किसी तरह हमारे पास अपने कोड तक पहुँच सकते हैं? मुझे पूरा यकीन है कि यह आपका कोड इश्यू है।
droidev

1
स्वीकार किए गए एक के बावजूद यह सही उत्तर है
एलेसियो

25

यह एक है विस्तार समारोह मैं में लिखा था Kotlin के साथ प्रयोग करने के लिए RecyclerView(@Paul Woitaschek उत्तर के आधार पर):

fun RecyclerView.smoothSnapToPosition(position: Int, snapMode: Int = LinearSmoothScroller.SNAP_TO_START) {
  val smoothScroller = object : LinearSmoothScroller(this.context) {
    override fun getVerticalSnapPreference(): Int = snapMode
    override fun getHorizontalSnapPreference(): Int = snapMode
  }
  smoothScroller.targetPosition = position
  layoutManager?.startSmoothScroll(smoothScroller)
}

इसे इस तरह उपयोग करें:

myRecyclerView.smoothSnapToPosition(itemPosition)

यह काम! चिकनी स्क्रॉल के साथ समस्या तब होती है जब आपके पास बड़ी सूचियाँ होती हैं और फिर नीचे या ऊपर तक स्क्रॉल करने में लंबा, लंबा समय लगता है
portfoliobuilder

@vahahost मैं वांछित स्थिति को कैसे केंद्र कर सकता हूं?
ysfcyln

@ysfcyln इस stackoverflow.com/a/39654328/1502079 उत्तर की जाँच करें
vovahost

12

हम इस तरह की कोशिश कर सकते हैं

    recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView,new RecyclerView.State(), recyclerView.getAdapter().getItemCount());

5

ऑफसेट Y / X स्थिति को लागू करने के लिए LinearSmoothScroller में CalculDyToMakeV अदृश्य / CalculDxToMakeVouble फ़ंक्शन को ओवरराइड करें

override fun calculateDyToMakeVisible(view: View, snapPreference: Int): Int {
    return super.calculateDyToMakeVisible(view, snapPreference) - ConvertUtils.dp2px(10f)
}

यह वही है जिसे मैं देख रहा था। X / y की ऑफसेट स्थिति को लागू करने के लिए इसे साझा करने के लिए धन्यवाद :)
शान Xeeshi

3

इस तरह से स्क्रॉल करने का सबसे आसान तरीका RecyclerViewनिम्न है:

// Define the Index we wish to scroll to.
final int lIndex = 0;
// Assign the RecyclerView's LayoutManager.
this.getRecyclerView().setLayoutManager(this.getLinearLayoutManager());
// Scroll the RecyclerView to the Index.
this.getLinearLayoutManager().smoothScrollToPosition(this.getRecyclerView(), new RecyclerView.State(), lIndex);

2
यदि यह स्थिति पहले से ही दिखाई दे रही है तो यह किसी स्थिति में स्क्रॉल नहीं करेगा। यह स्थिति को दृश्यमान बनाने के लिए कम से कम राशि को स्क्रॉल करता है। इस प्रकार हमें निर्दिष्ट ऑफसेट की आवश्यकता क्यों है।
TatiOverflow

3

मैं इस तरह का इस्तेमाल किया:

recyclerView.getLayoutManager().smoothScrollToPosition(recyclerView, new RecyclerView.State(), 5);

2

धन्यवाद, समाधान के लिए @droidev अगर किसी को कोटलिन समाधान की तलाश है, तो इसे देखें:

    class LinearLayoutManagerWithSmoothScroller: LinearLayoutManager {
    constructor(context: Context) : this(context, VERTICAL,false)
    constructor(context: Context, orientation: Int, reverseValue: Boolean) : super(context, orientation, reverseValue)

    override fun smoothScrollToPosition(recyclerView: RecyclerView?, state: RecyclerView.State?, position: Int) {
        super.smoothScrollToPosition(recyclerView, state, position)
        val smoothScroller = TopSnappedSmoothScroller(recyclerView?.context)
        smoothScroller.targetPosition = position
        startSmoothScroll(smoothScroller)
    }

    private class TopSnappedSmoothScroller(context: Context?) : LinearSmoothScroller(context){
        var mContext = context
        override fun computeScrollVectorForPosition(targetPosition: Int): PointF? {
            return LinearLayoutManagerWithSmoothScroller(mContext as Context)
                    .computeScrollVectorForPosition(targetPosition)
        }

        override fun getVerticalSnapPreference(): Int {
            return SNAP_TO_START
        }


    }

}

1
धन्यवाद @ पंकज, कोटलिन में एक समाधान की तलाश में था।
अक्षय कुमार

1
  1. "रैखिकलेआउट" क्लास बढ़ाएँ और आवश्यक कार्यों को ओवरराइड करें
  2. अपने खंड या गतिविधि में उपरोक्त वर्ग का एक उदाहरण बनाएँ
  3. कॉल "recyclerView.smoothScrollToPosition (लक्ष्य)

CustomLinearLayout.kt:

class CustomLayoutManager(private val context: Context, layoutDirection: Int):
  LinearLayoutManager(context, layoutDirection, false) {

    companion object {
      // This determines how smooth the scrolling will be
      private
      const val MILLISECONDS_PER_INCH = 300f
    }

    override fun smoothScrollToPosition(recyclerView: RecyclerView, state: RecyclerView.State, position: Int) {

      val smoothScroller: LinearSmoothScroller = object: LinearSmoothScroller(context) {

        fun dp2px(dpValue: Float): Int {
          val scale = context.resources.displayMetrics.density
          return (dpValue * scale + 0.5f).toInt()
        }

        // change this and the return super type to "calculateDyToMakeVisible" if the layout direction is set to VERTICAL
        override fun calculateDxToMakeVisible(view: View ? , snapPreference : Int): Int {
          return super.calculateDxToMakeVisible(view, SNAP_TO_END) - dp2px(50f)
        }

        //This controls the direction in which smoothScroll looks for your view
        override fun computeScrollVectorForPosition(targetPosition: Int): PointF ? {
          return this @CustomLayoutManager.computeScrollVectorForPosition(targetPosition)
        }

        //This returns the milliseconds it takes to scroll one pixel.
        override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float {
          return MILLISECONDS_PER_INCH / displayMetrics.densityDpi
        }
      }
      smoothScroller.targetPosition = position
      startSmoothScroll(smoothScroller)
    }
  }

नोट: उपरोक्त उदाहरण HORIZONTAL दिशा में सेट है, आप आरंभीकरण के दौरान VERTICAL / HORIZONTAL पास कर सकते हैं।

आप के लिए दिशा निर्धारित करते हैं तो VERTICAL आप "में बदल जाएगा calculateDxToMakeVisible " से " calculateDyToMakeVisible " (यह भी महाप्रकार कॉल वापसी मान मन)

गतिविधि / फ्रैगमेंट.किट :

...
smoothScrollerLayoutManager = CustomLayoutManager(context, LinearLayoutManager.HORIZONTAL)
recyclerView.layoutManager = smoothScrollerLayoutManager
.
.
.
fun onClick() {
  // targetPosition passed from the adapter to activity/fragment
  recyclerView.smoothScrollToPosition(targetPosition)
}

0

संभवत: @droidev दृष्टिकोण सही है, लेकिन मैं बस कुछ अलग प्रकाशित करना चाहता हूं, जो मूल रूप से एक ही काम करता है और लेआउटमैन के विस्तार की आवश्यकता नहीं है।

यहां एक नोट - यदि आपका आइटम (सूची के शीर्ष पर स्क्रॉल करना चाहते हैं) स्क्रीन पर दिखाई दे रहा है, तो यह अच्छी तरह से काम करने वाला है और आप बस इसे शीर्ष पर स्वचालित रूप से स्क्रॉल करना चाहते हैं। यह तब उपयोगी होता है जब आपकी सूची के अंतिम आइटम में कुछ क्रिया होती है, जो एक ही सूची में नए आइटम जोड़ता है और आप उपयोगकर्ता को नई जोड़ी गई वस्तुओं पर ध्यान केंद्रित करना चाहते हैं:

int recyclerViewTop = recyclerView.getTop();
int positionTop = recyclerView.findViewHolderForAdapterPosition(positionToScroll) != null ? recyclerView.findViewHolderForAdapterPosition(positionToScroll).itemView.getTop() : 200;
final int calcOffset = positionTop - recyclerViewTop; 
//then the actual scroll is gonna happen with (x offset = 0) and (y offset = calcOffset)
recyclerView.scrollBy(0, offset);

यह विचार सरल है: 1. हमें पुनरावर्तन तत्व के शीर्ष समन्वय की आवश्यकता है; 2. हमें उस आइटम का शीर्ष समन्वय प्राप्त करना होगा जिसे हम शीर्ष पर स्क्रॉल करना चाहते हैं; 3. गणना की गई ऑफसेट के साथ अंत में हमें करना होगा

recyclerView.scrollBy(0, offset);

200 उदाहरण के लिए केवल हार्ड कोडित पूर्णांक मूल्य है जिसे आप उपयोग कर सकते हैं यदि व्यूअर आइटम मौजूद नहीं है, क्योंकि यह संभव है।


0

मैं स्क्रॉल अवधि के मुद्दे को अधिक पूरी तरह से संबोधित करना चाहता हूं , जिसे आपको किसी भी पुराने उत्तर को चुनना चाहिए, वास्तव में वर्तमान स्थिति से लक्ष्य स्थान तक पहुंचने के लिए आवश्यक स्क्रॉलिंग की मात्रा के अनुसार नाटकीय रूप से (और अस्वीकार्य) अलग-अलग होंगे।

एक समान स्क्रॉल अवधि प्राप्त करने के लिए वेलोसिटी (पिक्सेल प्रति मिलीसेकंड) को प्रत्येक व्यक्तिगत आइटम के आकार के लिए होना चाहिए - और जब आइटम गैर-मानक आयाम के होते हैं तो जटिलता का एक नया स्तर जोड़ा जाता है।

यही कारण है कि RecyclerView डेवलपर्स ने चिकनी स्क्रॉलिंग के इस महत्वपूर्ण पहलू के लिए बहुत कठिन टोकरी को तैनात किया ।

यह मानते हुए कि आप अर्ध-समरूप स्क्रॉल अवधि चाहते हैं, और आपकी सूची में अर्ध-समरूप आइटम शामिल हैं, तो आपको कुछ इस तरह की आवश्यकता होगी।

/** Smoothly scroll to specified position allowing for interval specification. <br>
 * Note crude deceleration towards end of scroll
 * @param rv        Your RecyclerView
 * @param toPos     Position to scroll to
 * @param duration  Approximate desired duration of scroll (ms)
 * @throws IllegalArgumentException */
private static void smoothScroll(RecyclerView rv, int toPos, int duration) throws IllegalArgumentException {
    int TARGET_SEEK_SCROLL_DISTANCE_PX = 10000;     // See androidx.recyclerview.widget.LinearSmoothScroller
    int itemHeight = rv.getChildAt(0).getHeight();  // Height of first visible view! NB: ViewGroup method!
    itemHeight = itemHeight + 33;                   // Example pixel Adjustment for decoration?
    int fvPos = ((LinearLayoutManager)rv.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
    int i = Math.abs((fvPos - toPos) * itemHeight);
    if (i == 0) { i = (int) Math.abs(rv.getChildAt(0).getY()); }
    final int totalPix = i;                         // Best guess: Total number of pixels to scroll
    RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(rv.getContext()) {
        @Override protected int getVerticalSnapPreference() {
            return LinearSmoothScroller.SNAP_TO_START;
        }
        @Override protected int calculateTimeForScrolling(int dx) {
            int ms = (int) ( duration * dx / (float)totalPix );
            // Now double the interval for the last fling.
            if (dx < TARGET_SEEK_SCROLL_DISTANCE_PX ) { ms = ms*2; } // Crude deceleration!
            //lg(format("For dx=%d we allot %dms", dx, ms));
            return ms;
        }
    };
    //lg(format("Total pixels from = %d to %d = %d [ itemHeight=%dpix ]", fvPos, toPos, totalPix, itemHeight));
    smoothScroller.setTargetPosition(toPos);
    rv.getLayoutManager().startSmoothScroll(smoothScroller);
}

पुनश्च: मैं उस दिन को शाप देता हूं जिस दिन मैंने अंधाधुंध रूप से लिस्ट व्यू को रिसायकलर व्यू में बदलना शुरू किया ।


0

आप अपनी सूची को अंतिम list.reverse()और अंतिम कॉल के द्वारा बदल सकते हैंRecylerView.scrollToPosition(0)

    list.reverse()
    layout = LinearLayoutManager(this,LinearLayoutManager.VERTICAL,true)  
    RecylerView.scrollToPosition(0)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.