Soru İç içe geçmiş RecyclerView. Çocuk RecyclerView kaydırma yaparken ebeveyn RecyclerView'un nasıl kaydırılmasının nasıl önlenir?


Bir yatay uygulamayı deniyorum recyclerview ve her bir recyclerview dikey olacak recyclerview bir ızgara düzeni ile. Karşılaştığım sorun, çocuğu kaydırmaya çalıştığımda recyclerview dikey olarak bazen ebeveyn recyclerview kaydırmayı alır ve yatay olarak kaydırmaya başlar. Bunu düzeltmeye çalıştığım yaklaşımlar,

  1. setNestedScrollingEnabled(false) ebeveyn üzerinde recyclerview
  2. İçinde onTouch() çocuğun recyclerview Ebeveynte dokunma olaylarını devre dışı bırakıyorum recyclerview arayarak requestdisallowinterceptTouchevent(false)

Yukarıdaki çözümlerden hiçbiri problem için mükemmel bir düzeltme sağlamaz. Herhangi bir yardım takdir


25
2018-03-29 13:52


Menşei




Cevaplar:


Sorun benim için ilginç görünüyordu. Bu yüzden uygulamaya çalıştım ve bu benim elde ettiğim şeydi (aynı zamanda burada video) oldukça pürüzsüz.

enter image description here

Yani böyle bir şey deneyebilirsiniz:

Tanımlamak CustomLinearLayoutManager uzatma LinearLayoutManager bunun gibi:

public class CustomLinearLayoutManager extends LinearLayoutManager {

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

    @Override
    public boolean canScrollVertically() {
        return false;
    }
}

ve bunu ayarla CustomLinearLayoutManager ebeveynine RecyclerView.

RecyclerView parentRecyclerView = (RecyclerView)findViewById(R.id.parent_rv);
CustomLinearLayoutManager customLayoutManager = new CustomLinearLayoutManager(this, LinearLayoutManager.HORIZONTAL,false);
parentRecyclerView.setLayoutManager(customLayoutManager);
parentRecyclerView.setAdapter(new ParentAdapter(this)); // some adapter

Şimdi çocuk için RecyclerView, özel tanımla CustomGridLayoutManager uzatma GridLayoutManager:

public class CustomGridLayoutManager extends GridLayoutManager {

    public CustomGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public CustomGridLayoutManager(Context context, int spanCount) {
        super(context, spanCount);
    }

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

    @Override
    public boolean canScrollHorizontally() {
        return false;
    }
}

ve olarak ayarla layoutManger çocuğa RecyclerView:

childRecyclerView = (RecyclerView)itemView.findViewById(R.id.child_rv);
childRecyclerView.setLayoutManager(new CustomGridLayoutManager(context, 3));
childRecyclerView.setAdapter(new ChildAdapter()); // some adapter

Yani temelde ebeveyn RecyclerView sadece yatay kaydırmaları ve çocuğu dinliyor RecyclerView sadece dikey kaydırmaları dinliyor.

Bununla birlikte, aynı zamanda diyagonal kaydırma işlemlerini (dikey veya yatay olarak çok az çarpık) işlemek isterseniz, bir hareket dinleyicisini ebeveyn  RecylerView.

public class ParentRecyclerView extends RecyclerView {

    private GestureDetector mGestureDetector;

    public ParentRecyclerView(Context context) {
        super(context);
        mGestureDetector = new GestureDetector(this.getContext(), new XScrollDetector());
       // do the same in other constructors
    }

   // and override onInterceptTouchEvent

   @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return super.onInterceptTouchEvent(ev) && mGestureDetector.onTouchEvent(ev);
    }

}

Nerede XScrollDetector olduğu

class XScrollDetector extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            return Math.abs(distanceY) < Math.abs(distanceX);
        }
}

Böylece ParentRecyclerView kaydırma olayını işlemek için çocuk görünümünü (bizim durumumuzda, VerticalRecyclerView) sorar. Çocuk görünümü ele alınırsa, ebeveyn başka hiçbir şey yapmaz, ebeveyn sonunda en sonunda kaydırma işlemini gerçekleştirir.


20
2018-04-19 09:41



Doğrusal düzeni genişletmek büyük bir öneriydi. Uygulamamı neredeyse tamamen NestedScrollList olarak değiştirdim. Bana bu çabayı kurtardı gibi görünüyor. - wtk
@wtk, harika. :) - Rohit Arya
İnanılmaz cevap, benim durumumda, tüm çözüm, sadece SimpleOnGestureListener parçası kullanmak zorunda kalmadı ve mükemmel çalışır. Teşekkürler. - Pedro Okawa


parentNewedScrollingEnabled (false) ebeveyn geri dönüstürücü görünümünde

Ne deneyebilirsin setNestedScrollingEnabled(false) üzerinde çocuk Varsa RecyclerView. RecyclerView 'in nestedscroll-ness'i bir çocuğunkidür (bu yüzden uygular. NestedScrollingChild).

Çocuk geridönüşümünün onTouch () 'ında dokunma olaylarını devre dışı bırakıyorum   ebeveyn geri dönüşüm görüşmesi   requestdisallowinterceptTouchevent (yanlış)

Bu işe yaramalı, ama ne yapmanız gerektiği requestDisallowInterceptTouchEvent(true), değil false. RecyclerView alt sınıfını kullanıyorsanız, geçersiz kılabilirsiniz. onTouchEvent:

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_UP) {
        // ensure we release the disallow request when the finger is lifted
        getParent().requestDisallowInterceptTouchEvent(false);
    } else {
        getParent().requestDisallowInterceptTouchEvent(true);
    }
    // Call the super class to ensure touch handling
    return super.onTouchEvent(event);
}

Ya da dışarıdan bir dokunma dinleyicisi ile

child.setOnTouchListener(new View.OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (v.getId() == child.getId()) {
            if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_UP) {
                // ensure we release the disallow request when the finger is lifted
                child.getParent().requestDisallowInterceptTouchEvent(false);
            } else {
                child.getParent().requestDisallowInterceptTouchEvent(true);
            }
        }
        // Call the super class to ensure touch handling
        return super.onTouchEvent(event);
    }
});

6
2018-04-13 09:04





Bu sorunu, size karşı olan yaklaşımı (ve diğer herkes) alarak benzer bir projeye yerleştirdim.

Çocuğun ebeveynlere olaylara bakmayı ne zaman bırakacağını söylemesine izin vermek yerine, ebeveynin görmezden geleceğine (yönüne göre) karar verdim. Bu yaklaşım, biraz daha fazla iş olabilir, ancak özel bir görünüm gerektirir. Aşağıda, Dış / Ebeveyn görünümü olarak kullanılacak olanı yazdım.

public class DirectionalRecyclerView extends RecyclerView {

    private static float LOCK_DIRECTION_THRESHOLD; //The slop
    private float startX;
    private float startY;
    private LockDirection mLockDirection = null;

    public DirectionalRecyclerView(Context context) {
        super(context);
        findThreshold(context);
    }

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

    public DirectionalRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        findThreshold(context);
    }

    private void findThreshold(Context context) {
        //last number is number of dp to move before deciding that's a direction not a tap, you might want to tweak it
        LOCK_DIRECTION_THRESHOLD = context.getResources().getDisplayMetrics().density * 12;
    }

    //events start at the top of the tree and then pass down to
    //each child view until they reach where they were initiated
    //unless the parent (this) method returns true for this visitor
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                startX = event.getX();
                startY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (mLockDirection == null) {
                    float currentX = event.getX();
                    float currentY = event.getY();
                    float diffX = Math.abs(currentX - startX);
                    float diffY = Math.abs(currentY - startY);
                    if (diffX > LOCK_DIRECTION_THRESHOLD) {
                        mLockDirection = LockDirection.HORIZONTAL;
                    } else if (diffY > LOCK_DIRECTION_THRESHOLD) {
                        mLockDirection = LockDirection.VERTICAL;
                    }
                } else {
                    //we have locked a direction, check whether we intercept
                    //the future touches in this event chain
                    //(returning true steals children's events, otherwise we'll
                    // just let the event trickle down to the child as usual)
                    return mLockDirection == LockDirection.HORIZONTAL;
                }
                break;
            case MotionEvent.ACTION_UP:
                mLockDirection = null;
                break;
        }
        //dispatch cancel, clicks etc. normally
        return super.onInterceptTouchEvent(event);
    }

    private enum LockDirection {
        HORIZONTAL,
        VERTICAL
    }

}

6
2018-04-13 09:43



Bu, farklı bir senaryo için benim için çalıştı - çöken araç çubuğu ve iç içe kaydırma görünümü içeren yatay RV içeren koordinatör. Değişmesi gereken tek şey ayarlamaktır mLockDirection = null; içinde dispatchTouchEvent() çünkü onInterceptTouchEvent() ve onTouchEvent() tüm durumlarda çağrılmaz. - WindRider
İşte sonuç: i.giphy.com/qKfMIz3TbJ3hK.gif - WindRider
Bu değerli yaklaşım için teşekkürler. Ebeveynlerde bu tür şeyleri ele alma yaklaşımı çok daha kolay. Değiştirilmiş mantığınız ile yatay bir görüntüleyicinin içinde dikey olarak yapmayı başardım. - Stefan Sprenger


Şimdi deneyebilirsiniz android:nestedScrollingEnabled Google’ın nestedScrollingEnabled  (Sayı 197932)


4
2018-04-17 13:24





Dinleyiciyi iç içe geçmiş RecyclerView olarak ayarla

 View.OnTouchListener listener = new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    if (event.getAction() == MotionEvent.ACTION_MOVE
                            ) {
                        v.getParent().requestDisallowInterceptTouchEvent(true);

                    } else {
                        v.getParent().requestDisallowInterceptTouchEvent(false);

                    }
                    return false;
                }
            };

            mRecyclerView.setOnTouchListener(listener);

4
2018-04-19 13:36





Bunu dene. Benim kullanım durumum için çalıştı:

nestedRecyclerView.setOnTouchListener(new View.OnTouchListener() {

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        return true;
    }
});

3
2018-04-13 06:57





Aşağıdaki kodu deneyin, işe yarayacak umuyoruz.

nestedRecyclerView.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    int action = event.getAction();
                   switch (action) {
                  case MotionEvent.ACTION_DOWN:
                     // Disallow parent to intercept touch events.
                     v.getParent().requestDisallowInterceptTouchEvent(true);
                     break;

                 case MotionEvent.ACTION_UP:
                    // Allow parent to intercept touch events.
                    v.getParent().requestDisallowInterceptTouchEvent(false);
            break;
        }

                  // Handle inner(child) touch events.
                    v.onTouchEvent(event);
        return true;
                }
            });

3
2018-04-18 07:24





RecyclerView iç kaydırmak için kodun altında deneyin.

innerRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {

        @Override
        public void onTouchEvent(RecyclerView recycler, MotionEvent event) {
            // Handle on touch events here
            int action = event.getAction();
            switch (action) {
                case MotionEvent.ACTION_DOWN:
                    // Disallow Parent RecyclerView to intercept touch events.
                    recycler.getParent().requestDisallowInterceptTouchEvent(true);
                    break;

                case MotionEvent.ACTION_UP:
                    // Allow Parent RecyclerView to intercept touch events.
                    recycler.getParent().requestDisallowInterceptTouchEvent(false);
                    break;
            }


        }

        @Override
        public boolean onInterceptTouchEvent(RecyclerView recycler, MotionEvent event) {
            return false;
        }

    });

2
2018-04-13 07:26