RecyclerView - Belirli bir pozisyonda öğenin üst kısmına doğru kaydırma nasıl yapılır?


125

Bir RecyclerView'da, aşağıdakileri kullanarak aniden seçili bir öğenin üstüne gidebiliyorum:

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

Ancak bu , öğeyi aniden en üst konuma taşır. Sorunsuz bir şekilde bir öğenin en üstüne gitmek istiyorum .

Ben de denedim:

recyclerView.smoothScrollToPosition(position);

ancak öğeyi üstte seçilen konuma taşımadığı için iyi çalışmaz. Yalnızca, konumdaki öğe görünene kadar listeyi kaydırır.

Yanıtlar:


225

RecyclerViewgenişletilebilir olacak şekilde tasarlanmıştır, bu nedenle sadece kaydırmayı gerçekleştirmek için LayoutManager( droidev'in önerdiği gibi ) alt sınıflara ayırmaya gerek yoktur .

Bunun yerine, SmoothScrollertercihe göre bir oluşturun SNAP_TO_START:

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

Şimdi kaydırmak istediğiniz konumu ayarlayın:

smoothScroller.setTargetPosition(position);

ve SmoothScroller'ı LayoutManager'a iletin:

layoutManager.startSmoothScroll(smoothScroller);

9
Bu kısa çözüm için teşekkürler. Uygulamamda dikkate almam gereken iki şey daha var: Yatay kaydırma görünümüm olduğu için ayarlamam gerekiyordu protected int getHorizontalSnapPreference() { return LinearSmoothScroller.SNAP_TO_START; }. Ayrıca soyut yöntemi uygulamalıydım public PointF computeScrollVectorForPosition(int targetPosition) { return layoutManager.computeScrollVectorForPosition(targetPosition); }.
AustrianDude

2
RecyclerView genişletilebilir olacak şekilde tasarlanabilir, ancak bunun gibi basit bir şey eksik olduğu için tembel hissettiriyor. İyi cevap tho! Teşekkür ederim.
Michael

8
Başlamasına izin vermenin herhangi bir yolu var mı, ama X dp ofsetiyle?
Mark Buikema

5
hızını yavaşlatmak mümkün mü?
Alireza Noorali

3
Ayrıca kaydırmanın gerçekleştiği hızın değiştirilip değiştirilemeyeceğini (daha yavaş) merak ediyorum @AlirezaNoorali
vikzilla

112

bunun için özel bir LayoutManager oluşturmanız gerekir

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

bunu RecyclerView'ınız için kullanın ve smoothScrollToPosition'ı çağırın.

misal :

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

bu, belirtilen konumdaki RecyclerView öğesinin en üstüne kayacaktır.

Bu yardımcı olur umarım.


Önerilen LayoutManager'ı uygularken sorun yaşadım. Buradaki cevap benim için çok daha kolay çalıştı: stackoverflow.com/questions/28025425/…
arberg

3
Sadece saatler süren acıdan sonra faydalı cevap, bu tasarlandığı gibi çalışıyor!
wblaschko

1
@droidev Örneğinizi yumuşak kaydırma ile kullandım ve genel olarak SNAP_TO_START ihtiyacım olan şey ama benim için bazı sorunlarda çalışıyor. Bazen kaydırma, geçtiğimden önce 1,2,3 veya 4 konum önce duruyor smoothScrollToPosition. Bu sorun neden ortaya çıkabilir? Teşekkürler.
Natasha

kodunuzu bize bir şekilde ulaşabilir misiniz? Bunun kod sorununuz olduğundan oldukça eminim.
droidev

1
Kabul edilen cevaba rağmen doğru cevap bu
Alessio

26

This is an uzatma işlevi ı yazdı KOTLIN ile kullanım için RecyclerView(@ Paul Woitaschek Yanıta göre):

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

Bunu şu şekilde kullanın:

myRecyclerView.smoothSnapToPosition(itemPosition)

Bu çalışıyor! Düzgün kaydırmayla ilgili sorun, büyük listeleriniz olduğunda ve ardından aşağıya veya yukarıya kaydırmanın uzun, uzun bir zaman almasıdır
portföy oluşturucu

@vovahost İstenilen konumu nasıl ortalayabilirim?
ysfcyln

@ysfcyln Bu stackoverflow.com/a/39654328/1502079 yanıtını kontrol edin
vovahost

12

Böyle deneyebiliriz

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

5

Ofset Y / X konumunu uygulamak için LinearSmoothScroller'da calculateDyToMakeVisible / calculateDxToMakeVisible işlevini geçersiz kılın

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

Aradığım şey buydu. X / y'nin ofset konumunu uygulamak için bunu paylaştığınız için teşekkür ederiz :)
Shan Xeeshi

3

A kaydırmanın bulduğum en kolay yolu RecyclerViewşudur:

// 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
Bu konum zaten görünür durumdaysa bu, bir konuma kaydırılmaz. Konumu görünür kılmak için ihtiyaç duyduğu en az miktarda kaydırır. Bu yüzden neden belirli ofsetli olana ihtiyacımız var.
TatiOverflow

3

Böyle Kullandım:

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

2

Çözüm için teşekkürler, @droidev. Kotlin çözümü arayan biri varsa, şuna bakın:

    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
Teşekkürler @pankaj, Kotlin'de bir çözüm arıyordu.
Akshay Kumar

1
  1. "LinearLayout" sınıfını genişletin ve gerekli işlevleri geçersiz kılın
  2. Parçanızda veya aktivitenizde yukarıdaki sınıfın bir örneğini oluşturun
  3. "RecyclerView.smoothScrollToPosition (targetPosition) öğesini çağırın

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

Not: Yukarıdaki örnek YATAY yöne ayarlanmıştır, başlatma sırasında DÜŞEY / YATAY geçebilirsiniz.

Yönü VERTICAL olarak ayarlarsanız, " calculateDxToMakeVisible " değerini " calculateDyToMakeVisible " olarak değiştirmelisiniz (ayrıca süper tip çağrı dönüş değerini de unutmayın)

Activity / Fragment.kt :

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

0

Muhtemelen @droidev yaklaşımı doğrudur, ancak ben sadece biraz farklı bir şey yayınlamak istiyorum, bu temelde aynı işi yapar ve LayoutManager'ın uzatılmasını gerektirmez.

Buradaki bir NOT - bu, öğeniz (listenin en üstünde kaydırmak istediğiniz öğe) ekranda görünüyorsa ve yalnızca otomatik olarak en üste kaydırmak istiyorsanız iyi çalışacaktır. Listenizdeki son öğenin aynı listeye yeni öğeler ekleyen bir eylemi olduğunda ve kullanıcıyı yeni eklenen öğelere odaklamak istediğinizde kullanışlıdır:

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

Fikir basit: 1. Recyclerview öğesinin üst koordinatını almamız gerekiyor; 2. Yukarı kaydırmak istediğimiz görünüm öğesinin üst koordinatını almalıyız; 3. Hesaplanan ofset ile sonunda yapmamız gereken

recyclerView.scrollBy(0, offset);

200, görüntü sahibi öğesi yoksa kullanabileceğiniz yalnızca örnek olarak kodlanmış tam sayı değeridir, çünkü bu da mümkündür.


0

Önceki yanıtlardan herhangi birini seçmeniz durumunda, mevcut konumdan hedef konuma ulaşmak için gereken kaydırma miktarına göre gerçekten önemli ölçüde (ve kabul edilemeyecek şekilde) değişecek olan kaydırma süresi konusunu daha tam olarak ele almak istiyorum .

Tek tip bir kaydırma süresi elde etmek için hız (milisaniye başına piksel) her bir öğenin boyutunu hesaba katmalıdır - ve öğeler standart olmayan boyutta olduğunda, yepyeni bir karmaşıklık düzeyi eklenir.

Bu yüzden olabilir RecyclerView geliştiriciler konuşlandırılmış çok sert düzgün kaydırma bu hayati yönü için sepet.

Yarı tekdüze bir kaydırma süresi istediğinizi ve listenizin yarı tek tip öğeler içerdiğini varsayarsak, böyle bir şeye ihtiyacınız olacaktır.

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

Not: Ben gün başladı küfür gelişigüzel dönüştürme ListView için RecyclerView .


-1

Listenizi son olarak list.reverse()arayarak tersine çevirebilirsinizRecylerView.scrollToPosition(0)

    list.reverse()
    layout = LinearLayoutManager(this,LinearLayoutManager.VERTICAL,true)  
    RecylerView.scrollToPosition(0)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.