ScrollView+TabLayout+ViewPager嵌套解決方案

首先看一下效果圖,圖片來自于簡書APP


簡書App示例

做移動開發(fā)的小伙伴們肯定會遇到這樣的設計要求吧,做為一個初級程序員要完成這個效果還是會遇到不少坑,所以在這里記錄一下,有什么問題也可以在下面的評論區(qū)留言。

注意事項

  • 本身項目中TabLayout只有三個模塊,所以只有三個Fragment,這個是跟上面的圖不太一樣的地方,這里提供的圖片只是一個大概的模型。
  • 簡書App的標題欄是有滑到頂部固定的效果,這個因為不是本篇文章的重點,所以不再詳細說明。

解決思路

  1. 當我們用ScrollView嵌套ViewPager的時候,如果每個ViewPager的頁面高度不同會導致下面有一部分的空白,所以要在ViewPager切換頁面的時候重置它的高度,因此要重寫ViewPager。
  2. ViewPager的子布局是四個Fragment,如果Fragment里面是ListView的話,就要求我們獲取到這個ListView的高度。

實現(xiàn)步驟

第一步 重寫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 defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

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

注意這里LsitView分頁加載的功能是通過給ScrollView設置滾動到底部的監(jiān)聽器實現(xiàn)的,下面的內(nèi)容會提到。

第二步 重寫ViewPager
public class PersonalViewPager extends ViewPager {

    private int position;
    
    private HashMap<Integer, Integer> maps = new LinkedHashMap<Integer, Integer>();

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int height = 0;
        for (int i = 0; i < this.getChildCount(); i++) {
            View child = getChildAt(i);
            child.measure(widthMeasureSpec,
                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            int h = child.getMeasuredHeight();
            maps.put(i, h);
        }
        if (getChildCount() > 0) {
            height = getChildAt(position).getMeasuredHeight();
        }
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
                MeasureSpec.EXACTLY);
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    /**
     * 在切換tab的時候,重置viewPager的高度
     */
    public void resetHeight(int position) {
        this.position = position;
        if (maps.size() > position) {
            LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
            if (layoutParams == null) {
                layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, maps.get(position));
            } else {
                layoutParams.height = maps.get(position);
            }
            setLayoutParams(layoutParams);
        }
    }
}
第三步 重寫ScrollView
public class PersonalScrollView extends ScrollView {

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

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

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

    @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());

        if ((d == 0) && (onScrollBottomListener != null)) {
            onScrollBottomListener.onScrollBottom();
        }
    }

    public OnScrollBottomListener onScrollBottomListener = null;

    public interface OnScrollBottomListener {
        void onScrollBottom();
    }

    public void setOnScrollBottomListener(OnScrollBottomListener onScrollBottomListener) {
        this.onScrollBottomListener = onScrollBottomListener;
    }
}
第四步 Activity實現(xiàn)
  • 實現(xiàn)TabLayout和ViewPager的聯(lián)動
    這個可以百度一下相關的文章,后面也可能專門寫一篇文章來詳細介紹
  • ViewPager的滾動監(jiān)聽
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                if (position == 0) {
                    mViewPager.resetHeight(0);
                } else if (position == 1) {
                    mViewPager.resetHeight(1);
                } else if (position == 2) {
                    mViewPager.resetHeight(2);
                }
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
  • ScrollView的滾動監(jiān)聽
mScrollView.setOnScrollBottomListener(new PersonalScrollView.OnScrollBottomListener() {
            @Override
            public void onScrollBottom() {
                if (mTabLayout.getSelectedTabPosition() == 0) {
                    ((PersonalCourseFragment) fragmentList.get(0)).loadData();
                } else if (mTabLayout.getSelectedTabPosition() == 1) {
                    ((PersonalNewsFragment) fragmentList.get(1)).loadData();
                } else if (mTabLayout.getSelectedTabPosition() == 2) {
                    ((PersonalIssueAnswerFragment) fragmentList.get(2)).loadData();
                }
            }
        });

這里的loadData()方法是在Fragment里面自定義加載數(shù)據(jù)的方法。

也有簡書的朋友推薦CoordinatorLayout+TabLayout+ViewPager實現(xiàn),也是一種可行的辦法,不過這個要再研究一下,有結(jié)果的話就補充在文章的末尾。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容