ScrollView+TabLayout+ViewPager+ListView复杂滑动嵌套、上拉加载

先来看看要实现布局的样式哈,我感觉手动画的更详细嘿嘿。
这里写图片描述

要实现的就是这样的复杂布局,这里面涉及到各种嵌套滑动的冲突,还涉及ListView的上拉加载,接下来一点点开始哈。

一、ScrollView嵌套ListView

首先从整体看就会看到ScrollView和ListView,我们写过他俩嵌套的都知道,他俩有冲突,所以这里我们就要对ListView下手啦。

如果单纯的想解决ScrollView嵌套ListView冲突,使用如下方法会出现一个问题,就是ListView会默认的滑到顶部(这个之后说哈)

他俩冲突归根结底是高度问题,所以解决ListView的高度也就解决他俩的嵌套滑动问题了。

public class MyListView extends ListView {
    public MyListView(Context context) {
        super(context);
    }

    public MyListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_MOVE){
            return true;
        }
        return super.dispatchTouchEvent(ev);
    }
}

这里要注意的是高度给了它最大值,这样就导致了ListView的滑动监听失效了,那么怎么才能监听ListView到底了呢?----重写ScrollView

public class ScrollBottomView extends ScrollView {

    private int downX;
    private int downY;
    private int mTouchSlop;
    private int type;

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    private ScrollViewToBottomListener scrollViewListener = null;
    private OnScrollViewToBottomLiatener onScrollViewToBottomLiatener = null;

    public ScrollBottomView(Context context) {
        super(context);
    }

    public ScrollBottomView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public ScrollBottomView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setScrollViewListener(ScrollViewToBottomListener scrollViewListener) {
        this.scrollViewListener = scrollViewListener;
    }

    public void setOnScrollViewToBottomLiatener(OnScrollViewToBottomLiatener onScrollViewToBottomLiatener){
        this.onScrollViewToBottomLiatener = onScrollViewToBottomLiatener;
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);

        View view = (View) getChildAt(getChildCount() - 1);

        int d = view.getBottom();

        d -= (getHeight() + getScrollY());

//        Log.e("---------->","d"+d);
        if (d == 0) {

            if (onScrollViewToBottomLiatener != null) {
                onScrollViewToBottomLiatener.onScrollViewToBottomListener(type);

            }

        } else {

            if (scrollViewListener != null) {
                scrollViewListener.onScrollChanged(this, l, t, oldl, oldt);
            }
        }
    }

    interface OnScrollViewToBottomLiatener {
        void onScrollViewToBottomListener();
    }
}

监听ScrollView是否到底了,间接的监听了ListView到底了

二、ScrollView嵌套ViewPager

当我们用ScrollView嵌套ViewPager的时候,如果每个ViewPager的页面高度不同会导致下面有一部分的空白,所以要在ViewPager改变页面的时候重置它的高度,所以要重写ViewPager了

public class ChildViewPager extends ViewPager {

    private int current;
    /**
     * 保存position与对于的View
     */
    private HashMap<Integer, Integer> maps = new LinkedHashMap<Integer, Integer>();

    public ChildViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ChildViewPager(Context context) {
        super(context);
    }

    public int getCurrent() {
        return current;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int height = 0;
        // 下面遍历所有child的高度
        for (int i = 0; i < this.getChildCount(); i++) {
            View child = getChildAt(i);
            child.measure(widthMeasureSpec,
                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            int h = child.getMeasuredHeight();
            // 采用最大的view的高度
            maps.put(i, h);

        }
        if (getChildCount() > 0) {
            height = getChildAt(current).getMeasuredHeight();

        }
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
                MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    public void resetHeight(int current) {
        this.current = current;
        if (maps.size() > current) {

            LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
            if (layoutParams == null) {
                layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, maps.get(current));
            } else {
                layoutParams.height = maps.get(current);
            }
            setLayoutParams(layoutParams);
        }
    }

}

看到resetHeight这个方法没有,给ViewPager设置监听,在onPageSelected方法中调用这个方法,来改变ViewPager的高度

注意:

1、得到ViewPager后要给它设置一个属性,来避免ListView滑到顶部

    viewPager.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);

2、如果编译时ViewPager出错要设置如下代码

//有几页设置几页
viewPager.setOffscreenPageLimit(3);

三、ViewPager中的Fragment

public class MyListViewFragment extends Fragment {

    private MyListView myListView;
    private ArrayAdapter<String> adapter;
    private List<String> list;
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_mylistview,container,false);

        myListView = (MyListView) view.findViewById(R.id.listView);
        list = new ArrayList<>();
        for(int i =0;i<30;i++){
            list.add("数据:"+i);
        }

        adapter = new ArrayAdapter<String>(getActivity(),android.R.layout.simple_list_item_1,list);
        myListView.setAdapter(adapter);
        return view;
    }

    public void loadData(){
        //加载数据list.add()……别忘了刷新哈
    }

}

四、整体的代码调用

我认为主要的都写在标注里了,一个是上拉加载,一个是ViewPager的相关设置

public class ScrollViewEnterActivity extends AppCompatActivity {

    private ScrollBottomView mScrollView;

    private ChildViewPager viewPager;
    private TabLayout tabLayout;
    private MyFragmentAdapter adapter;
    private List<MyListViewFragment> fragments;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scroll_view_enter);
        viewPager = (ChildViewPager) findViewById(R.id.viewpager);
        tabLayout = (TabLayout) findViewById(R.id.tablayout);
        mScrollView = (ScrollBottomView) findViewById(R.id.activity_scroll_view_enter);
        mScrollView.setOnScrollViewToBottomLiatener(new ScrollBottomView.OnScrollViewToBottomLiatener() {
            @Override
            public void onScrollViewToBottomListener(int type) {
                //滑到底部刷新每个tab中的数据
                fragments.get(viewPager.getCurrent()).loadData();
            }
        });

        fragments = new ArrayList<>();
        for(int i = 0;i<3;i++){
            fragments.add(new MyListViewFragment());
        }

        adapter = new MyFragmentAdapter(getSupportFragmentManager());
        viewPager.setAdapter(adapter);
        tabLayout.setupWithViewPager(viewPager);
        //设置欲缓存页
        viewPager.setOffscreenPageLimit(3);

        //我通过xml设置该属性不管用,这样好用(不知道为什么),设置该属性目的是防止listView(ViewPager)跳到顶部

        viewPager.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
        viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                viewPager.resetHeight(position);
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });

    }

    private class MyFragmentAdapter extends FragmentPagerAdapter{

        public MyFragmentAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            return fragments.get(position);
        }

        @Override
        public int getCount() {
            return 3;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return "标题"+position;
        }
    }
}

看看XML文件吧

<?xml version="1.0" encoding="utf-8"?>
<com.example.ws.scrollviewdemo.ScrollBottomView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_scroll_view_enter"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.ws.scrollviewdemo.ScrollViewEnterActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="300dp"
            android:background="@color/color_00ff66"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="各种View" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="随意的View" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:background="@color/color_ff6b00" />
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_launcher"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="动不动" />

        <android.support.design.widget.TabLayout
            android:id="@+id/tablayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

        </android.support.design.widget.TabLayout>
        <com.example.ws.scrollviewdemo.ChildViewPager
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            >

        </com.example.ws.scrollviewdemo.ChildViewPager>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="1" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="2" />

    </LinearLayout>

</com.example.ws.scrollviewdemo.ScrollBottomView>
  • 4
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 30
    评论
在使用ViewPager和ScrollView嵌套时,可能会出现滑动冲突的问题,解决方法如下: 1. 自定义ViewPager,重写onInterceptTouchEvent方法,处理滑动事件: ```java public class CustomViewPager extends ViewPager { private float mStartX; private float mStartY; public CustomViewPager(Context context) { super(context); } public CustomViewPager(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mStartX = ev.getX(); mStartY = ev.getY(); break; case MotionEvent.ACTION_MOVE: float x = ev.getX(); float y = ev.getY(); float dx = Math.abs(x - mStartX); float dy = Math.abs(y - mStartY); if (dx > dy) { return super.onInterceptTouchEvent(ev); } else { getParent().requestDisallowInterceptTouchEvent(true); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: getParent().requestDisallowInterceptTouchEvent(false); break; } return super.onInterceptTouchEvent(ev); } } ``` 2. 在ScrollView中设置onTouchEvent事件,处理滑动事件: ```java public class CustomScrollView extends ScrollView { private float mStartX; private float mStartY; public CustomScrollView(Context context) { super(context); } public CustomScrollView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mStartX = ev.getX(); mStartY = ev.getY(); getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: float x = ev.getX(); float y = ev.getY(); float dx = Math.abs(x - mStartX); float dy = Math.abs(y - mStartY); if (dy > dx) { getParent().requestDisallowInterceptTouchEvent(false); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: getParent().requestDisallowInterceptTouchEvent(false); break; } return super.onTouchEvent(ev); } } ``` 3. 在布局文件中使用自定义的ViewPager和ScrollView。 ```xml <com.example.CustomViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="wrap_content"> <androidx.viewpager.widget.ViewPager android:id="@+id/pager" android:layout_width="match_parent" android:layout_height="wrap_content" /> </com.example.CustomViewPager> <com.example.CustomScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> ... </LinearLayout> </com.example.CustomScrollView> ``` 以上是一种常见的解决滑动冲突问题的方法,在使用时需要注意不同的布局会有不同的处理方式,可以根据实际情况进行调整。
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值