Android: 两个不同视图的同步滚动

3

我有一个与两个不同视图的同步滚动相关的棘手问题。 我制作了自己的自定义网格视图小部件,它具有左侧和顶部的“粘性”视图,只在一个方向上与网格一起移动。想像一个日历,你在上面有时间,在左边有日期,当你水平滚动时间时,日期视图应该保持不变,当你垂直滚动日期时,时间视图应该保持不变。

网格本身是使用嵌套的水平ScrollView实现的,位于垂直ScrollView中。网格工作得很好,所以没有问题。由于粘性视图不在实际网格中,因此我已经覆盖了网格ScrollView中的onScrollChanged,并在用户滚动网格时以编程方式调用了sticky视图的scrollTo。

这按预期工作,除了两个不同视图开始滚动和结束滚动的时间存在轻微的偏移。我认为考虑到滚动可能是在线程上线性执行的,这是有道理的。

所有视图都是scroll views,我启用了平滑滚动并使用smoothScrollTo等方法来尝试改进,但无论如何问题都是一样的。该问题特别明显在较大的屏幕上,例如三星Galaxy Tab上,而在中小型屏幕设备上几乎不可见。

任何帮助都将不胜感激!如果有简单的解决方法,那太好了......如果意味着需要新的设计(满足上面的粘性视图用例),那就这样吧。

触发程序滚动的代码,水平方向相同

@Override  
protected void onScrollChanged(int x, int y, int oldx, int oldy) {  
   mListener.onScrollY(y);  
   super.onScrollChanged(x, y, oldx, oldy);  
}  
// which leads to,  
// Handle vertical scroll  
public void onScrollY(final int y) {  
   mCurrentY = y;  
   mVerticalScroll.smoothScrollTo(0, y);  
}  

以下是XML布局,如果有帮助的话

实际的网格是一个水平滚动视图包裹在垂直滚动视图中,网格项在嵌套的线性布局中垂直添加
>

  < com.....VerticalScrollView  
    android:id="@+id/gridscroll" 
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent" 
    android:layout_below="@id/timescroll"
    android:layout_toRightOf="@id/vertscroll"  
    android:layout_alignTop="@id/vertscroll"  
    android:layout_marginLeft="2dp" android:scrollbars="none"  
    android:fadingEdge="none">   

    < com....HorizScrollView
    android:id="@+id/horizscroll"
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent"
    android:scrollbars="none"  
    android:fadingEdge="none">  

    < LinearLayout android:id="@+id/grid"  
      android:layout_width="fill_parent"  
      android:layout_height="fill_parent"  
      android:orientation="vertical">  

      < /LinearLayout>  

      < /com.....HorizScrollView>  

      < /com.....VerticalScrollView>  

The horizontal sticky view

 < com.....GridTimeScrollView
    android:id="@+id/timescroller" 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:scrollbars="none"
    android:fadingEdge="none">

    < LinearLayout android:id="@+id/timelist"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" />
    < /com.....GridTimeScrollView>

垂直粘性视图

< com....GridVertListScrollView
android:id="@+id/vertscroller"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="none" 
android:fadingEdge="none">

< LinearLayout
android:id="@+id/vertitemlist"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" />
< /com.....GridVertListScrollView>
3个回答

6
首先,我认为你应该意识到这一点:ScrollView Inside ScrollView
简而言之:在滚动视图中使用滚动视图是一件糟糕的事情,会破坏许多优化。 现在,进入你的问题。
我有一个类似于你描述的需求。最终,我实现了一个自定义视图及其onDraw方法。部分原因是我正在绘制一些不同寻常的东西,而你可能不必这样做。
无论如何,我认为你最好的选择是:
  1. 实现一个自定义视图,该视图扩展RelativeLayout
  2. 创建此视图的布局,其中包括顶部、左侧和“主要”视图,它们将是可滚动组件
  3. 向此视图添加OnGestureListener并将触摸事件传递到自定义视图中的活动
  4. 当手势侦听器检测到快速滑动或滚动时,在每个滚动视图中调用scrollBy。当这样做时,如果希望顶部视图仅水平滚动,请将0作为垂直滚动距离传递。
  5. 为了实现平稳的快速滑动动作,您需要在自定义视图中创建一个scroller。当手势监听器检测到快速滑动事件时,设置滚动条。然后,重写自定义视图的computeScroll()方法,并更新每个子视图的滚动。 请参阅示例以了解如何实现它。对不起,我会尽可能发布更好的示例。查看下面的代码... 它更简单 :)

更新:示例代码

@Override
    public void computeScroll() {
        if (scroller.computeScrollOffset()) {
            if (!scrolledLastFrame) {
                lastX = scroller.getStartX();
                lastY = scroller.getStartY();
            }

            int dx = scroller.getCurrX() - lastX;
            int dy = scroller.getCurrY() - lastY;

            lastX = scroller.getCurrX();
            lastY = scroller.getCurrY();

            doScroll(dx, dy);
            scrolledLastFrame = true;
        } else {
            scrolledLastFrame = false;
        }

    }

谢谢你的回答。你提到的帖子似乎讨论了滚动视图和列表视图的组合?我确实意识到嵌套滚动视图可能不是最佳选择,但它似乎是不需要从头开始创建新小部件的唯一选择。在我认为这个问题已经得到解答之前,我想知道这如何保证比我的当前解决方案更“同步”的滚动?我是否仍然需要一次滚动一个视图(网格、粘性视图),这可能会导致它们滚动的偏移量略有不同? - Andreas
不,我认为你的“同步”问题是因为当你获取滚动信息时,它已经被一个视图处理过了,然后你命令其他视图像第一个视图一样滚动。这样,一个视图会滚动,其他视图将在下一个周期中进行相应的滚动。而我的方法是,所有视图同时滚动。这样你就不会遇到那个问题了。 - Pedro Loureiro

3

不要使用smoothScrollTo,而应该使用scrollTo。smoothScrollTo将从当前位置动画滚动到您想要的位置,因为您希望它们同步,您希望另一个滚动视图立即恰好位于另一个滚动视图所在的位置,scrollTo可以做到这一点。


2

看一下这些答案。为什么不使用OnTouch监听器来处理它呢?当触摸事件是MOVE时,你就调用它。

public void onScrollY(final int y) {  
   mCurrentY = y;  
   mVerticalScroll.smoothScrollTo(0, y);  
}  

使用另一个ScrollView的getVerticalScroll值很容易。并返回true,以便进一步处理TouchEvents。
以下是示例:
@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_MOVE:
            lv2.onScrollY(lv.getScrollY());
            break;
        default:
            break;
    }
    return false;
}

很简单,对吧?

不确定这是否完全符合您的要求。但是您能否详细说明您想要实现什么。不是布局方面的,而是实践方面的...您的UI的目的是什么?


谢谢你的回答。这可能是一个可行的解决方案,但现在我还有其他原因需要选择另一种实现方式。 - Andreas

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