Прокрутка RecyclerView не работает с NestedScrollView

У меня есть макет, в котором у меня есть NestedScrollView, содержащий изображение, несколько кнопок и RecycleView.

Когда я говорю recycleView.smoothScrollToPosition или recycleView.scrollToPosition(), в данный момент ничего не происходит. Отказаться от прокрутки даже на пиксель. Если я удалю NestedScrollView, он будет работать нормально, но если я потеряю эффект прокрутки в окружающих областях.

Кто-нибудь из вас встречался с этой проблемой раньше?

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout   xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="world.the.rule.com.testtollbarstuff.ScrollingActivity">

<android.support.design.widget.AppBarLayout
    android:id="@+id/app_bar"
    android:layout_width="match_parent"
    android:layout_height="@dimen/app_bar_height"
    android:fitsSystemWindows="true"
    android:orientation="vertical"
    android:theme="@style/AppTheme.AppBarOverlay">


    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:layout_collapseMode="none"
        app:popupTheme="@style/AppTheme.PopupOverlay" />


</android.support.design.widget.AppBarLayout>

<android.support.design.widget.CollapsingToolbarLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:fitsSystemWindows="true"
    app:contentScrim="?attr/colorPrimary"
    app:layout_scrollFlags="scroll">

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_collapseMode="parallax">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:adjustViewBounds="true"
                android:clickable="true"
                android:src="@drawable/mock_image" />

            <include layout="@layout/content_scrolling" />

        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CollapsingToolbarLayout>


person Karoly    schedule 21.12.2016    source источник
comment
Я сделал сейчас, но теперь эффект на всех!   -  person Karoly    schedule 21.12.2016
comment
в свой LinearLayout добавьте это свойство android:descendantFocusability="blocksDescendants".   -  person Harshad Pansuriya    schedule 21.12.2016
comment
@Кароли, сработало ли мое решение ??   -  person Jimit Patel    schedule 06.01.2017


Ответы (3)


Если вам просто нужна плавная прокрутка, то пользовательская прокрутка не требуется, как я упоминал ранее в какой-то другой теме.

Добавьте строки, как показано ниже, когда вы хотите начать плавную прокрутку

appBarLayout.setExpanded(false, /*true if animation required else false*/true);
recyclerView.smoothScrollToPosition(position);

Дополнительная информация

Кроме того, я не вижу RecyclerView в вашем макете, и есть вероятность, что вы сохранили его в LinearLayout, который снова является частью CollapsingToolbarLayout. Я совершенно не понимаю, почему вы сохранили RecyclerView как часть CollapsingToolbarLayout. Я дам макет (упрощенный), который я использую.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.CoordinatorLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.design.widget.AppBarLayout
            android:id="@+id/app_bar_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:expanded="true"
            app:layout_behavior="com.company.app.custom.CustomRecyclerScrollBehavior">

            <android.support.design.widget.CollapsingToolbarLayout
                android:id="@+id/collapsing_toolbar"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:contentScrim="?attr/colorPrimary"
                app:expandedTitleMarginEnd="64dp"
                app:expandedTitleMarginStart="48dp"
                app:layout_scrollFlags="scroll|exitUntilCollapsed">

                <FrameLayout
                    android:id="@+id/header_frame"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    app:layout_collapseMode="parallax"
                    app:layout_collapseParallaxMultiplier="0.8">

                    <com.company.app.custom.CustomViewPager
                        android:id="@+id/view_pager"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        app:calculation="height"
                        app:height_ratio="@integer/product_listitem_img_width_ratio"
                        app:width_ratio="@integer/product_listitem_img_height_ratio" />

                    <com.company.app.custom.CustomImageView
                        android:id="@+id/img_stock_layer"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:adjustViewBounds="true"
                        android:scaleType="fitXY"
                        android:src="@color/transparent_app_black"
                        android:visibility="gone"
                        app:calculation_type="height"
                        app:ratio_height="@integer/product_listitem_img_width_ratio"
                        app:ratio_width="@integer/product_listitem_img_height_ratio" />

                    <com.inneex.www.customfonts.FontTextView
                        android:id="@+id/lbl_out_of_stock"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="center"
                        android:text="@string/out_of_stock"
                        android:textColor="?android:attr/textColorPrimaryInverse"
                        android:textSize="13sp"
                        android:visibility="gone"
                        app:customFont="@string/font_ss_semibold" />

                    <LinearLayout
                        android:id="@+id/ll_page"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_gravity="bottom|center_horizontal"
                        android:orientation="horizontal"
                        android:paddingBottom="@dimen/inspire_detail_oval_margin_bottom" />

                </FrameLayout>

                <android.support.v7.widget.Toolbar
                    android:id="@+id/main_toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="@dimen/toolbar_height"
                    android:background="?attr/colorPrimary"
                    app:layout_anchor="@id/header_frame"
                    app:layout_collapseMode="pin"
                    app:title="">

                    <LinearLayout
                        android:layout_width="wrap_content"
                        android:layout_height="match_parent"
                        android:orientation="horizontal">

                        <com.inneex.www.customfonts.FontTextView
                            android:id="@+id/lbl_toolbar_product_name"
                            android:layout_width="0dp"
                            android:layout_height="wrap_content"
                            android:layout_gravity="center"
                            android:layout_weight="1"
                            android:ellipsize="end"
                            android:lines="1"
                            android:maxLines="1"
                            android:minLines="1"
                            android:textColor="?android:attr/textColorPrimary"
                            android:textSize="18sp"
                            app:customFont="@string/font_ss_semibold" />

                    </LinearLayout>
                </android.support.v7.widget.Toolbar>
            </android.support.design.widget.CollapsingToolbarLayout>

        </android.support.design.widget.AppBarLayout>

        <ImageView
            android:id="@+id/img_back"
            android:layout_width="@dimen/toolbar_height"
            android:layout_height="@dimen/toolbar_height"
            android:layout_gravity="top|start"
            android:scaleType="fitXY"
            android:src="@drawable/back"
            app:layout_collapseMode="parallax" />

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@color/white"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    </android.support.design.widget.CoordinatorLayout>

    <include
        layout="@layout/listitem_product_detail_buy_cheap"
        android:layout_width="match_parent"
        android:layout_height="@dimen/product_detail_button_buy_cheap_height"
        android:layout_alignParentBottom="true"
        android:layout_gravity="bottom" />

</RelativeLayout>

FrameLayout с id header_frame отвечает за большую панель инструментов, которая будет сворачиваться при прокрутке. Toolbar с id main_toolbar отвечает за свернутый вид панели инструментов. RecyclerView ниже AppBarLayout. ImageView предназначена для кнопки «Назад», которая будет отображаться после отображения свернутого Toolbar.

Для справки, чтобы сделать прокрутку плавной, я добавил CustomRecyclerScrollBehavior, о котором говорил с вами ранее. Вот что это

import android.content.Context;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by jimitpatel on 13/12/16.
 */

public class CustomRecyclerScrollBehavior extends AppBarLayout.Behavior {

    private Map<RecyclerView, RecyclerViewScrollListener> scrollListenerMap = new HashMap<>(); //keep scroll listener map, the custom scroll listener also keep the current scroll Y position.


    public CustomRecyclerScrollBehavior() {
    }

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

    /**
     *
     * @param coordinatorLayout
     * @param child The child that attached the behavior (AppBarLayout)
     * @param target The scrolling target e.g. a recyclerView or NestedScrollView
     * @param velocityX
     * @param velocityY
     * @param consumed The fling should be consumed by the scrolling target or not
     * @return
     */
    @Override
    public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) {
        if (target instanceof RecyclerView) {
            final RecyclerView recyclerView = (RecyclerView) target;
            if (scrollListenerMap.get(recyclerView) == null) {
                RecyclerViewScrollListener recyclerViewScrollListener = new RecyclerViewScrollListener(coordinatorLayout, child, this);
                scrollListenerMap.put(recyclerView, recyclerViewScrollListener);
                recyclerView.addOnScrollListener(recyclerViewScrollListener);
            }
            scrollListenerMap.get(recyclerView).setVelocity(velocityY);
            consumed = scrollListenerMap.get(recyclerView).getScrolledY() > 0; //recyclerView only consume the fling when it's not scrolled to the top
        }
        return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
    }

    private static class RecyclerViewScrollListener extends RecyclerView.OnScrollListener {
        private int scrolledY;
        private boolean dragging;
        private float velocity;
        private WeakReference<CoordinatorLayout> coordinatorLayoutRef;
        private WeakReference<AppBarLayout> childRef;
        private WeakReference<CustomRecyclerScrollBehavior> behaviorWeakReference;

        public RecyclerViewScrollListener(CoordinatorLayout coordinatorLayout, AppBarLayout child, CustomRecyclerScrollBehavior barBehavior) {
            coordinatorLayoutRef = new WeakReference<>(coordinatorLayout);
            childRef = new WeakReference<>(child);
            behaviorWeakReference = new WeakReference<>(barBehavior);
        }

        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            dragging = newState == RecyclerView.SCROLL_STATE_DRAGGING;
        }

        public void setVelocity(float velocity) {
            this.velocity = velocity;
        }

        public int getScrolledY() {
            return scrolledY;
        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            scrolledY += dy;

            if (scrolledY <= 0 && !dragging && childRef.get() != null && coordinatorLayoutRef.get() != null && behaviorWeakReference.get() != null) {
                //manually trigger the fling when it's scrolled at the top
                behaviorWeakReference.get().onNestedFling(coordinatorLayoutRef.get(), childRef.get(), recyclerView, 0, velocity, false);
            }
        }
    }
}

Этот класс используется для поддержания скорости прокрутки для RecyclerView во вложенных прокрутках. Это было изменено здесь

Надеюсь, это сработает для вас!

person Jimit Patel    schedule 05.01.2017
comment
Спасибо, что добавили это, это отлично работает в моем любимом проекте, который я реализую в своем проекте продукта! - person Karoly; 06.01.2017

сделай это

recycleView.smoothScrollToPosition(80);

позже, а не внутри метода onCreate, либо используйте отложенный поток, либо другой метод переопределения.

пример:

     new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
              recycleView.smoothScrollToPosition(80);
        }
     }, 150);
person yanivtwin    schedule 21.12.2016
comment
Спасибо, приятель, но я уже пробовал это добавление в onPostResume. Ничего не случилось :( - person Karoly; 21.12.2016
comment
попробуйте с примером, он должен работать, сохраните его в oncreate и поместите туда пример - person yanivtwin; 21.12.2016

Дайте некоторую фиксированную высоту вместо match_parent или wrap_content для recyclerview. Потому что, когда recyclerview внутри прокрутки, его высота будет равна общей высоте его элементов, поэтому это отключит прокрутку recyclerview и включит только вложенный прокрутку.

person sohan shetty    schedule 21.12.2016
comment
Это имело смысл, теперь scrollingToPosition работает нормально, но я не могу прокрутить RW - person Karoly; 21.12.2016
comment
извините, я не понял ваш вопрос - person sohan shetty; 21.12.2016
comment
удалите эту строку из своего кода recycleView.setNestedScrollingEnabled(false); - person sohan shetty; 21.12.2016
comment
Я сделал это, но ничего не изменилось: прокрутка recycleview теперь работает очень хорошо, но когда я прокручиваю ее, изображение выше не сворачивается, оно остается статичным. - person Karoly; 21.12.2016
comment
Затем используйте макет координатора в качестве родительского макета. - person sohan shetty; 22.12.2016
comment
Когда вы используете recyclerview в вложенномScrollView, на самом деле это уже не recyclerView! это scrollView, и его высота заполнена - person hamid; 15.07.2021