美文网首页
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