How to programmatically scroll an HorizontalScrollView
Asked Answered
J

9

23

I have an HorizontalScrollView which contains a RelativeLayout. This layout is empty in the XML, and is populated from java in the onCreate.

I would like this scroll view to be initially somewhere in the middle of the RelativeLayout, which is way larger than the screen.

I tried mHorizScrollView.scrollTo(offsetX, 0); which doesn't work. I don't know what's wrong with this.

I could post the code, but it is not really relevant. What matters is that everything is done programatically (has to :s), and that the initial position of the HorizontalScrollView has to be set programmatically.

Thanks for reading. Please tell me if you need more details or if this is not clear enough.

Janettejaneva answered 25/1, 2011 at 23:23 Comment(0)
P
30

I found that if you extend the HorizontalScrollView and override the onLayout, you can cleanly scroll, after the super call to onLayout:

MyScrollView extends HorizontalScrollView {
    protected void onLayout (boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        this.scrollTo(wherever, 0);
    }
}

EDIT: For scrolling to start or end you can use:

this.fullScroll(HorizontalScrollView.FOCUS_RIGHT/FOCUS_LEFT);

instead of

this.scrollTo(wherever, 0);
Physiological answered 29/2, 2012 at 22:55 Comment(0)
M
26
public void autoSmoothScroll() {

        final HorizontalScrollView hsv = (HorizontalScrollView) view.findViewById(R.id.horiscroll);
        hsv.postDelayed(new Runnable() {
            @Override
            public void run() {
                //hsv.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
                hsv.smoothScrollBy(500, 0);
            }
        },100);
    }
Monia answered 20/6, 2013 at 21:13 Comment(2)
I want to scroll to a view in horizontal scroll view. This code works for me. I used hsv.post() instead of hsv.postDelayed()Gramineous
this is also good to scroll smoothly hsv.arrowScroll(HorizontalScrollView.FOCUS_LEFT/RIGHT);Latishalatitude
U
16

To test whether it's a timing issue (which I think it is), instead of calling scrollTo() in onStart, call postDelayed() with a Runnable that calls scrollTo, with a delay of 30 or so.

Unmoved answered 26/1, 2011 at 1:33 Comment(5)
This confirmed that it is, indeed, a timing issue. Is there a cleaner way to do this, than a postDelayed Runnable ?Janettejaneva
You could probably subclass HorizontalScrollView, add a field that indicates that scrolling is necessary, and then do the scrolling at the end of the next call to onLayout() (or perhaps on computeScroll() or some other method). I think using postDelayed is just easier.Unmoved
I found something curious, i think with 'scroll.smoothScrollTo' you need more delay then with 'scroll.scrollTo'.Keratoplasty
I would be very careful using delays as a solution as you can't say if the delay set on your device/emulator will fit the performance of every other device. I think the better and cleaner solution is extending HorizontalScrollView as you can see in HaIR's answerTrichiasis
@Trichiasis - I think HaIR's answer offers a better solution than my own.Unmoved
P
9

A better approach would be using the ViewTreeObserver to observe layouts.

View interestedInView;
onCreate(){
  //Code

  //Observe for a layout change
  ViewTreeObserver viewTreeObserver = interestedInView.getViewTreeObserver();
  if (viewTreeObserver.isAlive()) {
    viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
      @Override
      public void onGlobalLayout() {
        //REMOVE THE VIEWTREE OBSERVER
        //interestedInView is ready for size and position queries because it has been laid out
      }
    });
  }
}
Performance answered 15/8, 2011 at 3:48 Comment(5)
Thanks for help its working for me, but I have question does it require more processing then postdelayed(new Runnable() ....)Trefoil
Well, if you are taking run-time overhead, the answer is yes, because you are adding a new listener and removing it then. But it is so very minimal that it wont count. And it's worth the robustness of the approach.Performance
Now I am using ViewTreeObserver and its working for me, please tell me if its less efficient or what ???Trefoil
@Trefoil Not really. You are only attaching a listener and then removing it. So its equally efficient and more importantly, very robust.Performance
thanks for the comment Vikram yet I am having lil problem that I have posted #9414535 please consider this question if you could help :)Trefoil
I
1

The problem is that the view doesn't have a size yet. The Clean way to do this is to implement the onSizeChanged of the ScrollView to send a message to a Handler in your activity to in order to notify the activity that the view has a size and that scrolling is possible.

Illustration answered 10/5, 2011 at 20:37 Comment(1)
This looked really good but it doesn't work. I created a class that extends HorizontalScrollView, and that implements onSizeChanged. When this callback is called, it calls a method in the activity that does the scrolling. However, the scroll is not working. It only works if it is post delayed.Janettejaneva
R
1

Use view.post and write scrollTo(x,y) inside run method. This the way to invalidate view in post method as per android API document.

Robespierre answered 25/5, 2011 at 4:31 Comment(1)
I just replaced my postDelayed with a .post and it works like a charm. Just as robust as a listener as it is triggered after the view has bee attached. I use this to access the dimensions of a view as this is only available after it has been attached to the screen an measured.Weatherby
M
1

Use Kotlin extension function.

inline fun <T : View> T.afterMeasured(crossinline f: T.() -> Unit) {
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
    override fun onGlobalLayout() {
        if (measuredWidth > 0 && measuredHeight > 0) {
            viewTreeObserver.removeOnGlobalLayoutListener(this)
            f()
        }
    }
})

}

Usage:

mHorizontalScrollView.afterMeasured {
        var scrollTo = 0
        val count = (layout.svList.getChildAt(0) as LinearLayout)
            .childCount

        if(index < count){
            val child = (layout.svList.getChildAt(0) as LinearLayout)
                .getChildAt(index)

            scrollTo = child.width

        }

        Log.d(TAG, "scrollToComment: $scrollTo")

        mHorizontalScrollView.scrollTo(scrollTo, 0)
    }
Mikkanen answered 10/6, 2021 at 7:27 Comment(0)
M
0

The scrollTo function should work. What is the layout_width of the HSV, and the view inside of it?

Melitta answered 25/1, 2011 at 23:28 Comment(1)
I think this is a timing issue. The views inside the HSV are added at runtime (from onCreate) and the layout_width of the HSV is fill_parent. The mHSV.scrollTo() is called from onStart.Janettejaneva
K
0

The onSizeChanged function should work. What you fail to understand is that the onSizeChanged should send a message to a Handler of your activity and not any callback function, because a handler will execute code in the UI thread. You cannot do UI operations outside it. Have a look at handlers.

Knp answered 13/5, 2011 at 6:6 Comment(1)
onSizeChanged must be called in the UI thread because when I called the scrollTo from onSizeChanged, it did not complain.Janettejaneva

© 2022 - 2024 — McMap. All rights reserved.