Programing

Android 레이아웃 : 스크롤 동작이있는 Viewpager 내부의 Vertical Recyclerview 내부의 Horizontal Recyclerview

crosscheck 2020. 12. 8. 07:44
반응형

Android 레이아웃 : 스크롤 동작이있는 Viewpager 내부의 Vertical Recyclerview 내부의 Horizontal Recyclerview


이것은 아래 매핑 된 모든 요소를 ​​사용하여 빌드하려는 앱입니다.

채소 앱

그러나 모든 것이 작동하지만 내부 수평 recyclerview가 수직 스크롤을 캡처하지 않기를 바랍니다. 모든 수직 스크롤은 수평 스크롤이 아닌 외부 수직 recyclerview쪽으로 이동해야합니다. 그래야 수직 스크롤을 사용하면 도구 모음이 scrollFlag에 따라보기에서 벗어날 수 있습니다.

recyclerview의 "StrawBerry Plant"부분에 손가락을 놓고 위로 스크롤하면 도구 모음이 스크롤됩니다.

딸기 식물

가로 스크롤 뷰에 손가락을 놓고 위로 스크롤하면 도구 모음이 전혀 스크롤되지 않습니다.

다음은 지금까지 내 xml 레이아웃 코드입니다.

활동 XML 레이아웃 :

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    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:id="@+id/fragment_container"
    android:clipChildren="false">

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

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appBarLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:minHeight="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_scrollFlags="scroll|enterAlways">

        </android.support.v7.widget.Toolbar>

        <android.support.design.widget.TabLayout
            android:id="@+id/sliding_tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            style="@style/CustomTabLayout"
            />

    </android.support.design.widget.AppBarLayout>
    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        />

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

</FrameLayout>

"과일"조각 (조각 코드입니다 - 조각이 위의 그림에 표시되어) XML 레이아웃 :

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

    <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/progressBar"
        android:visibility="gone"
        android:layout_centerInParent="true"
        android:indeterminate="true"/>

<!--    <android.support.v7.widget.RecyclerView-->
    <com.example.simon.customshapes.VerticallyScrollRecyclerView
        android:id="@+id/main_recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</RelativeLayout>

뷰 그룹에서 터치 이벤트를 처리하는 Google 예제를 따르는 VerticallyScrollRecyclerView라는 사용자 정의 클래스를 사용했습니다. 그 목표는 모든 수직 스크롤 이벤트를 가로 채서 소비하여 툴바를 안팎으로 스크롤하도록하는 것입니다. http://developer.android.com/training/gestures/viewgroup.html

VerticallyScrollRecyclerView 의 코드 는 다음과 같습니다.

public class VerticallyScrollRecyclerView extends RecyclerView {

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

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

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

    ViewConfiguration vc = ViewConfiguration.get(this.getContext());
    private int mTouchSlop = vc.getScaledTouchSlop();
    private boolean mIsScrolling;
    private float startY;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);
        // Always handle the case of the touch gesture being complete.
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            // Release the scroll.
            mIsScrolling = false;
            startY = ev.getY();
            return super.onInterceptTouchEvent(ev); // Do not intercept touch event, let the child handle it
        }
        switch (action) {
            case MotionEvent.ACTION_MOVE: {
                Log.e("VRecView", "its moving");
                if (mIsScrolling) {
                    // We're currently scrolling, so yes, intercept the
                    // touch event!
                    return true;
                }
                // If the user has dragged her finger horizontally more than
                // the touch slop, start the scroll
                // left as an exercise for the reader
                final float yDiff = calculateDistanceY(ev.getY());
                Log.e("yDiff ", ""+yDiff);
                // Touch slop should be calculated using ViewConfiguration
                // constants.
                if (Math.abs(yDiff) > 5) {
                    // Start scrolling!
                    Log.e("Scroll", "we are scrolling vertically");
                    mIsScrolling = true;
                    return true;
                }
                break;
            }
        }
        return super.onInterceptTouchEvent(ev);
    }


    private float calculateDistanceY(float endY) {
        return startY - endY;
    }

}

"즐겨 찾기"레이아웃 수직 recyclerview 내 recyclerview입니다 :

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="@color/white"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Favourite"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="16dp"
        android:id="@+id/header_fav"/>

    <android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_below="@+id/header_fav"
        android:id="@+id/recyclerview_fav">
    </android.support.v7.widget.RecyclerView>

</RelativeLayout>

이것은 한동안 나를 괴롭 혔고 해결책을 찾지 못했습니다. 누구든지이 문제를 해결하는 방법을 알고 있습니까?

5 points to Griffindor for the correct answer and of course, reputation points on SO.


Tested solution:

All you need is to call mInnerRecycler.setNestedScrollingEnabled(false); on your inner RecyclerViews


Explanation:

RecyclerView has support for nested scrolling introduced in API 21 through implementing the NestedScrollingChild interface. This is a valuable feature when you have a scrolling view inside another one that scrolls in the same direction and you want to scroll the inner View only when focused.

In any case, RecyclerView by default calls RecyclerView.setNestedScrollingEnabled(true); on itself when initializing. Now, back to the problem, since both of your RecyclerViews are within the same ViewPager that has the AppBarBehavior, the CoordinateLayout has to decide which scroll to respond to when you scroll from your inner RecyclerView; when your inner RecyclerView's nested scrolling is enabled, it gets the scrolling focus and the CoordinateLayout will choose to respond to its scrolling over the outer RecyclerView's scrolling. The thing is that, since your inner RecyclerViews don't scroll vertically, there is no vertical scroll change (from the CoordinateLayout's point of view), and if there is no change, the AppBarLayout doesn't change either.

In your case, because your inner RecyclerViews are scrolling in a different direction, you can disable it, thus causing the CoordinateLayout to disregard its scrolling and respond to the outer RecyclerView's scrolling.


Notice:

The xml attribute android:nestedScrollingEnabled="boolean" is not intended for use with the RecyclerView, and an attempt to use android:nestedScrollingEnabled="false" will result in a java.lang.NullPointerException so, at least for now, you will have to do it in code.


Tested solution, use a custom NestedScrollView().

Code:

public class CustomNestedScrollView extends NestedScrollView {
    public CustomNestedScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
         // Explicitly call computeScroll() to make the Scroller compute itself
            computeScroll();
        }
        return super.onInterceptTouchEvent(ev);
    }
}

I am a bit late but this will defintly work for others facing the same problem

 mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
            @Override
            public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
                int action = e.getAction();
               // Toast.makeText(getActivity(),"HERE",Toast.LENGTH_SHORT).show();
                switch (action) {
                    case MotionEvent.ACTION_POINTER_UP:
                        rv.getParent().requestDisallowInterceptTouchEvent(true);

                        break;
                }
                return false;
            }

if any one still looking , try this :

private val Y_BUFFER = 10
private var preX = 0f
private var preY = 0f

mView.rv.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
        override fun onTouchEvent(p0: RecyclerView, p1: MotionEvent) {

            }

        override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
                when (e.action) {
                    MotionEvent.ACTION_DOWN -> rv.parent.requestDisallowInterceptTouchEvent(true)
                    MotionEvent.ACTION_MOVE -> {
                        if (Math.abs(e.x - preX) > Math.abs(e.y - preY)) {
                            rv.parent.requestDisallowInterceptTouchEvent(true)
                        } else if (Math.abs(e.y - preY) > Y_BUFFER) {
                            rv.parent.requestDisallowInterceptTouchEvent(false)
                        }

                    }
                }
                preX = e.x
                preY = e.y
                return false
            }

            override fun onRequestDisallowInterceptTouchEvent(p0: Boolean) {
            }
        })

it checks if currently scrolling horizontal then don't allow parent to handel event


try

public OuterRecyclerViewAdapter(List<Item> items) {
    //Constructor stuff
    viewPool = new RecyclerView.RecycledViewPool();
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    //Create viewHolder etc
    holder.innerRecyclerView.setRecycledViewPool(viewPool);

}

inner recylerview will use the same viewpool and it'll be smoother


Google 앱과 같은 조각 안에 수평 recyclerview를 추가하는 것이 좋습니다.

참고 URL : https://stackoverflow.com/questions/33456216/android-layout-horizontal-recyclerview-inside-a-vertical-recyclerview-inside-a

반응형