美文网首页
ViewPager跟随整个页面向上移动,并固定头部效果实现记录

ViewPager跟随整个页面向上移动,并固定头部效果实现记录

作者: smallestt | 来源:发表于2018-02-01 17:52 被阅读0次

主要实现ViewPager跟随整个页面一起向上滑动,并把头部固定在顶部,解决ViewPager里fragment的刷新操作与整体向上移动操作的冲突。具体实现步骤:

  1. 自定义LinearLayout,实现向上滑动并固定的功能
    StickyNavLayout.java
public class StickyNavLayout extends LinearLayout implements NestedScrollingParent {
    private NestedScrollingParentHelper parentHelper = new NestedScrollingParentHelper(this);
    private View mTop;
    private View mNav;
    private ViewPager mViewPager;
    private int mTopViewHeight;
    private OverScroller mScroller;

    private boolean showTop = true;
    public OnLayoutScrollListener scroll = null;

    /**
     * 向外部暴露接口,当头部view消失后和显示时的监听
     * */
    public interface OnLayoutScrollListener {
        abstract void isTopShow(boolean isTopShow);
    }


    public void setOnListener(Fragment fragment) {
        scroll = (OnLayoutScrollListener) fragment;
    }

    public StickyNavLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOrientation(LinearLayout.VERTICAL);
        mScroller = new OverScroller(context);

    }


    @Override
    protected void onFinishInflate() {
        mTop = findViewById(R.id.id_stickynavlayout_topview);
        mNav = findViewById(R.id.id_stickynavlayout_indicator);
        mViewPager = (ViewPager) findViewById(R.id.id_stickynavlayout_viewpager);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mTopViewHeight = mTop.getMeasuredHeight();
        //上面测量的结果是viewPager的高度只能占满父控件的剩余空间
        //重新设置viewPager的高度
        ViewGroup.LayoutParams layoutParams = mViewPager.getLayoutParams();
        layoutParams.height = getMeasuredHeight() - mNav.getMeasuredHeight();
        mViewPager.setLayoutParams(layoutParams);
    }

    @Override
    public void scrollTo(int x, int y) {
        //限制滚动范围
        if (y < 0) {
            y = 0;
        }
        if (y > mTopViewHeight) {
            y = mTopViewHeight;
        }

        super.scrollTo(x, y);
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(0, mScroller.getCurrY());
            invalidate();
        }
    }

    public void fling(int velocityY) {
        mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, mTopViewHeight);
        invalidate();
    }

    //实现NestedScrollParent接口-------------------------------------------------------------------------

    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        return true;
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
        parentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
    }

    @Override
    public void onStopNestedScroll(View target) {
        parentHelper.onStopNestedScroll(target);
    }

    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
    }

    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
        boolean hiddenTop = dy > 0 && getScrollY() < mTopViewHeight;
        showTop = dy < 0 && getScrollY() >= 0 && !ViewCompat.canScrollVertically(target, -1);
         if (getScrollY() > 0)
            scroll.isTopShow(false);//头部view处于隐藏状态
        else scroll.isTopShow(true);//头部view处于显示状态
        if (hiddenTop || showTop) {
            scrollBy(0, dy);
            consumed[1] = dy;
        }

    }


    //boolean consumed:子view是否消耗了fling
    //返回值:自己是否消耗了fling。可见,要消耗只能全部消耗
    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
        Log.e("onNestedFling", "called");
        return false;
    }

    //返回值:自己是否消耗了fling。可见,要消耗只能全部消耗
    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
        Log.e("onNestedPreFling", "called");
        if (getScrollY() < mTopViewHeight) {
            fling((int) velocityY);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public int getNestedScrollAxes() {
        return parentHelper.getNestedScrollAxes();
    }
  1. value下创建id资源文件
    ids_sticky_nav_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="id_stickynavlayout_topview" type="id"/>
    <item name="id_stickynavlayout_viewpager" type="id"/>
    <item name="id_stickynavlayout_indicator" type="id"/>
    <item name="id_stickynavlayout_innerscrollview" type="id"/>
</resources>

注:因为要在StickyNavLayout里计算头部view的高度,所以在xml布局里头部控件的id也需引用ids_sticky_nav_layout.xml里的资源。

  1. xml布局里引入自定义LinearLayout
    <com.nongji.ah.custom.StickyNavLayout
        android:id="@+id/layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:orientation="vertical">

        <RelativeLayout
            android:id="@id/id_stickynavlayout_topview"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content">
          <!--头部view-->
        </RelativeLayout>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="@dimen/dp_45">

             <!--切换ViewPager每个Item的tab导航条-->
            <com.viewpagerindicator.TabPageIndicator
                android:id="@id/id_stickynavlayout_indicator"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginLeft="@dimen/dp_15"
                android:background="@color/white"
                android:visibility="gone" />

        </RelativeLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="@dimen/dp_1"
            android:background="@color/e0"></View>
        
       <!--需要跟随主页面一起移动的ViewPager-->
       <android.support.v4.view.ViewPager
            android:id="@id/id_stickynavlayout_viewpager"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </com.nongji.ah.custom.StickyNavLayout>

注:TabPageIndicator的实现方法在下一篇的记录。

4.解决ViewPager子Fragment下拉刷新和下滑ViewPager显示出头部view这两个操作冲突问题。

  • 在StickNavLayout里定义OnLayoutScrollListener接口,当ViewPager头部View显示或者隐藏时,实现isTopShow方法,这样方便ViewPager子Fragment通过isTopShow方法判断是否要刷新(当TopView显示时执行刷新操作,当TopView隐藏时,禁掉刷新操作而是执行主View的滑动让TopView显示的操作)。
  /**
     * 暴露接口供外部操作
     * */
    public interface OnLayoutScrollListener {
        abstract void isTopShow(boolean isTopShow);
    }

    /**
     *  初始化
     * */
    public void setOnListener(Fragment fragment) {
        scroll = (OnLayoutScrollListener) fragment;
    }

在StickyNavLayout的onNestedPreScroll方法里判断TopView的状态

 @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
        boolean hiddenTop = dy > 0 && getScrollY() < mTopViewHeight;
        showTop = dy < 0 && getScrollY() >= 0 && !ViewCompat.canScrollVertically(target, -1);
        if (getScrollY() > 0)
            scroll.isTopShow(false);//头部view处于隐藏状态
        else scroll.isTopShow(true);//头部view处于显示状态
        if (hiddenTop || showTop) {
            scrollBy(0, dy);
            consumed[1] = dy;
        }

    }
  • 在ViewPager操作类里实现StickyNavLayout.OnLayoutScrollListener接口实现isTopShow方法
  @Override
    public void isTopShow(boolean isTopShow) {
        isShowTop = isTopShow;
        //获取当前显示的Fragment
        pagerAdapter.getCurrentFragment().refresh(isTopShow);
    }

在Fragment类里

  /*是否可以刷新数据*/
    public void refresh(boolean isShowTop) {
        if (isShowTop)
            mRefreshLayout.setPullDownRefreshEnable(true);//头部view处于显示状态可刷新
        else mRefreshLayout.setPullDownRefreshEnable(false);//反之。。。
    }

5.解决ViewPager切选项卡时,Fragment里列表的位置紊乱问题,实现目标:当切换ViewPager选项卡时,头部View处于显示状态时,Fragment里列表应该位于头部。

/**
* 切选项卡时
*/
 indicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            public void onPageSelected(int arg0) {
               // 获取当前显示着的Fragment,当TopView处于显示时,让列表滚到头部
                FragmentStatePagerAdapter f = (FragmentStatePagerAdapter) pager.getAdapter();
                CommunityFragment fragment = (CommunityFragment) f.instantiateItem(pager, arg0);
                fragment.scrollTo(isShowTop);
                fragment.refresh(isShowTop);
            }

            public void onPageScrolled(int arg0, float arg1, int arg2) {

            }

            public void onPageScrollStateChanged(int arg0) {

            }
        });

Fragment里:

 public void scrollTo(boolean isShowTop) {
        if (isShowTop)
            mRvNews.scrollToPosition(0);

    }

相关文章

网友评论

      本文标题:ViewPager跟随整个页面向上移动,并固定头部效果实现记录

      本文链接:https://www.haomeiwen.com/subject/ahwrzxtx.html