Horizontal ScrollView inside Vertical RecyclerView inside Horizontal RecyclerView scrolling behavior











up vote
0
down vote

favorite
1












I have an Activity below:



<RecyclerView> // with horizontal LinearLayoutManager and PagerSnapHelper
<RecyclerView> // with vertical LinearLayoutManager
<RecyclcerView /> // with horizontal LinearLayoutManager
<HorizontalScrollView />
// ... and there are other normal list items
</RecyclerView>
<RecyclerView> // another RV with vertical LinearLayoutManager
</RecyclerView>
</RecyclerView>


The problem is when I scroll horizontally, the inner horizontal RecyclerView and HorizontalScrollView doesn't scroll as expected.



Why I have to do this:



I have several ListFragments, each of which takes data from different data sources, but has same type of list items. I don't want a ViewPager, which either hold all pages immediately after ViewPager is created(which may cause OutOfMemoryError), or reload data from network after user scrolling to other pages and scroll back. Further more, with nested RV, pages can share one same RecyclerViewPool, which helps to decrease total count of item views in all pages.



What I have done in the outer horizontal RV:



private static final int INVALID_POINTER = -1;
private int mActivePointerId = INVALID_POINTER;
private float lastX;
private GestureDetector mVerticalScrollGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return Math.abs(distanceX) < Math.abs(distanceY);
}
});
private GestureDetector mHorizontalScrollGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return Math.abs(distanceX) > Math.abs(distanceY);
}
});

@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
if (mVerticalScrollGestureDetector.onTouchEvent(e) || !mAllowPageScroll) {
return false;
}
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = e.getPointerId(0);
break;
case MotionEvent.ACTION_MOVE:
int pointerIndex = e.findPointerIndex(mActivePointerId);
float x = e.getX(pointerIndex);
float dx = x - lastX;
lastX = x;
if (childCanScroll(this, false, (int) dx, (int) x)) {
Log.d("ScrollX", "not intercept");
return false;
}
Log.d("ScrollX", "maybe intercept");
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mActivePointerId = INVALID_POINTER;
break;
}
return super.onInterceptTouchEvent(e);
}

private boolean childCanScroll(View v, boolean considerSelf, int dx, int x) {
if (v instanceof ViewGroup) {
ViewGroup group = (ViewGroup) v;
int scrollX = v.getScrollX();
for (int i = group.getChildCount() - 1; i >= 0; i--) {
View child = group.getChildAt(i);
if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight()
&& childCanScroll(child, true, dx, x + scrollX - child.getLeft())) {
return true;
}
}
}
return considerSelf && v.canScrollHorizontally(-dx);
}


Now it works with inner horizontal RV with occasionally not scrolling, and doesn't work with HorizontalScrollView at all.



How can I achieve the goal to make inner horizontally-scrollable view to scroll first?










share|improve this question




























    up vote
    0
    down vote

    favorite
    1












    I have an Activity below:



    <RecyclerView> // with horizontal LinearLayoutManager and PagerSnapHelper
    <RecyclerView> // with vertical LinearLayoutManager
    <RecyclcerView /> // with horizontal LinearLayoutManager
    <HorizontalScrollView />
    // ... and there are other normal list items
    </RecyclerView>
    <RecyclerView> // another RV with vertical LinearLayoutManager
    </RecyclerView>
    </RecyclerView>


    The problem is when I scroll horizontally, the inner horizontal RecyclerView and HorizontalScrollView doesn't scroll as expected.



    Why I have to do this:



    I have several ListFragments, each of which takes data from different data sources, but has same type of list items. I don't want a ViewPager, which either hold all pages immediately after ViewPager is created(which may cause OutOfMemoryError), or reload data from network after user scrolling to other pages and scroll back. Further more, with nested RV, pages can share one same RecyclerViewPool, which helps to decrease total count of item views in all pages.



    What I have done in the outer horizontal RV:



    private static final int INVALID_POINTER = -1;
    private int mActivePointerId = INVALID_POINTER;
    private float lastX;
    private GestureDetector mVerticalScrollGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
    return Math.abs(distanceX) < Math.abs(distanceY);
    }
    });
    private GestureDetector mHorizontalScrollGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
    return Math.abs(distanceX) > Math.abs(distanceY);
    }
    });

    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
    if (mVerticalScrollGestureDetector.onTouchEvent(e) || !mAllowPageScroll) {
    return false;
    }
    switch (e.getAction()) {
    case MotionEvent.ACTION_DOWN:
    mActivePointerId = e.getPointerId(0);
    break;
    case MotionEvent.ACTION_MOVE:
    int pointerIndex = e.findPointerIndex(mActivePointerId);
    float x = e.getX(pointerIndex);
    float dx = x - lastX;
    lastX = x;
    if (childCanScroll(this, false, (int) dx, (int) x)) {
    Log.d("ScrollX", "not intercept");
    return false;
    }
    Log.d("ScrollX", "maybe intercept");
    break;
    case MotionEvent.ACTION_UP:
    case MotionEvent.ACTION_CANCEL:
    mActivePointerId = INVALID_POINTER;
    break;
    }
    return super.onInterceptTouchEvent(e);
    }

    private boolean childCanScroll(View v, boolean considerSelf, int dx, int x) {
    if (v instanceof ViewGroup) {
    ViewGroup group = (ViewGroup) v;
    int scrollX = v.getScrollX();
    for (int i = group.getChildCount() - 1; i >= 0; i--) {
    View child = group.getChildAt(i);
    if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight()
    && childCanScroll(child, true, dx, x + scrollX - child.getLeft())) {
    return true;
    }
    }
    }
    return considerSelf && v.canScrollHorizontally(-dx);
    }


    Now it works with inner horizontal RV with occasionally not scrolling, and doesn't work with HorizontalScrollView at all.



    How can I achieve the goal to make inner horizontally-scrollable view to scroll first?










    share|improve this question


























      up vote
      0
      down vote

      favorite
      1









      up vote
      0
      down vote

      favorite
      1






      1





      I have an Activity below:



      <RecyclerView> // with horizontal LinearLayoutManager and PagerSnapHelper
      <RecyclerView> // with vertical LinearLayoutManager
      <RecyclcerView /> // with horizontal LinearLayoutManager
      <HorizontalScrollView />
      // ... and there are other normal list items
      </RecyclerView>
      <RecyclerView> // another RV with vertical LinearLayoutManager
      </RecyclerView>
      </RecyclerView>


      The problem is when I scroll horizontally, the inner horizontal RecyclerView and HorizontalScrollView doesn't scroll as expected.



      Why I have to do this:



      I have several ListFragments, each of which takes data from different data sources, but has same type of list items. I don't want a ViewPager, which either hold all pages immediately after ViewPager is created(which may cause OutOfMemoryError), or reload data from network after user scrolling to other pages and scroll back. Further more, with nested RV, pages can share one same RecyclerViewPool, which helps to decrease total count of item views in all pages.



      What I have done in the outer horizontal RV:



      private static final int INVALID_POINTER = -1;
      private int mActivePointerId = INVALID_POINTER;
      private float lastX;
      private GestureDetector mVerticalScrollGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
      @Override
      public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
      return Math.abs(distanceX) < Math.abs(distanceY);
      }
      });
      private GestureDetector mHorizontalScrollGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
      @Override
      public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
      return Math.abs(distanceX) > Math.abs(distanceY);
      }
      });

      @Override
      public boolean onInterceptTouchEvent(MotionEvent e) {
      if (mVerticalScrollGestureDetector.onTouchEvent(e) || !mAllowPageScroll) {
      return false;
      }
      switch (e.getAction()) {
      case MotionEvent.ACTION_DOWN:
      mActivePointerId = e.getPointerId(0);
      break;
      case MotionEvent.ACTION_MOVE:
      int pointerIndex = e.findPointerIndex(mActivePointerId);
      float x = e.getX(pointerIndex);
      float dx = x - lastX;
      lastX = x;
      if (childCanScroll(this, false, (int) dx, (int) x)) {
      Log.d("ScrollX", "not intercept");
      return false;
      }
      Log.d("ScrollX", "maybe intercept");
      break;
      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL:
      mActivePointerId = INVALID_POINTER;
      break;
      }
      return super.onInterceptTouchEvent(e);
      }

      private boolean childCanScroll(View v, boolean considerSelf, int dx, int x) {
      if (v instanceof ViewGroup) {
      ViewGroup group = (ViewGroup) v;
      int scrollX = v.getScrollX();
      for (int i = group.getChildCount() - 1; i >= 0; i--) {
      View child = group.getChildAt(i);
      if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight()
      && childCanScroll(child, true, dx, x + scrollX - child.getLeft())) {
      return true;
      }
      }
      }
      return considerSelf && v.canScrollHorizontally(-dx);
      }


      Now it works with inner horizontal RV with occasionally not scrolling, and doesn't work with HorizontalScrollView at all.



      How can I achieve the goal to make inner horizontally-scrollable view to scroll first?










      share|improve this question















      I have an Activity below:



      <RecyclerView> // with horizontal LinearLayoutManager and PagerSnapHelper
      <RecyclerView> // with vertical LinearLayoutManager
      <RecyclcerView /> // with horizontal LinearLayoutManager
      <HorizontalScrollView />
      // ... and there are other normal list items
      </RecyclerView>
      <RecyclerView> // another RV with vertical LinearLayoutManager
      </RecyclerView>
      </RecyclerView>


      The problem is when I scroll horizontally, the inner horizontal RecyclerView and HorizontalScrollView doesn't scroll as expected.



      Why I have to do this:



      I have several ListFragments, each of which takes data from different data sources, but has same type of list items. I don't want a ViewPager, which either hold all pages immediately after ViewPager is created(which may cause OutOfMemoryError), or reload data from network after user scrolling to other pages and scroll back. Further more, with nested RV, pages can share one same RecyclerViewPool, which helps to decrease total count of item views in all pages.



      What I have done in the outer horizontal RV:



      private static final int INVALID_POINTER = -1;
      private int mActivePointerId = INVALID_POINTER;
      private float lastX;
      private GestureDetector mVerticalScrollGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
      @Override
      public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
      return Math.abs(distanceX) < Math.abs(distanceY);
      }
      });
      private GestureDetector mHorizontalScrollGestureDetector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
      @Override
      public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
      return Math.abs(distanceX) > Math.abs(distanceY);
      }
      });

      @Override
      public boolean onInterceptTouchEvent(MotionEvent e) {
      if (mVerticalScrollGestureDetector.onTouchEvent(e) || !mAllowPageScroll) {
      return false;
      }
      switch (e.getAction()) {
      case MotionEvent.ACTION_DOWN:
      mActivePointerId = e.getPointerId(0);
      break;
      case MotionEvent.ACTION_MOVE:
      int pointerIndex = e.findPointerIndex(mActivePointerId);
      float x = e.getX(pointerIndex);
      float dx = x - lastX;
      lastX = x;
      if (childCanScroll(this, false, (int) dx, (int) x)) {
      Log.d("ScrollX", "not intercept");
      return false;
      }
      Log.d("ScrollX", "maybe intercept");
      break;
      case MotionEvent.ACTION_UP:
      case MotionEvent.ACTION_CANCEL:
      mActivePointerId = INVALID_POINTER;
      break;
      }
      return super.onInterceptTouchEvent(e);
      }

      private boolean childCanScroll(View v, boolean considerSelf, int dx, int x) {
      if (v instanceof ViewGroup) {
      ViewGroup group = (ViewGroup) v;
      int scrollX = v.getScrollX();
      for (int i = group.getChildCount() - 1; i >= 0; i--) {
      View child = group.getChildAt(i);
      if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight()
      && childCanScroll(child, true, dx, x + scrollX - child.getLeft())) {
      return true;
      }
      }
      }
      return considerSelf && v.canScrollHorizontally(-dx);
      }


      Now it works with inner horizontal RV with occasionally not scrolling, and doesn't work with HorizontalScrollView at all.



      How can I achieve the goal to make inner horizontally-scrollable view to scroll first?







      android-recyclerview android-nestedscrollview






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 7 at 9:12

























      asked Nov 7 at 5:01









      HJWAJ

      435




      435
























          1 Answer
          1






          active

          oldest

          votes

















          up vote
          0
          down vote



          accepted










          There are two mistakes I make.




          1. In case of ACTION_DOWN I need to record the lastX variable as well as in case of ACTION_MOVE, so that first move of dx is a proper value. This explains why the childCanScroll method doesn't work on things like ScrollView(as RecyclerView's getScrollX() always return 0).


          2. In method childCanScroll, the inner if statement should not only consider the x coordinate, but also the y coordinate. If y is not considered, when I scroll outside inner horizontal-scrollable-views, the method also return true, that make things not working properly.



          After I fix these two mistakes all things are right.






          share|improve this answer





















            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














             

            draft saved


            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53183810%2fhorizontal-scrollview-inside-vertical-recyclerview-inside-horizontal-recyclervie%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            0
            down vote



            accepted










            There are two mistakes I make.




            1. In case of ACTION_DOWN I need to record the lastX variable as well as in case of ACTION_MOVE, so that first move of dx is a proper value. This explains why the childCanScroll method doesn't work on things like ScrollView(as RecyclerView's getScrollX() always return 0).


            2. In method childCanScroll, the inner if statement should not only consider the x coordinate, but also the y coordinate. If y is not considered, when I scroll outside inner horizontal-scrollable-views, the method also return true, that make things not working properly.



            After I fix these two mistakes all things are right.






            share|improve this answer

























              up vote
              0
              down vote



              accepted










              There are two mistakes I make.




              1. In case of ACTION_DOWN I need to record the lastX variable as well as in case of ACTION_MOVE, so that first move of dx is a proper value. This explains why the childCanScroll method doesn't work on things like ScrollView(as RecyclerView's getScrollX() always return 0).


              2. In method childCanScroll, the inner if statement should not only consider the x coordinate, but also the y coordinate. If y is not considered, when I scroll outside inner horizontal-scrollable-views, the method also return true, that make things not working properly.



              After I fix these two mistakes all things are right.






              share|improve this answer























                up vote
                0
                down vote



                accepted







                up vote
                0
                down vote



                accepted






                There are two mistakes I make.




                1. In case of ACTION_DOWN I need to record the lastX variable as well as in case of ACTION_MOVE, so that first move of dx is a proper value. This explains why the childCanScroll method doesn't work on things like ScrollView(as RecyclerView's getScrollX() always return 0).


                2. In method childCanScroll, the inner if statement should not only consider the x coordinate, but also the y coordinate. If y is not considered, when I scroll outside inner horizontal-scrollable-views, the method also return true, that make things not working properly.



                After I fix these two mistakes all things are right.






                share|improve this answer












                There are two mistakes I make.




                1. In case of ACTION_DOWN I need to record the lastX variable as well as in case of ACTION_MOVE, so that first move of dx is a proper value. This explains why the childCanScroll method doesn't work on things like ScrollView(as RecyclerView's getScrollX() always return 0).


                2. In method childCanScroll, the inner if statement should not only consider the x coordinate, but also the y coordinate. If y is not considered, when I scroll outside inner horizontal-scrollable-views, the method also return true, that make things not working properly.



                After I fix these two mistakes all things are right.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered 2 days ago









                HJWAJ

                435




                435






























                     

                    draft saved


                    draft discarded



















































                     


                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53183810%2fhorizontal-scrollview-inside-vertical-recyclerview-inside-horizontal-recyclervie%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    這個網誌中的熱門文章

                    Xamarin.form Move up view when keyboard appear

                    Post-Redirect-Get with Spring WebFlux and Thymeleaf

                    Anylogic : not able to use stopDelay()