ScrollView scrollTo不起作用

7

和这个问题相同:ScrollView .scrollTo不起作用?如何在旋转后保存ScrollView的位置

我在onCreate中动态向scrollView添加了项目,所有项目都添加完成后,我尝试执行以下操作:

    // no effect
    ScrollView mainScroll = (ScrollView) findViewById(R.id.average_scroll_mainScroll);

    // no effect
    ScrollView mainScroll = (ScrollView) findViewById(R.id.average_scroll_mainScroll);
    mainScroll.post(new Runnable() {
        public void run(){
            ScrollView mainScroll = (ScrollView) findViewById(R.id.average_scroll_mainScroll);
            mainScroll.scrollTo(0, 0);
        } 
    });

    // works like a charm
    ScrollView mainScroll = (ScrollView) findViewById(R.id.average_scroll_mainScroll);
    mainScroll.postDelayed(new Runnable() {
        public void run(){
            ScrollView mainScroll = (ScrollView) findViewById(R.id.average_scroll_mainScroll);
            mainScroll.scrollTo(0, 0);
        } 
    }, 30);

我认为有类似于“DOM-ready”的事件吗?是否有任何回调函数?

1
我最好的猜测是,因为在充气后ScrollView还没有被布局或测量,所以第一种方法不起作用。通过添加延迟,您可以给视图层次结构足够的时间来初始化。我建议尝试将OnGlobalLayoutListener附加到ScrollViewViewTreeObserver上,以便在视图“准备好滚动”时得到通知。 - MH.
5个回答

20

根据David Daudelin这个问题的答案,您不需要扩展ScrollView并创建自己的ScrollView。

获取ScrollView的ViewTreeObserver,并向其添加OnGlobalLayoutListener。然后从OnGlobalLayoutListener的onGlobalLayout()方法中调用ScrollView.scrollTo(x,y)方法。代码:

 ScrollView mainScroll = (ScrollView) findViewById(R.id.average_scroll_mainScroll);

 ViewTreeObserver vto = scrollView.getViewTreeObserver();
 vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
      public void onGlobalLayout() {
           mainScroll.scrollTo(0, 0);
      }
 });

7

如果以上解决方案无效,只需添加50-100毫秒的PostDelayed间隔,因为Android可能需要一些时间重新绘制您的视图。 - Denys Lobur

1

当您对ScrollView进行一些更改时,它需要一段时间才能将布局更改复制到显示列表并通知ScrollView它确实被允许滚动到某个位置。

我通过使用自己的ScrollView扩展ScrollView,并添加了一个方法,根据需要添加OnGlobalLayoutListener(如MH建议的那样),稍后滚动到那里来使其正常工作。这比在每个需要它的情况下都应用它要自动化得多(但是您需要使用新的ScrollView)。无论如何,这是相关的代码:

public class ZScrollView extends ScrollView {

    // Properties
    private int desiredScrollX = -1;
    private int desiredScrollY = -1;
    private OnGlobalLayoutListener gol;

    // ================================================================================================================
    // CONSTRUCTOR ----------------------------------------------------------------------------------------------------

    public ZScrollView(Context __context) {
        super(__context);
    }

    public ZScrollView(Context __context, AttributeSet __attrs) {
        super(__context, __attrs);
    }

    public ZScrollView(Context __context, AttributeSet __attrs, int __defStyle) {
        super(__context, __attrs, __defStyle);
    }

    // ================================================================================================================
    // PUBLIC INTERFACE -----------------------------------------------------------------------------------------------

    public void scrollToWithGuarantees(int __x, int __y) {
        // REALLY Scrolls to a position
        // When adding items to a scrollView, you can't immediately scroll to it - it takes a while
        // for the new addition to cycle back and update the scrollView's max scroll... so we have
        // to wait and re-set as necessary

        scrollTo(__x, __y);

        desiredScrollX = -1;
        desiredScrollY = -1;

        if (getScrollX() != __x || getScrollY() != __y) {
            // Didn't scroll properly: will create an event to try scrolling again later

            if (getScrollX() != __x) desiredScrollX = __x;
            if (getScrollY() != __y) desiredScrollY = __y;

            if (gol == null) {
                gol = new OnGlobalLayoutListener() {
                    @Override
                    public void onGlobalLayout() {
                        int nx = desiredScrollX == -1 ? getScrollX() : desiredScrollX;
                        int ny = desiredScrollY == -1 ? getScrollY() : desiredScrollY;
                        desiredScrollX = -1;
                        desiredScrollY = -1;
                        scrollTo(nx, ny);
                    }
                };

                getViewTreeObserver().addOnGlobalLayoutListener(gol);
            }
        }
    }
}

对我非常有用,因为我想在添加完ScrollView后立即滚动到给定的View。


不知道为什么,但是在 Nexus 7 上的 Android 5 上,使用 OnGlobalLayoutListener 的这个技巧不起作用。 - matreshkin
抱歉,那是我的错:/ 我有一个设置了LayoutTransition的线性布局。添加项目后,布局动画开始。直到它结束,我才能滚动到我想要的位置,仅限于当前(在动画期间)布局宽度。 - matreshkin

0

Try this it works: (kotlin)

             myScrollView.post(Runnable {
                 myScrollView.scrollTo(0, scrollValue)
             })

0
这是一个滚动回收视图的示例。
private int scrollPosition = 0;
@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.home_activity);
    ...
    // set scrollPosition value
    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            scrollPosition += dy;
        }
    });
    ...
}      

@Override
protected void onResume() {
    super.onResume();
    ...
    // scrollToPosition recycleView
    recyclerView.post(() -> {
        recyclerView.scrollToPosition(scrollPosition);//0 is x position
    });
}


// Save the scroll position when the activity is paused (e.g., when navigating away)
@Override
protected void onPause() {
    super.onPause();
    scrollPosition = getCurrentScrollPosition();
}

private int getCurrentScrollPosition() {
    LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
    if (layoutManager != null) {
        return layoutManager.findLastVisibleItemPosition();
    }
    return 0;
}

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