滚动视图同步问题
Scrollviews synchronisation issue
我写了一个小应用程序,显示 2 个并排的滚动视图。我需要同步 2 个滚动视图的滚动位置。为此,我扩展了 ScrollView 并覆盖了 onScrollChanged 以在发生滚动时得到通知,然后同步 2 个滚动视图。
我的两个滚动视图显示一堆蓝色视图。左边scrollView是红色背景,右边是绿色背景。
这是左侧 scrollView 滚动时发生的情况:
=> 同步正常
下面是右侧 scrollView 滚动时发生的情况:
=> 同步不好,有差距
(两个屏幕截图都是在 scrollviews 浏览期间截取的)
两种情况如何同步好?
我的Activity、我的scrollView和我的scrollView容器的代码是here。
您似乎遇到了系统限制 - 系统按照在您的 XML 中声明的顺序绘制视图。
因此,当您抛出第一个声明的 ScrollView
时,第二个会更新,但第一个不会再次更新。然而,当你扔第二个时,第一个得到更新,然后第二个得到更新,变化反映到第一个并再次更新。
我不确定上述描述是否 100% 准确,但大致是这样。
我创建了一个测试用例来检查我的假设,用以下内容代替 main.xml
:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/main_layout">
<View android:id="@+id/separator_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_centerHorizontal="true"
android:visibility="invisible"/>
<com.example.www.syncscrollviewtesting_Whosebug.ObservableScrollView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_toEndOf="@id/separator_view"
android:background="@android:color/holo_green_light"
android:id="@+id/left_scrollview">
<com.example.www.syncscrollviewtesting_Whosebug.Container
android:layout_width="match_parent"
android:layout_height="100000dp"
android:minHeight="100000dp"
android:id="@+id/left_container"/>
</com.example.www.syncscrollviewtesting_Whosebug.ObservableScrollView>
<com.example.www.syncscrollviewtesting_Whosebug.ObservableScrollView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_toStartOf="@id/separator_view"
android:background="@android:color/holo_red_dark"
android:id="@+id/right_scrollview">
<com.example.www.syncscrollviewtesting_Whosebug.Container
android:layout_width="match_parent"
android:layout_height="100000dp"
android:minHeight="100000dp"
android:id="@+id/right_container"/>
</com.example.www.syncscrollviewtesting_Whosebug.ObservableScrollView>
</RelativeLayout>
上面的 XML 允许您将两个 ScrollView
放置在 separator_view
的两侧。我发现无论你如何定位它们,ScrollView
的红色背景(第二个声明)总是会导致 "lag",而绿色背景的 ScrollView
会起作用很好。
我还试图通过将此添加到他们的代码中来防止对 ScrollView
进行不必要的更新:
@Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
if (!mIsDisabled && scrollViewListener != null) {
scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);
}
}
public void setDisabled(boolean isDisabled) {
mIsDisabled = isDisabled;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mIsDisabled)
return false; // Ignore touch event when disabled
else
return super.onTouchEvent(ev);
}
... 这对 Activity
的:
@Override
public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) {
if (scrollView == mRightScrollView) {
mLeftScrollView.setDisabled(true);
mLeftScrollView.setScrollY(y);
mLeftScrollView.setDisabled(false);
} else {
mRightScrollView.setDisabled(true);
mRightScrollView.setScrollY(y);
mRightScrollView.setDisabled(false);
}
}
但也没用...
所以,我猜,你最好找到另一种方法,它不涉及重新绘制一大堆 Views
,或者只接受 "lag".
解法:
根据我对情况的分析,这个解决方案是OP自己提供的:触摸事件可以从右边ScrollView
(在XML中声明为第二个)转发到左边ScrollView
。这样,考虑到左侧的滑动 ScrollView
不会导致延迟,所有触摸事件都被视为由第一个声明的 ScrollView
发起,并且避免了延迟。
很抱歉我之前的回答没有正确研究您的问题。
如我所说,我认为您无法在完全没有任何延迟的情况下正确同步 2 ScrollView
。但是您仍然可以使用单个视图。
这有点棘手,因为您还想将拆分分成具有权重属性的相等部分。
可以找到解决方案here。在此基础上,我提出了以下代码:
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:id="@+id/spacer"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_centerHorizontal="true"/>
<LinearLayout
android:id="@+id/left"
android:layout_alignRight="@id/spacer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
...
</LinearLayout>
<LinearLayout
android:id="@+id/right"
android:layout_alignLeft="@id/spacer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
...
</LinearLayout>
</RelativeLayout>
</ScrollView>
然后当您想要切换 left
和 right
视图重叠时调用此:
leftParams.addRule(RelativeLayout.ALIGN_RIGHT, 0);
rightParams.addRule(RelativeLayout.ALIGN_LEFT, 0);
和
leftParams.addRule(RelativeLayout.ALIGN_RIGHT, R.id.spacer);
rightParams.addRule(RelativeLayout.ALIGN_LEFT, R.id.spacer);
设置参数后,您需要重绘视图:
left.requestLayout();
right.requestLayout();
可在 this GitHub page.
上找到工作示例
我写了一个小应用程序,显示 2 个并排的滚动视图。我需要同步 2 个滚动视图的滚动位置。为此,我扩展了 ScrollView 并覆盖了 onScrollChanged 以在发生滚动时得到通知,然后同步 2 个滚动视图。
我的两个滚动视图显示一堆蓝色视图。左边scrollView是红色背景,右边是绿色背景。
这是左侧 scrollView 滚动时发生的情况:
=> 同步正常
下面是右侧 scrollView 滚动时发生的情况:
=> 同步不好,有差距
(两个屏幕截图都是在 scrollviews 浏览期间截取的)
两种情况如何同步好?
我的Activity、我的scrollView和我的scrollView容器的代码是here。
您似乎遇到了系统限制 - 系统按照在您的 XML 中声明的顺序绘制视图。
因此,当您抛出第一个声明的 ScrollView
时,第二个会更新,但第一个不会再次更新。然而,当你扔第二个时,第一个得到更新,然后第二个得到更新,变化反映到第一个并再次更新。
我不确定上述描述是否 100% 准确,但大致是这样。
我创建了一个测试用例来检查我的假设,用以下内容代替 main.xml
:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/main_layout">
<View android:id="@+id/separator_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_centerHorizontal="true"
android:visibility="invisible"/>
<com.example.www.syncscrollviewtesting_Whosebug.ObservableScrollView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_toEndOf="@id/separator_view"
android:background="@android:color/holo_green_light"
android:id="@+id/left_scrollview">
<com.example.www.syncscrollviewtesting_Whosebug.Container
android:layout_width="match_parent"
android:layout_height="100000dp"
android:minHeight="100000dp"
android:id="@+id/left_container"/>
</com.example.www.syncscrollviewtesting_Whosebug.ObservableScrollView>
<com.example.www.syncscrollviewtesting_Whosebug.ObservableScrollView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_toStartOf="@id/separator_view"
android:background="@android:color/holo_red_dark"
android:id="@+id/right_scrollview">
<com.example.www.syncscrollviewtesting_Whosebug.Container
android:layout_width="match_parent"
android:layout_height="100000dp"
android:minHeight="100000dp"
android:id="@+id/right_container"/>
</com.example.www.syncscrollviewtesting_Whosebug.ObservableScrollView>
</RelativeLayout>
上面的 XML 允许您将两个 ScrollView
放置在 separator_view
的两侧。我发现无论你如何定位它们,ScrollView
的红色背景(第二个声明)总是会导致 "lag",而绿色背景的 ScrollView
会起作用很好。
我还试图通过将此添加到他们的代码中来防止对 ScrollView
进行不必要的更新:
@Override
protected void onScrollChanged(int x, int y, int oldx, int oldy) {
super.onScrollChanged(x, y, oldx, oldy);
if (!mIsDisabled && scrollViewListener != null) {
scrollViewListener.onScrollChanged(this, x, y, oldx, oldy);
}
}
public void setDisabled(boolean isDisabled) {
mIsDisabled = isDisabled;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mIsDisabled)
return false; // Ignore touch event when disabled
else
return super.onTouchEvent(ev);
}
... 这对 Activity
的:
@Override
public void onScrollChanged(ObservableScrollView scrollView, int x, int y, int oldx, int oldy) {
if (scrollView == mRightScrollView) {
mLeftScrollView.setDisabled(true);
mLeftScrollView.setScrollY(y);
mLeftScrollView.setDisabled(false);
} else {
mRightScrollView.setDisabled(true);
mRightScrollView.setScrollY(y);
mRightScrollView.setDisabled(false);
}
}
但也没用...
所以,我猜,你最好找到另一种方法,它不涉及重新绘制一大堆 Views
,或者只接受 "lag".
解法:
根据我对情况的分析,这个解决方案是OP自己提供的:触摸事件可以从右边ScrollView
(在XML中声明为第二个)转发到左边ScrollView
。这样,考虑到左侧的滑动 ScrollView
不会导致延迟,所有触摸事件都被视为由第一个声明的 ScrollView
发起,并且避免了延迟。
很抱歉我之前的回答没有正确研究您的问题。
如我所说,我认为您无法在完全没有任何延迟的情况下正确同步 2 ScrollView
。但是您仍然可以使用单个视图。
这有点棘手,因为您还想将拆分分成具有权重属性的相等部分。
可以找到解决方案here。在此基础上,我提出了以下代码:
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:id="@+id/spacer"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_centerHorizontal="true"/>
<LinearLayout
android:id="@+id/left"
android:layout_alignRight="@id/spacer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
...
</LinearLayout>
<LinearLayout
android:id="@+id/right"
android:layout_alignLeft="@id/spacer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
...
</LinearLayout>
</RelativeLayout>
</ScrollView>
然后当您想要切换 left
和 right
视图重叠时调用此:
leftParams.addRule(RelativeLayout.ALIGN_RIGHT, 0);
rightParams.addRule(RelativeLayout.ALIGN_LEFT, 0);
和
leftParams.addRule(RelativeLayout.ALIGN_RIGHT, R.id.spacer);
rightParams.addRule(RelativeLayout.ALIGN_LEFT, R.id.spacer);
设置参数后,您需要重绘视图:
left.requestLayout();
right.requestLayout();
可在 this GitHub page.
上找到工作示例