android源码、开源框架解析

ScrollView嵌套ListView滑动冲突处理

2021-10-23  本文已影响0人  loby

需求:

实现一个页面上整体内容可以上下滚动,页面的上半部分是一些固定内容,下半部分是一个固定高度的列表。
这种布局页面底部使用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);
    }

}

QQ群:445224732

上一篇下一篇

猜你喜欢

热点阅读