當(dāng)我們有兩個ScrollView嵌套時斑响,通常需要解決事件沖突煞额。不然當(dāng)內(nèi)層的ScrollView因為內(nèi)容展示不完整時,比如內(nèi)層的內(nèi)容長度是1000,但是內(nèi)層的ScrollView的viewport則只有500時中狂,那么在嵌套ScrollView時,內(nèi)層的ScrollView是無法滑動查看自己的內(nèi)容的祖搓。若要讓內(nèi)層的ScrollView可以滑動佑力,那么我們可以在內(nèi)層的ScrollView的事件攔截中,通過強制要求父布局植袍,也就是外層的ScrollView不要處理該事件惧眠,就可以解決這個問題。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
getParent().requestDisallowInterceptTouchEvent(true);
return super.onInterceptTouchEvent(ev);
}
但是這樣的效果有點僵硬于个,當(dāng)用戶滑動內(nèi)層ScrollView到內(nèi)容的上下邊緣時氛魁,其實是想要讓外層ScrollView能夠接著繼續(xù)滑動,而不是抬起手指再次滑動厅篓。也就存在兩種情況:
- 1:當(dāng)InnerScrollView的內(nèi)容mScrollY是0時秀存,允許用戶向下滑動內(nèi)層scrollView時,其實聯(lián)動外層scrollView跟著滑動羽氮。
- 2:當(dāng)InnerScrollView的內(nèi)容mScrollY達到(內(nèi)容高度-視窗高度)這個距離時或链,允許用戶向上滑動內(nèi)層scrollView,并且讓外層scrollView跟著滑動档押。
那么我們要實現(xiàn)的目標如下:
scrollView.gif
思路上面已經(jīng)很清楚了澳盐,那么就可以根據(jù)內(nèi)部攔截法來實現(xiàn)代碼如下:
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);
}
其中g(shù)etScrollY()表示的就是內(nèi)部ScrollView已經(jīng)滑動的距離。
完整代碼如下:
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="內(nèi)層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="內(nèi)層scrollView結(jié)束" />
</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>