【问题标题】:Synchronise ScrollView scroll positions - android同步 ScrollView 滚动位置 - android
【发布时间】:2011-04-26 07:46:11
【问题描述】:

我的 android 布局中有 2 个 ScrollView。如何同步他们的滚动位置?

【问题讨论】:

    标签: android scroll


    【解决方案1】:

    ScrollView中有一个方法...

    protected void onScrollChanged(int x, int y, int oldx, int oldy)
    

    不幸的是,Google 从未想过我们需要访问它,这就是为什么他们将其设为受保护并且没有添加“setOnScrollChangedListener”挂钩的原因。所以我们必须自己做。

    首先我们需要一个接口。

    package com.test;
    
    public interface ScrollViewListener {
    
        void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy);
    
    }
    

    然后我们需要重写 ScrollView 类,提供 ScrollViewListener 钩子。

    package com.test;
    
    import android.content.Context;
    import android.util.AttributeSet;
    import android.widget.ScrollView;
    
    public class ObservableScrollView extends ScrollView {
    
        private ScrollViewListener scrollViewListener = null;
    
        public ObservableScrollView(Context context) {
            super(context);
        }
    
        public ObservableScrollView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }
    
        public ObservableScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public void setScrollViewListener(ScrollViewListener scrollViewListener) {
            this.scrollViewListener = scrollViewListener;
        }
    
        @Override
        protected void onScrollChanged(int x, int y, int oldx, int oldy) {
            super.onScrollChanged(x, y, oldx, oldy);
            if(scrollViewListener != null) {
                scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);
            }
        }
    
    }
    

    我们应该在布局中指定这个新的 ObservableScrollView 类,而不是现有的 ScrollView 标签。

    <com.test.ObservableScrollView
        android:id="@+id/scrollview1"
        ... >
    
        ...
    
    </com.test.ObservableScrollView>
    

    最后,我们将它们放在 Layout 类中。

    package com.test;
    
    import android.app.Activity;
    import android.os.Bundle;
    
    public class Q3948934 extends Activity implements ScrollViewListener {
    
        private ObservableScrollView scrollView1 = null;
        private ObservableScrollView scrollView2 = null;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.q3948934);
    
            scrollView1 = (ObservableScrollView) findViewById(R.id.scrollview1);
            scrollView1.setScrollViewListener(this);
            scrollView2 = (ObservableScrollView) findViewById(R.id.scrollview2);
            scrollView2.setScrollViewListener(this);
        }
    
        public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) {
            if(scrollView == scrollView1) {
                scrollView2.scrollTo(x, y);
            } else if(scrollView == scrollView2) {
                scrollView1.scrollTo(x, y);
            }
        }
    
    }
    

    scrollTo() 代码为我们处理任何循环条件,因此我们无需担心。唯一需要注意的是,这个解决方案不能保证在未来的 Android 版本中有效,因为我们覆盖了一个受保护的方法。

    【讨论】:

    • 嗨,安迪,感谢您花时间提出这个答案 - 让我前进。再次感谢。蒂姆
    • -Andy 我也在使用代码,但它让我期待空指针,你能帮我吗
    • @hemant 您能否指出 NullPointer 是在哪一行抛出的以及您使用的 Android 版本是什么?
    • 今日英雄:救了我的命! =D
    • 嗨,安迪,谢谢你的代码。它工作得很好。我想知道一件事[如果你或任何人有 ans。至此]- 当我抛出我的滚动视图时,onscrollchanged 方法不会在滚动视图移动时注册所有 X 和 Y 坐标。它只给了我起始位置和最后一个位置。例如,我从 Y=10 开始投掷并在 Y=30 处离开,然后投掷速度将其带到 Y = 50 然后停止。所以 onscroll 只改变了寄存器——可能是 10、11、12..30,然后是 49、50。我怎样才能让它也注册所有中间位置并获取从 10 到 50 的所有坐标??
    【解决方案2】:

    对 Andy 解决方案的改进: 在他的代码中,他使用了scrollTo,问题是,如果您将一个滚动视图向一个方向移动,然后将另一个滚动视图向另一个方向移动,您会注意到第一个并没有停止他之前的移动。

    这是因为scrollView使用computeScroll()来做它的投掷手势,并且它进入与scrollTo冲突。

    为了防止这种情况,只需以这种方式对 onScrollChanged 进行编程:

        public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) {
        if(interceptScroll){
            interceptScroll=false;
            if(scrollView == scrollView1) {
                scrollView2.onOverScrolled(x,y,true,true);
            } else if(scrollView == scrollView2) {
                scrollView1.onOverScrolled(x,y,true,true);
            }
            interceptScroll=true;
        }
    }
    

    使用interceptScroll 初始化为true 的静态布尔值。 (这有助于避免 ScrollChanged 出现无限循环)

    onOverScrolled 是我发现的唯一一个可以用来阻止 scrollView 的函数(但可能还有其他我错过了!)

    为了访问此功能(受保护),您必须将其添加到您的 ObservableScrollViewer

    public void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
    }
    

    【讨论】:

    • 多么好的收获,儿子!
    【解决方案3】:

    为什么不在你的活动中实现OnTouchListener。 然后重写onTouch方法,然后获取第一个ScrollViewOne.getScrollY()的滚动位置并更新ScrollViewTwo.scrollTo(0, ScrollViewOne.getScrollY());

    只是另一个想法... :)

    【讨论】:

    • 这可以工作,但是当你触摸并放手时,滚动视图可以在你放手后滚动一点,这不会被捕获。
    • 在这种情况下,如何在 java 中引用/获取 scrollView ScrollViewOne 的引用?
    • 还有其他的滚动方式(比如onTrackballEvent())
    • 这是一个非常好的主意,但不是获取和设置滚动 y 位置,而是将触摸事件发送到第二个滚动视图更容易。这也将在第二个滚动视图中保持“投掷”。 MotionEvent newEvent = MotionEvent.obtain(event);文档ScrollView.dispatchTouchEvent(newEvent);
    【解决方案4】:

    在 Android support-v4 包中,Android 提供了一个名为 NestedScrollView 的新类。

    我们可以在布局xml中用&lt;android.support.v4.widget.NestedScrollView&gt;替换&lt;ScrollView&gt;节点, 并在 Java 中实现其NestedScrollView.OnScrollChangeListener 来处理滚动。

    这让事情变得更容易。

    【讨论】:

      猜你喜欢
      • 2015-07-29
      • 1970-01-01
      • 1970-01-01
      • 2021-08-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多