如何通过编程实现HorizontalScrollView的滚动

22
我有一个 HorizontalScrollView,其中包含一个RelativeLayout。在XML中该布局为空,在onCreate中从Java代码中填充。
我希望这个滚动视图最初位于RelativeLayout的中间位置,而RelativeLayout比屏幕要大得多。
我尝试了mHorizScrollView.scrollTo(offsetX, 0);,但它不起作用。我不知道哪里出了问题。
我可以发布代码,但那并不是很重要。重要的是一切都是通过程序完成的(必须如此),并且必须通过编程方式设置HorizontalScrollView的初始位置。
感谢阅读。如果您需要更多详细信息或者不够清楚,请告诉我。
9个回答

30

我发现如果你扩展HorizontalScrollView并覆盖onLayout,在调用onLayout的super之后,就可以干净地滚动:

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);
    }
}

编辑: 要滚动到开头结尾,您可以使用以下方法:

this.fullScroll(HorizontalScrollView.FOCUS_RIGHT/FOCUS_LEFT);

替代

this.scrollTo(wherever, 0);

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);
    }

我想在水平滚动视图中滚动到一个视图。这段代码对我有效。我使用了hsv.post()而不是hsv.postDelayed() - Minh Nguyen
这也可以使滚动更加平滑: hsv.arrowScroll(HorizontalScrollView.FOCUS_LEFT/RIGHT); - CanCoder

16

为了测试是否是时序问题(我认为是),不要在onStart中调用scrollTo(),而是使用一个调用scrollTo的Runnable来调用postDelayed(),延迟30左右。


1
这证实了这确实是一个时间问题。有没有比postDelayed Runnable更简洁的方法? - Benoit Duffez
1
你可以尝试继承HorizontalScrollView类,添加一个指示滚动是否必要的字段,然后在下一次onLayout()调用结束时执行滚动(或者可能是在computeScroll()或其他方法中)。不过我认为使用postDelayed更加简单。 - Ted Hopp
我发现了一件有趣的事情,我认为使用“scroll.smoothScrollTo”需要比使用“scroll.scrollTo”更多的延迟。 - neteinstein
我认为使用延迟作为解决方案需要非常小心,因为你无法确定在你的设备/模拟器上设置的延迟是否适用于其他每个设备的性能。我认为更好、更干净的解决方案是扩展HorizontalScrollView,就像HaIR的答案中所示。 - Muzikant
@Muzikant - 我认为HaIR的答案比我的更好。 - Ted Hopp

9
更好的方法是使用ViewTreeObserver来观察布局。
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
      }
    });
  }
}

谢谢您的帮助,它对我有用,但我有一个问题,它需要比postdelayed(new Runnable() ....)更多的处理吗? - MGD
1
如果你考虑运行时开销,那么答案是肯定的,因为你要添加一个新的监听器,然后再移除它。但这个开销非常小,几乎可以忽略不计。而且这种方法的健壮性是值得的。 - Vikram Bodicherla
现在我正在使用ViewTreeObserver,它对我很有效,请告诉我它是否不够高效或有什么问题吗? - MGD
@MGD 并不完全是这样。你只是在附加一个监听器,然后将其删除。因此它同样高效,更重要的是,非常健壮。 - Vikram Bodicherla
谢谢你的评论,Vikram。但我遇到了一点问题,我已经在http://stackoverflow.com/questions/9414035/horizontal-scrollview-full-scroll-focus-right-with-viewtreeobserver发布了这个问题,请考虑一下是否能帮忙 :) - MGD

1
使用view.post方法,在其run方法中编写scrollTo(x,y)。这是根据Android API文档在post方法中使视图失效的方法。

我刚刚用.post替换了我的postDelayed,现在它像魔法般地运作。与监听器一样强大,因为它是在视图附加之后触发的。 我使用它来访问视图的尺寸,因为这些尺寸只有在视图已经附加到屏幕并测量过之后才可用。 - slott

1
问题在于视图尚未确定大小。解决方法是实现ScrollView的onSizeChanged方法,向活动中的Handler发送消息,以通知活动视图已确定大小并且可以滚动。

这看起来非常不错,但它不起作用。我创建了一个扩展HorizontalScrollView的类,并实现了onSizeChanged。当调用此回调时,它会调用活动中执行滚动的方法。 然而,滚动不起作用。只有在延迟后才能正常工作。 - Benoit Duffez

0
使用 Kotlin 扩展函数。
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()
        }
    }
})

}

使用方法:

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)
    }

0

onSizeChanged函数应该可以工作。你没有理解的是,onSizeChanged应该向你的活动处理程序发送消息,而不是任何回调函数,因为处理程序将在UI线程中执行代码。你不能在外部进行UI操作。看一下handlers。


1
onSizeChanged必须在UI线程中调用,因为当我从onSizeChanged调用scrollTo时,它没有抱怨。 - Benoit Duffez

0
scrollTo函数应该可以正常工作。HSV的layout_width是多少,里面的视图呢?

1
我认为这是一个时序问题。HSV中的视图是在运行时(从onCreate)添加的,而HSV的layout_width为fill_parent。mHSV.scrollTo()是从onStart调用的。 - Benoit Duffez

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接