HorizontalScrollView: auto-scroll to end when new Views are added?

怎甘沉沦 提交于 2019-11-26 21:58:43

I think there's a timing issue. Layout isn't done when a view is added. It is requested and done a short time later. When you call fullScroll immediately after adding the view, the width of the linearlayout hasn't had a chance to expand.

Try replacing:

s.fullScroll(HorizontalScrollView.FOCUS_RIGHT);

with:

s.postDelayed(new Runnable() {
    public void run() {
        s.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
    }
}, 100L);

The short delay should give the system enough time to settle.

P.S. It might be sufficient to simply delay the scrolling until after the current iteration of the UI loop. I have not tested this theory, but if it's right, it would be sufficient to do the following:

s.post(new Runnable() {
    public void run() {
        s.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
    }
});

I like this better because introducing an arbitrary delay seems hacky to me (even though it was my suggestion).

I agree with @monxalo and @granko87 that the better approach is with a listener instead of making an assumption that the layout will be complete if we let some arbitrary amount of time pass.

In case, like me, you don't need to use a custom HorizontalScrollView subclass you can just add an OnLayoutChangeListener to keep things simple:

mTagsScroller.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        mTagsScroller.removeOnLayoutChangeListener(this);
        mTagsScroller.fullScroll(View.FOCUS_RIGHT);
    }
});

Just another sugestion, since this question helped me a lot :).

You can put a listener when the view has finished its layout phase, and right after do the fullScroll althought you'll need to extend the class for that.

I only did this because i wanted to scroll to a section right after onCreate() to avoid that flickering from starting point to scroll point.

Something like:

public class PagerView extends HorizontalScrollView {

    private OnLayoutListener mListener;
    ///...
    private interface OnLayoutListener {
        void onLayout();
    }

    public void fullScrollOnLayout(final int direction) {
        mListener = new OnLayoutListener() {            
            @Override
            public void onLayout() {
                 fullScroll(direction)
                 mListener = null;
            }
        };
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if(mListener != null)
             mListener.onLayout();
    }
}

Use the View method smoothScrollTo

postDelayed(new Runnable() {
    public void run() {
       scrollView.smoothScrollTo(newView.getLeft(), newView.getTop());
 }
 }, 100L);

You need to put in a runnable so you give time to the layout process to position the new View

This is what I did.

//Observe for a layout change    
ViewTreeObserver viewTreeObserver = yourLayout.getViewTreeObserver();
   if (viewTreeObserver.isAlive()) {
     viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                yourHorizontalScrollView.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
            }
     });
   }

Use below code for horizontal scrollview {Scroll to right}

view.post(new Runnable() {
                @Override
                public void run() {
                    ((HorizontalScrollView) Iview
                            .findViewById(R.id.hr_scroll))
                            .fullScroll(HorizontalScrollView.FOCUS_RIGHT);
                }
            });

I think the best approach is the one posted by monxalo because you can not know how much time will it take until the layout is done. I tried it and it works perfectly. The only difference is, that I added only one row to my custom horizontal scroll view:

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    super.onLayout(changed, l, t, r, b);
    this.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
}

The child View may not be immediately added in a ViewGroup. So it is required to post a Runnable which will run after other pending tasks are completed.

So after adding View, use:

horizontalScrollView.post(new Runnable() {
    @Override
    public void run() {
        horizontalScrollView.fullScroll(View.FOCUS_RIGHT);
    }
});

This is my way for 3 ImageViews in kotlin:

var p1 = true; var p2 = false; var p3 = false
val x = -55
val handler = Handler()
handler.postDelayed(object : Runnable {
    override fun run() {

        if (p1){
            mainView.picsScrollViewID.smoothScrollTo(mainView.bigPic1ID.x.toInt() + x, 0)
            p1 = false
            p2 = true
            p3 = false
        }
        else if(p2){
            mainView.picsScrollViewID.smoothScrollTo(mainView.bigPic2ID.x.toInt() + x, 0)
            p1 = false
            p2 = false
            p3 = true
        }
        else if(p3){
            mainView.picsScrollViewID.smoothScrollTo(mainView.bigPic3ID.x.toInt() + x, 0)
            p1 = true
            p2 = false
            p3 = false
        }
        handler.postDelayed(this, 2000)
    }
}, 1400)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!