当我们有两个ScrollView嵌套时,通常需要解决事件冲突。不然当内层的ScrollView因为内容展示不完整时,比如内层的内容长度是1000,但是内层的ScrollView的viewport则只有500时,那么在嵌套ScrollView时,内层的ScrollView是无法滑动查看自己的内容的。若要让内层的ScrollView可以滑动,那么我们可以在内层的ScrollView的事件拦截中,通过强制要求父布局,也就是外层的ScrollView不要处理该事件,就可以解决这个问题。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
getParent().requestDisallowInterceptTouchEvent(true);
return super.onInterceptTouchEvent(ev);
}
但是这样的效果有点僵硬,当用户滑动内层ScrollView到内容的上下边缘时,其实是想要让外层ScrollView能够接着继续滑动,而不是抬起手指再次滑动。也就存在两种情况:
- 1:当InnerScrollView的内容mScrollY是0时,允许用户向下滑动内层scrollView时,其实联动外层scrollView跟着滑动。
- 2:当InnerScrollView的内容mScrollY达到(内容高度-视窗高度)这个距离时,允许用户向上滑动内层scrollView,并且让外层scrollView跟着滑动。
那么我们要实现的目标如下:

思路上面已经很清楚了,那么就可以根据内部拦截法来实现代码如下:
private int lastY;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int currentY= (int) ev.getY();
switch (ev.getAction()){
case MotionEvent.ACTION_MOVE:
int i = currentY - lastY;
//判断方向
if(i>0){
if(getScrollY()==0){
getParent().requestDisallowInterceptTouchEvent(false);
}
}else{
//innerScrollView的可移动高度。
if(getScrollY()==(getChildAt(0).getMeasuredHeight()-getHeight())){
getParent().requestDisallowInterceptTouchEvent(false);
}
}
break;
}
lastY = currentY;
return super.dispatchTouchEvent(ev);
}
其中getScrollY()表示的就是内部ScrollView已经滑动的距离。
完整代码如下:
MyScrollView.java
public class MyScrollView extends ScrollView {
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
getParent().requestDisallowInterceptTouchEvent(true);
return super.onInterceptTouchEvent(ev);
}
private int lastY;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int currentY= (int) ev.getY();
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
int i = currentY - lastY;
if(i>0){
if(getScrollY()==0){
getParent().requestDisallowInterceptTouchEvent(false);
}
}else{
//innerScrollView的可移动高度。
if(getScrollY()==(getChildAt(0).getMeasuredHeight()-getHeight())){
getParent().requestDisallowInterceptTouchEvent(false);
}
}
break;
}
lastY = currentY;
return super.dispatchTouchEvent(ev);
}
}
activity_scrollview_brace_scrollview.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:gravity="center"
android:textSize="50dp"
android:text="OuterScrollView"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#8900ad" />
<com.goodsnow.view.custom.MyScrollView
android:layout_width="match_parent"
android:layout_height="400dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="30dp"
android:text="内层scrollView开始" />
<TextView
android:textSize="50dp"
android:gravity="center"
android:text="innerScrollView"
android:layout_width="match_parent"
android:layout_height="500dp"
android:background="#554411" />
<TextView
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="30dp"
android:text="内层scrollView结束" />
</LinearLayout>
</com.goodsnow.view.custom.MyScrollView>
<TextView
android:text="OuterScrollView"
android:gravity="center"
android:textSize="50dp"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#a60127" />
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
网友评论