1. 程式人生 > >ScrollView+TabLayout+ViewPager+ListView複雜滑動巢狀、上拉載入

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>