android开发:ScrollView嵌套ListView滑动冲突处理
需求:
实现一个页面上整体内容可以上下滚动,页面的上半部分是一些固定内容,下半部分是一个固定高度的列表。
这种布局页面底部使用ListView,外层使用ScrollView包裹就可以实现页面内容整体上下滚动。但是当ScrollView嵌套ListView的时候会产生滑动冲突,会导致ListView滑动不了。
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/scroll_view" android:layout_width="match_parent" android:layout_height="match_parent" android:scrollbars="none"> <androidx.appcompat.widget.LinearLayoutCompat android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> //上半部分固定内容 <TextView android:layout_width="match_parent" android:layout_height="500dp" android:gravity="center" android:textSize="15sp" android:textColor="@color/color_font_333333" android:text="上半部分固定内容"/> <FrameLayout android:layout_width="match_parent" android:layout_height="400dp"> <com.student.MyListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="match_parent" android:divider="#00000000" android:dividerHeight="10dp" android:listSelector="@android:color/transparent" android:scrollbars="none" /> <include layout="@layout/view_empty" /> </FrameLayout> </androidx.appcompat.widget.LinearLayoutCompat> </ScrollView>
解决方法:
MainActivity
MainActivity对ListView进行赋值,ListView保留ScrollView的引用。
package com.student; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.ScrollView; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import com.student.R; import com.student.MyListView; import java.util.ArrayList; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; public class MainActivity extends AppCompatActivity { @BindView(R.id.list_view) MyListView mListView; @BindView(R.id.scroll_view) ScrollView mScrollView; private List<String> mData; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); initView(); } private void initView() { mData = new ArrayList<>(); for (int i = 'A'; i <= 'Z'; i++) { mData.add((char) i + ""); } mListView.setAdapter(new ArrayAdapter<String>(this, R.layout.word_item)); mListView.setScrollView(mScrollView); } }
R.layout.word_item
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="50dp" android:gravity="center" android:text="A"/>
MyListView
package com.student; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.AbsListView; import android.widget.ListView; import android.widget.ScrollView; public class MyListView extends ListView { private boolean mIsScrolledToTop = true; private boolean mIsScrolledToBottom = true; private int mDownY, mMoveY; private ScrollView mScrollView; public MyListView(Context context) { super(context); init(); } public MyListView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public MyListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { //分别判断是否已经滑动到了顶部和底部 //当Item无法填充满ListView高度的时候,滑动到了顶部和底部都是成立的 mIsScrolledToTop = false; mIsScrolledToBottom = false; if (firstVisibleItem == 0) { View firstVisibleItemView = MyListView.this.getChildAt(0); if (firstVisibleItemView != null && firstVisibleItemView.getTop() == 0) { mIsScrolledToTop = true; } } if ((firstVisibleItem + visibleItemCount) == totalItemCount) { View lastVisibleItemView = MyListView.this.getChildAt(MyListView.this.getChildCount() - 1); //需要考虑Item无法填充满ListView高度的情况,所以使用<= if (lastVisibleItemView != null && lastVisibleItemView.getBottom() <= MyListView.this.getHeight()) { mIsScrolledToBottom = true; } } } }); } public void setScrollView(ScrollView scrollView) { mScrollView = scrollView; } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mDownY = (int) ev.getY(); break; case MotionEvent.ACTION_MOVE: if (MyListView.this.getAdapter() == null || MyListView.this.getAdapter().getCount() <= 0) { //空数据时,ListView不需要滑动事件 this.mScrollView.requestDisallowInterceptTouchEvent(false); } else { mMoveY = (int) ev.getY(); if (mMoveY > mDownY) { Log.e("MyListView", "向下滑动"); //向下滑动 //证明ListView已经滑动了顶部,这时候的滑动事件就应该交给ScrollView了,允许ScrollView对事件进行拦截访问 if (mIsScrolledToTop) { this.mScrollView.requestDisallowInterceptTouchEvent(false); } else { this.mScrollView.requestDisallowInterceptTouchEvent(true); } } else { Log.e("MyListView", "向上滑动"); //向上滑动 //证明ListView已经滑动了底部,这时候的滑动事件就应该交给ScrollView了,允许ScrollView对事件进行拦截访问 if (mIsScrolledToBottom) { this.mScrollView.requestDisallowInterceptTouchEvent(false); } else { this.mScrollView.requestDisallowInterceptTouchEvent(true); } } } break; case MotionEvent.ACTION_UP: this.mScrollView.requestDisallowInterceptTouchEvent(false); break; } return super.dispatchTouchEvent(ev); } }