Soru Android: RecyclerView'ın şu anki X ofsetini nasıl alabilirim?


Kullanıyorum Scrollview sonsuz bir "Zaman Seçici Atlı Karınca" için ve en iyi yaklaşım olmadığını öğrendim (son soru)

Şimdi buldum Recycler Görüntüle fakat Mevcut kaydırma ofsetini geri dönüştürücünün X yönüne alamıyorum.? (Her bir öğenin 100 piksel genişliğinde olduğunu ve ikinci öğenin yalnızca% 50 görülebildiğini düşünelim, böylece kaydırma ofseti 150 pikseldir)

  • tüm öğeler aynı genişliğe sahiptir, ancak son öğeden sonra ilk önce boşluk olur
  • recyclerView.getScrollX() döner 0 (dokümanlar şunu söylüyor: ilk kaydırma değeri)
  • LayoutManager vardır findFirstVisibleItemPositionama X ofsetini bununla hesaplayamıyorum

GÜNCELLEŞTİRME

Sadece X-Konumunu takip etmenin bir yolunu buldum. onScrolled geri arama, ama her zaman onu izlemek yerine gerçek değeri almayı tercih ederim!

private int overallXScroll = 0;
//...
mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);

            overallXScroll = overallXScroll + dx;

            Log.i("check","overallXScroll->" + overallXScroll);

        }
    });

44
2017-12-16 15:10


Menşei


"x" değil, daha çok item pozisyonunu mu kastediyorsunuz? - pskink
Sadece (?) sorumu açıklığa kavuşturdum. Tüm öğelerin X yönünde geçerli ofsetini arıyorum. - longilong
Bunu denedin mi? developer.android.com/reference/android/support/v7/widget/...? - pskink
RecyclerView, getScrollX / Y'yi desteklemiyor çünkü doğru sonucu garanti edemiyor. Örneğin, kullanıcı scrollToPosition (100) öğesini arayabilir. Gerçek scrollY'yi hesaplamak için, 0 ile 100 arasındaki tüm öğeleri düzenlemek zorunda kalacaktır. Mümkün değil. setOnScrollListener Bağdaştırıcı içeriğiniz değişmediği sürece çalışacaktır. Alternatif olarak, kullanabilirsiniz LLM#findLastVisibleItemPosition ve view.getLeft () kullanarak toplam sapmayı kendiniz hesaplayın - yigit
Dinleyiciye kaydırılmış mesafe ekleyerek toplam kaydırma tasarrufu, şimdiye kadar bulduğum en basit ve çalışan çözümdür. Ama biliyorum ki, RecyclerView sistem tarafından öldürüldükten sonra aktiviteyi sürdürürken kaydırma pozisyonunu kaydeder. Yani, toplam kaydırma içeren özel değişkenimiz arka plandan devam ettikten sonra senkronizasyon dışı olabilir. - Deepscorn


Cevaplar:


RecyclerView zaten yatay ve dikey kaydırma ofset elde etmek için bir yöntem var

mRecyclerView.computeHorizontalScrollOffset()
mRecyclerView.computeVerticalScrollOffset()

Bu, aynı yükseklikte (dikey kaydırma ofseti için) ve aynı genişlikte (yatay kaydırma ofseti için) bulunan RecyclerViews için çalışacaktır.


45
2017-09-30 09:55



Bu benim için işe yaramıyor, bazen sadece liste boyunca ilerlerken neredeyse 400px atlar, bir şey eksik gibi görünüyor - A. Steenbergen
Hücrelerin aynı genişliğe sahip olmaması (ya da kaydırma yönüne bağlı olarak yüksekliğin) olması. computeXScrollOffset, ortalama hücre boyutlarını kullanır, bu yüzden hücreleriniz boyut olarak değişirse, bu yöntem işe yaramaz. Bunu hesaplamak için iyi bir çözüm bulmak için saatlerce çalışıyorum :) - andreimarinescu
Bu kabul edilen cevap olmalı. - TomTaila
yanlış değerler döndürebilir, kullanmayın! - iscariot
@andreimarinescu henüz herhangi bir çözüm? - urSus


enter image description here

Çözüm 1:  setOnScrollListener

X-Position'ınızı sınıf değişkeni olarak kaydedin ve her bir değişikliği onScrollListener. Sıfırlamadığınızdan emin olun overallXScroll (F. E. onScreenRotationChange)

 private int overallXScroll = 0;
 //...
 mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);

        overallXScroll = overallXScroll + dx;
        Log.i("check","overall X  = " + overallXScroll);

    }
 });

Çözüm 2: Mevcut pozisyonu hesapla.

Benim durumumda, zaman değerleri ile dolu bir yatay Listem var (0:00, 0:30, 1:00, 1:30 ... 23:00, 23:30). Ekranın ortasında bulunan zaman kaleminden zamanı hesaplıyorum (hesaplama noktası). İşte bu yüzden benim tam X-Kaydırma Pozisyonuna ihtiyacım var. RecycleView

  • Her zaman öğesi aynı genişlikte 60dp'ye sahiptir (fyi: 60dp = 30min, 2dp = 1 dk)
  • İlk öğe (Başlık öğesi), merkeze 0dk ayarlamak için ekstra bir dolguya sahiptir

    private int mScreenWidth = 0;
    private int mHeaderItemWidth = 0;
    private int mCellWidth = 0;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
    
      //init recycle views
      //...
      LinearLayoutManager mLLM = (LinearLayoutManager) getLayoutManager();
      DisplayMetrics displaymetrics = new DisplayMetrics();
      this.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
      this.mScreenWidth = displaymetrics.widthPixels;
    
      //calculate value on current device
      mCellWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60, getResources()
            .getDisplayMetrics());
    
      //get offset of list to the right (gap to the left of the screen from the left side of first item)
      final int mOffset = (this.mScreenWidth / 2) - (mCellWidth / 2);
    
      //HeaderItem width (blue rectangle in graphic)
      mHeaderItemWidth = mOffset + mCellWidth;
    
      mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
    
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
    
            //get first visible item
            View firstVisibleItem = mLLM.findViewByPosition(mLLM.findFirstVisibleItemPosition());
    
            int leftScrollXCalculated = 0;
            if (firstItemPosition == 0){
                   //if first item, get width of headerview (getLeft() < 0, that's why I Use Math.abs())
                leftScrollXCalculated = Math.abs(firstVisibleItem.getLeft());
            }
            else{
    
                   //X-Position = Gap to the right + Number of cells * width - cell offset of current first visible item
                   //(mHeaderItemWidth includes already width of one cell, that's why I have to subtract it again)
                leftScrollXCalculated = (mHeaderItemWidth - mCellWidth) + firstItemPosition  * mCellWidth + firstVisibleItem.getLeft();
            }
    
            Log.i("asdf","calculated X to left = " + leftScrollXCalculated);
    
        }
    });
    

    }


42
2017-12-18 12:01



nedir firstItemPosition - ZeroOne
Çok hızlı ilerlerken Çözüm 1 belirli bir miktar hata yapabilir. - lovefish
Çözüm 2 tamamen geride kaldım. Çözüm 1'i kullanıyordum ve hızlı bir şekilde yukarı ve aşağı kaydırırken birkaç piksel tarafından kaydırmaya devam ediyordum ama şimdi sadece mLLM.findFirstCompletelyVisibleItemPosition() == 0 ve üstte olduğumu biliyorum ve tekrar senkronize olmak için kaydırmamı sıfırlayabilirim. FYI: eğer kimse merak ediyorsa mLLM öyle mi LinearLayoutManager ve şunları yaparak bir tane alabilirsiniz: LinearLayoutManager mLLM = (LinearLayoutManager) getLayoutManager(); biriyle çalıştığınızı varsayarak LayoutManager bu form uzar LinearLayoutManager Tabii ki Thx çok uzun @longilong! - Francois Dermu
@ZeroOne, firstImtePosition = mLLM.findFirstVisibleItemPosition (); Doğrusal Düzen yöneticisinden, ilk görünür öğe pozisyonunu alın. - Mallikarjungouda Annigeri


Doğru yol için @ umar-qureshi'ye teşekkürler! Kaydırma yüzdesini şu şekilde belirleyebilirsiniz: Offset, Extent, ve Range öyle ki

percentage = 100 * offset / (range - extent)

Örneğin (içine konulacak OnScrollListener):

int offset = recyclerView.computeVerticalScrollOffset();
int extent = recyclerView.computeVerticalScrollExtent();
int range = recyclerView.computeVerticalScrollRange();

int percentage = (int)(100.0 * offset / (float)(range - extent));

Log.i("RecyclerView, "scroll percentage: "+ percentage + "%");

24
2018-05-08 02:11





public class CustomRecyclerView extends RecyclerView {

    public CustomRecyclerView(Context context) {
        super(context);
    }

    public CustomRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public int getHorizontalOffset() {
        return super.computeHorizontalScrollOffset();
    }
}

Ofset gereken yere göre:

recyclerView.getHorizontalOffset()

8
2018-02-20 22:48



@MichaelDePhillips Sıfır döndüren içsel sınıfın LayoutManager kaynağına bakın ama bu yöntemi önemsemiyoruz;) İlgilendiğimiz gerçek yöntem aşağıdakileri döndürür: return mLayout.canScrollHorizontally ()? mLayout.computeHorizontalScrollOffset (mState): 0; - SemaphoreMetaphor
@MichaelDePhillips Ve bu yöntemi umursamamanın nedeni, bir geridönüşümünü başlatırken yaptığımız ilk şeylerden biri, bir layoutmanager oluşturmasıdır. Çoğu durumda bu, LayoutManager yöntemini dikey ve yatay rahatsızlık için doğru bir ofset sağlamak üzere genişleten bir LinearLayoutManager olacak. - SemaphoreMetaphor
Furthur muayenesinde, haklısın. Temizlediğin için teşekkürler. Yorumumu düzenleyeceğim. - Michael DePhillips
Bu, keyfi bir birimde int döndürür ve güvenilir bir şekilde doğru bir kaydırma miktarı vermez; bunun ne için olduğu değil. Örneğin, ilk görünen konumum 0 ile 1 arasında hareket ettiğinde, 1850px ile 300 piksel arasında düşer. - Tom
computeHorizontalScrollOffset()kaydırma çubuğu boyutunu hesaplamak için kullanılır. Bir hücrenin bir merkezi ekrandan düzgün bir şekilde kayarken bu değerin aniden düşmesiyle ilgili bir sorun var. Öte yandan, @longilong'un 1. çözümü güvenilir ofset elde edebilir. - Xiang


RecyclerView'ı geçersiz kılmak onScrolled(int dx,int dy) Kaydırma ofsetini alabilirsiniz. ancak öğeyi eklediğinizde veya kaldırdığınızda, değer yanlış olabilir.

public class TempRecyclerView extends RecyclerView {
   private int offsetX = 0;
   private int offsetY = 0;
   public TempRecyclerView(Context context) {
       super(context);
   }

   public TempRecyclerView(Context context, @Nullable AttributeSet attrs) {
      super(context, attrs);
   }

   public TempRecyclerView(Context context, @Nullable AttributeSet attrs,int defStyle){
      super(context, attrs, defStyle);
   }
/**
 * Called when the scroll position of this RecyclerView changes. Subclasses 
   should usethis method to respond to scrolling within the adapter's data 
   set instead of an explicit listener.
 * <p>This method will always be invoked before listeners. If a subclass 
   needs to perform any additional upkeep or bookkeeping after scrolling but 
   before listeners run, this is a good place to do so.</p>
 */
   @Override
   public void onScrolled(int dx, int dy) {
  //  super.onScrolled(dx, dy);
      offsetX+=dx;
      offsetY+=dy;
     }
 }

0
2017-12-29 13:39





Geçerli sayfayı ve ofsetini RecyclerView genişliğine göre bilmeniz gerekiyorsa, böyle bir şeyi denemek isteyebilirsiniz:

recyclerView.addOnScrollListener(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);

        final int scrollOffset = recyclerView.computeHorizontalScrollOffset();
        final int width = recyclerView.getWidth();
        final int currentPage = scrollOffset / width;
        final float pageOffset = (float) (scrollOffset % width) / width;

        // the following lines just the example of how you can use it
        if (currentPage == 0) {
            leftIndicator.setAlpha(pageOffset);
        }

        if (adapter.getItemCount() <= 1) {
            rightIndicator.setAlpha(pageOffset);
        } else if (currentPage == adapter.getItemCount() - 2) {
            rightIndicator.setAlpha(1f - pageOffset);
        }
    }
});

Eğer currentPage sonra 0 dizinine sahip leftIndicator (ok) şeffaf olacak ve öyle olacak ve rightIndicator sadece bir tane varsa page (örnekte, bağdaştırıcının gösterilmesi için her zaman en az bir öğe vardır, böylece boş değer için herhangi bir denetim yoktur).

Bu şekilde temelde geri çağırma özelliğini kullanıyormuşsunuz gibi neredeyse aynı işlevselliğe sahip olacaksınız. pageScrolled itibaren ViewPager.OnPageChangeListener.


0
2018-05-09 15:24