Search code examples
androidright-to-left

2D Scrolling, supporting RTL layout


Recently, we have started adding support for Arabic - which needs RTL layouts. One of our screens uses a 2D scroller, which I found here:

https://github.com/mukulraw/rekhta/blob/master/app/src/main/java/com/example/mukul/rekhta/TwoDScrollView.java

But, when we switch to RTL there are two problems:

  • The childview is cut off
  • Initially doesn't want to scroll to the left, but allows scrolling to the right (away from the child view).

I've tried looking at the source code for Horizontal scrollview - which handles rtl correctly. So far, I only saw two references to rtl in the code: during onLayout(). When I try something similar though, my child view just disappears.

So far I could find no other solutions supporting both 2d scrolling and rtl. So, I'm hoping to resolve it here for me and for some future soul that needs something similar.


Solution

  • Ok, so put this together from a whole bunch of different solutions:

    1. Create two custom components, one extending ScrollView and the other extending Horizontal Scroll View:

      public class VScroll extends ScrollView {
      
       public VScroll(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
       } 
      
       public VScroll(Context context, AttributeSet attrs) {
        super(context, attrs);
       }
      
       public VScroll(Context context) {
         super(context);
       }
      
       @Override
       public boolean onTouchEvent(MotionEvent ev) {
         return false;
       } 
      }
      

      AND

      public class HScroll extends HorizontalScrollView {
      
       public HScroll(Context context, AttributeSet attrs, int defStyle) {
           super(context, attrs, defStyle);
       }
      
       public HScroll(Context context, AttributeSet attrs) {
           super(context, attrs);
       }
      
       public HScroll(Context context) {
           super(context);
       }
      
       @Override
       public boolean onTouchEvent(MotionEvent ev) {
           return false;
       }
      }
      
    2. in the activity where you need the '2d scroller', use the following: in OnCreate():

          vScroll = findViewById(R.id.vScroll);
          hScroll = findViewById(R.id.hScroll);
      

    and then

    private VelocityTracker mVelocityTracker;
    private float mx, my;
    private float curX, curY;
    private boolean started;
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
    
      if (mVelocityTracker == null) {
         mVelocityTracker = VelocityTracker.obtain();
      }
      mVelocityTracker.addMovement(event);
    
      curX = event.getX();
      curY = event.getY();
      int dx = (int) (mx - curX);
      int dy = (int) (my - curY);
      switch (event.getAction()) {
         case MotionEvent.ACTION_MOVE:
            if (started) {
                vScroll.smoothScrollBy(0, dy);
                hScroll.smoothScrollBy(dx, 0);
            } else {
                started = true;
            }
            mx = curX;
            my = curY;
            break;
          case MotionEvent.ACTION_UP:
            final VelocityTracker velocityTracker = mVelocityTracker;
            velocityTracker.computeCurrentVelocity(1000);
            int initialXVelocity = (int) velocityTracker.getXVelocity();
            int initialYVelocity = (int) velocityTracker.getYVelocity();
            vScroll.fling(-initialYVelocity);
            hScroll.fling(-initialXVelocity);
    
            if (mVelocityTracker != null) {
              mVelocityTracker.recycle();
              mVelocityTracker = null;
            }
            break;
        }
        return true;
    }
    

    This allows for bi-directional scrolling (yes, even diagonal) as well as some velocity to allow for the 'fling' effect. Best of all, it works nicely with RTL and LTR layouts! Hope this is useful for someone else out there too....

    EDIT: Forgot to add the XML part:

    <pack.customcomponents.VScroll android:layout_height="fill_parent"
        android:layout_width="fill_parent" android:id="@+id/vScroll">
        <pack.customcomponents.HScroll android:id="@+id/hScroll"
            android:layout_width="fill_parent" android:layout_height="fill_parent">
            <RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@android:color/white"
                android:id="@+id/view_to_scroll"/>
        </pack.customcomponents.HScroll>
    </pack.customcomponents.VScroll>