web-dev-qa-db-fra.com

La vue Recyclerview ne fonctionne pas avec setNestedScrollingEnabled et ne recycle jamais aucune vue.

Je suis confronté à deux problèmes:

  1. L'auditeur de défilement ne fonctionnera pas
  2. La RecyclerView ne recycle jamais les vues lorsqu'elle est attachée à une NestedScrollView. Il agit comme une mise en page linéaire dans ScrollView. Il utilise beaucoup de mémoire et crée des décalages. 

J'attache un fragment de lecteur youtube au-dessus de la vue du recycleur, car je ne peux pas en insérer un dans la vue du recycleur. Dans mon code, vous pouvez voir qu'il y a une disposition du cadre.

Ma mise en page ressemble à ceci:

    <Android.support.v4.widget.NestedScrollView xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:id="@+id/nestedScroll"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:background="#ffffff"
    Android:orientation="vertical">


    <LinearLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:background="#ffffff"
        Android:orientation="vertical">

        <LinearLayout
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:orientation="vertical">

            <FrameLayout               
                Android:layout_width="match_parent"
                Android:layout_height="240dp"
                Android:layout_alignParentTop="true"/>





        </LinearLayout>


        <Android.support.v7.widget.RecyclerView
            Android:id="@+id/recycler_view"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:layout_below="@+id/youtube_layout"
            Android:visibility="visible"/>




    </LinearLayout>
</Android.support.v4.widget.NestedScrollView>

Je veux utiliser le listener de défilement pour charger plus d’éléments . J’ai donc essayé ce qui suit:

@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    Log.i(TAG, "onScrolled: ");
    // bail out if scrolling upward or already loading data
    if (dy < 0 || dataLoading.isDataLoading()) return;

    final int visibleItemCount = recyclerView.getChildCount();
    final int totalItemCount = layoutManager.getItemCount();
    final int firstVisibleItem = layoutManager.findFirstVisibleItemPosition();

    if ((totalItemCount - visibleItemCount) <= (firstVisibleItem )) {
        onLoadMore();
    }


}

Mais layoutManager.findFirstVisibleItemPosition()==0 ne fonctionne donc pas, et plus encore sur onScrolled never called twice puisque j'ai défini Recyclerview.setNestedScrollingEnabled (false)

donc j'ai essayé dans onBindView comme ça

   public void onBindViewHolder(RecyclerView.ViewHolder customViewHolder, int i) {
    Log.w("d","inside bind view");

    if(i>=getItemCount()-1  &&   !datamanager.isDataLoading()){
        datamanager.loadmoreData();
    }

mais le recylerview lie la vue tout à la fois avant même que je commence à faire défiler, donc cette méthode ne fonctionne pas non plus.

14
Asthme

La vue recycleur ne recycle jamais aucune vue lorsqu'elle est attachée à la vue de défilement imbriquée, elle agit comme une mise en page linéaire dans la vue de défilement. Elle crée une mémoire énorme et retarde l'écran.

Exactement. Vous ne pouvez pas placer un RecyclerView dans une autre vue défilante.

Cela ne fonctionnera pas, car la vue d'habillage doit connaître la hauteur totale de RecyclerView et RecyclerView ne peut connaître sa hauteur totale qu'en mesurant et en mettant en forme tous de ses éléments.

Comment régler ceci?

Ne placez pas un RecyclerView dans une autre vue défilante.

Si vous avez besoin d'un en-tête ou d'un pied de page, vous devrez l'ajouter à RecyclerView et laisser le RecyclerView se charger de l'afficher. Débarrassez-vous de votre ScrollView et déplacez votre en-tête à l'intérieur de RecyclerView.

Techniquement, il est également possible de charger des fragments à l'intérieur d'un RecyclerView, mais je dois admettre que le fonctionnement correct de ce dernier est un peu délicat.

Il y a aussi beaucoup de bibliothèques qui facilitent le processus, mon préféré étant Epoxy fabriqué par AirBnb, mais il y a aussi Groupie , et bien d'autres.

11
David Medenjak

J'ai géré ce genre de situation. Il y a un recyclage. La vue Recycler a viewpager. Le visualiseur a des fragments, puis il y a une vue de recyclage à l'intérieur du fragment.

Vous devez pirater les touches pour le faire défiler parfaitement.

private boolean interceptTouch = true;
    private static final int MIN_DISTANCE = 200;
    private float downX = 0, downY = 0, moveX = 0, moveY = 0, upX = 0, upY;

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

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


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (getRvTop() == 0) {
            onTouchEvent(ev);
        }
        return interceptTouch;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                downY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                moveX = event.getX();
                moveY = event.getY();
                break;
            case MotionEvent.ACTION_UP:
                upX = event.getX();
                upY = event.getY();
                break;
        }
        float deltaX = upX - downX;

        if (getViewTop() == 0 && moveY > downY && Math.abs(moveY - downY) > MIN_DISTANCE) {  // moving down
            interceptTouch = true;
        }else if (Math.abs(deltaX) > MIN_DISTANCE) {
            if (upX > downX) {    // Left to Right swipe action
                interceptTouch = false;
            }else {    // Right to left swipe action
                interceptTouch = false;
            }
        } else {  // screen tap
            interceptTouch = false;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return super.dispatchTouchEvent(ev);
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
    }

    @Override
    public void onScrolled(int dx, int dy) {
        if (getViewTop() <= 0) {
            interceptTouch = false;            // dispatch the touch to child when reached top
        } else {
            interceptTouch = true;           // do not dispatch the touch event
        }
        super.onScrolled(dx, dy);
    }

    public int getViewTop() {
        int top = 0;
        View v = this.getChildAt(1);
        if (v == null) {
            v = this.getChildAt(0);
        }
        if (v != null && v instanceof LinearLayout) {
            top = v.getTop();
        }
        return top;
    }

    public int getRvTop() {
        int top = -1;
        View v = this.getChildAt(1);
        if (v == null) {
            v = this.getChildAt(0);
        }
        if (v != null && v instanceof LinearLayout) {

            ViewPager vp = (ViewPager) v.findViewById(R.id.single_tribe_pager);
            if (vp != null) {
                int currentPos = vp.getCurrentItem();
                RecyclerView rv = (RecyclerView) vp.findViewWithTag("recyclerview" + currentPos);
                if (rv != null) {
                    View v1 = rv.getChildAt(0);
                    if (v1 != null && v1 instanceof CardView) {
                        top = v1.getTop() - ResourceUtils.getDimensionPixelOffset(R.dimen.padding_20);   // deducting 20 as we have give top margin as 20 to the top layout
                    }
                }
            }
        }
        return top;
    }
0
Harsh