當我們有兩個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跟著滑動。
那么我們要實現的目標如下:
scrollView.gif
思路上面已經很清楚了,那么就可以根據內部攔截法來實現代碼如下:
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>