package com.yuyh.sprintnba.widget; import android.content.Context; import android.content.res.TypedArray; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.OverScroller; import android.widget.ScrollView; import com.yuyh.sprintnba.R; import com.yuyh.sprintnba.base.BaseFragment; import com.yuyh.library.view.viewpager.indicator.FragmentListPageAdapter; public class StickyNavLayout extends LinearLayout { private static final String TAG = "StickyNavLayout"; private View mTop; private View mNav; private ViewPager mViewPager; private int mTopViewHeight; private ViewGroup mInnerScrollView; private boolean isTopHidden = false; private OverScroller mScroller; private VelocityTracker mVelocityTracker; private int mTouchSlop; private int mMaximumVelocity, mMinimumVelocity; private float mLastY; private boolean mDragging; private boolean isStickNav; private boolean isInControl = false; private int stickOffset; private int mViewPagerMaxHeight; private int mTopViewMaxHeight; public StickyNavLayout(Context context) { this(context, null); } public StickyNavLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public StickyNavLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setOrientation(LinearLayout.VERTICAL); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.StickNavLayout); isStickNav = a.getBoolean(R.styleable.StickNavLayout_isStickNav, false); stickOffset = a.getDimensionPixelSize(R.styleable.StickNavLayout_stickOffset, 0); a.recycle(); mScroller = new OverScroller(context); mVelocityTracker = VelocityTracker.obtain(); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mMaximumVelocity = ViewConfiguration.get(context) .getScaledMaximumFlingVelocity(); mMinimumVelocity = ViewConfiguration.get(context) .getScaledMinimumFlingVelocity(); } public void setIsStickNav(boolean isStickNav) { this.isStickNav = isStickNav; } /** * 设置悬浮,并自动滚动到悬浮位置(即把top区域滚动上去) */ public void setStickNavAndScrollToNav() { this.isStickNav = true; scrollTo(0, mTopViewHeight); } /**** * 设置顶部区域的高度 * * @param height height */ public void setTopViewHeight(int height) { mTopViewHeight = height; if (isStickNav) scrollTo(0, mTopViewHeight); } /**** * 设置顶部区域的高度 * * @param height height * @param offset offset */ public void setTopViewHeight(int height, int offset) { mTopViewHeight = height; if (isStickNav) scrollTo(0, mTopViewHeight - offset); } @Override protected void onFinishInflate() { super.onFinishInflate(); mTop = findViewById(R.id.snlTopview); mNav = findViewById(R.id.snlIindicator); View view = findViewById(R.id.snlViewPager); if (!(view instanceof ViewPager)) { throw new RuntimeException( "id_stickynavlayout_viewpager show used by ViewPager !"); } else if (mTop instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) mTop; if (viewGroup.getChildCount() >= 2) { throw new RuntimeException( "if the TopView(android:id=\"R.id.id_stickynavlayout_topview\") is a ViewGroup(ScrollView,LinearLayout,FrameLayout, ....) ,the children count should be one !"); } } mViewPager = (ViewPager) view; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); ViewGroup.LayoutParams params = mViewPager.getLayoutParams(); //修复键盘弹出后键盘关闭布局高度不对问题 int height = getMeasuredHeight() - mNav.getMeasuredHeight(); mViewPagerMaxHeight = (height >= mViewPagerMaxHeight ? height : mViewPagerMaxHeight); params.height = /*mViewPagerMaxHeight - stickOffset*/height; mViewPager.setLayoutParams(params); //修复键盘弹出后Top高度不对问题 int topHeight = mTop.getMeasuredHeight(); ViewGroup.LayoutParams topParams = mTop.getLayoutParams(); mTopViewMaxHeight = (topHeight >= mTopViewMaxHeight ? topHeight : mTopViewMaxHeight); topParams.height = /*mTopViewMaxHeight*/topHeight; mTop.setLayoutParams(topParams); //设置mTopViewHeight mTopViewHeight = topParams.height; } /** * 更新top区域的视图,如果是处于悬浮状态,隐藏top区域的控件是不起作用的!! */ public void updateTopViews() { if (isTopHidden) { return; } final ViewGroup.LayoutParams params = mTop.getLayoutParams(); mTop.post(new Runnable() { @Override public void run() { if (mTop instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) mTop; int height = viewGroup.getChildAt(0).getHeight(); mTopViewHeight = height - stickOffset; params.height = height; mTop.setLayoutParams(params); params.height = ViewGroup.LayoutParams.WRAP_CONTENT; } else { mTopViewHeight = mTop.getMeasuredHeight() - stickOffset; } } }); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); final ViewGroup.LayoutParams params = mTop.getLayoutParams(); Log.d(TAG, "onSizeChanged-mTopViewHeight:" + mTopViewHeight); mTop.post(new Runnable() { @Override public void run() { if (mTop instanceof ViewGroup) { ViewGroup viewGroup = (ViewGroup) mTop; int height = viewGroup.getChildAt(0).getHeight(); mTopViewHeight = height - stickOffset; params.height = height; mTop.setLayoutParams(params); mTop.requestLayout(); } else { mTopViewHeight = mTop.getMeasuredHeight() - stickOffset; } Log.d(TAG, "mTopViewHeight:" + mTopViewHeight); if (null != mInnerScrollView) { Log.d(TAG, "mInnerScrollViewHeight:" + mInnerScrollView.getMeasuredHeight()); } if (isStickNav) { scrollTo(0, mTopViewHeight); } } }); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { int action = ev.getAction(); float y = ev.getY(); switch (action) { case MotionEvent.ACTION_DOWN: mLastY = y; break; case MotionEvent.ACTION_MOVE: float dy = y - mLastY; getCurrentScrollView(); if (mInnerScrollView instanceof ScrollView) { if (mInnerScrollView.getScrollY() == 0 && isTopHidden && dy > 0 && !isInControl) { isInControl = true; ev.setAction(MotionEvent.ACTION_CANCEL); MotionEvent ev2 = MotionEvent.obtain(ev); dispatchTouchEvent(ev); ev2.setAction(MotionEvent.ACTION_DOWN); isSticky = true; return dispatchTouchEvent(ev2); } } else if (mInnerScrollView instanceof ListView) { ListView lv = (ListView) mInnerScrollView; View c = lv.getChildAt(lv.getFirstVisiblePosition()); if (!isInControl && c != null && c.getTop() == 0 && isTopHidden && dy > 0) { isInControl = true; ev.setAction(MotionEvent.ACTION_CANCEL); MotionEvent ev2 = MotionEvent.obtain(ev); dispatchTouchEvent(ev); ev2.setAction(MotionEvent.ACTION_DOWN); isSticky = true; return dispatchTouchEvent(ev2); } } else if (mInnerScrollView instanceof RecyclerView) { RecyclerView rv = (RecyclerView) mInnerScrollView; if (!isInControl && android.support.v4.view.ViewCompat.canScrollVertically(rv, -1) && isTopHidden && dy > 0) { isInControl = true; ev.setAction(MotionEvent.ACTION_CANCEL); MotionEvent ev2 = MotionEvent.obtain(ev); dispatchTouchEvent(ev); ev2.setAction(MotionEvent.ACTION_DOWN); isSticky = true; return dispatchTouchEvent(ev2); } } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP://处理悬停后立刻抬起的处理 float distance = y - mLastY; if (isSticky && /*distance==0.0f*/Math.abs(distance) <= mTouchSlop) { isSticky = false; return true; } else { isSticky = false; return super.dispatchTouchEvent(ev); } } return super.dispatchTouchEvent(ev); } private boolean isSticky;//mNav-view 是否悬停的标志 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { int action = ev.getAction(); float y = ev.getY(); switch (action) { case MotionEvent.ACTION_DOWN: mLastY = y; break; case MotionEvent.ACTION_MOVE: float dy = y - mLastY; getCurrentScrollView(); if (Math.abs(dy) > mTouchSlop) { mDragging = true; if (mInnerScrollView instanceof ScrollView) { // 如果topView没有隐藏 // 或sc的scrollY = 0 && topView隐藏 && 下拉,则拦截 if (!isTopHidden || (mInnerScrollView.getScrollY() == 0 && isTopHidden && dy > 0)) { initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(ev); mLastY = y; return true; } } else if (mInnerScrollView instanceof ListView) { ListView lv = (ListView) mInnerScrollView; View c = lv.getChildAt(lv.getFirstVisiblePosition()); if (!isTopHidden || (c != null && c.getTop() == 0 && isTopHidden && dy > 0)) { initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(ev); mLastY = y; return true; } else { if (lv.getAdapter() != null && lv.getAdapter().getCount() == 0) {//当ListView或ScrollView 没有数据为空时 initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(ev); mLastY = y; return true; } } } else if (mInnerScrollView instanceof RecyclerView) { RecyclerView rv = (RecyclerView) mInnerScrollView; if (!isTopHidden || (!android.support.v4.view.ViewCompat.canScrollVertically(rv, -1) && isTopHidden && dy > 0)) { initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(ev); mLastY = y; return true; } } } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: mDragging = false; recycleVelocityTracker(); break; } return super.onInterceptTouchEvent(ev); } private void getCurrentScrollView() { int currentItem = mViewPager.getCurrentItem(); PagerAdapter a = mViewPager.getAdapter(); if (a instanceof FragmentPagerAdapter) { FragmentPagerAdapter fadapter = (FragmentPagerAdapter) a; Fragment item = fadapter.getItem(currentItem); View v = item.getView(); if (v != null) { mInnerScrollView = (ViewGroup) (v.findViewById(R.id.snlScrollView)); } } else if (a instanceof FragmentStatePagerAdapter) { FragmentStatePagerAdapter fsAdapter = (FragmentStatePagerAdapter) a; Fragment item = fsAdapter.getItem(currentItem); View v = item.getView(); if (v != null) { mInnerScrollView = (ViewGroup) (v.findViewById(R.id.snlScrollView)); } } else if(a instanceof PagerAdapter){ FragmentListPageAdapter adapter = (FragmentListPageAdapter)a; BaseFragment item = (BaseFragment) adapter.getExitFragment(currentItem); View v = item.getContentView(); if (v != null) { mInnerScrollView = (ViewGroup) (v.findViewById(R.id.snlScrollView)); } } else { //throw new RuntimeException("mViewPager should be used FragmentPagerAdapter or FragmentStatePagerAdapter !"); } } @Override public boolean onTouchEvent(MotionEvent event) { initVelocityTrackerIfNotExists(); mVelocityTracker.addMovement(event); int action = event.getAction(); float y = event.getY(); switch (action) { case MotionEvent.ACTION_DOWN: if (!mScroller.isFinished()) mScroller.abortAnimation(); mLastY = y; return true; case MotionEvent.ACTION_MOVE: float dy = y - mLastY; if (!mDragging && Math.abs(dy) > mTouchSlop) { mDragging = true; } if (mDragging) { scrollBy(0, (int) -dy); // 如果topView隐藏,且上滑动时,则改变当前事件为ACTION_DOWN if (getScrollY() == mTopViewHeight && dy < 0) { event.setAction(MotionEvent.ACTION_DOWN); dispatchTouchEvent(event); isInControl = false; isSticky = true; } else { isSticky = false; } } mLastY = y; break; case MotionEvent.ACTION_CANCEL: mDragging = false; recycleVelocityTracker(); if (!mScroller.isFinished()) { mScroller.abortAnimation(); } break; case MotionEvent.ACTION_UP: mDragging = false; mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int velocityY = (int) mVelocityTracker.getYVelocity(); if (Math.abs(velocityY) > mMinimumVelocity) { fling(-velocityY); } recycleVelocityTracker(); break; } return super.onTouchEvent(event); } public void fling(int velocityY) { mScroller.fling(0, getScrollY(), 0, velocityY, 0, 0, 0, mTopViewHeight); invalidate(); } @Override public void scrollTo(int x, int y) { if (y < 0) { y = 0; } if (y > mTopViewHeight) { y = mTopViewHeight; } if (y != getScrollY()) { super.scrollTo(x, y); } isTopHidden = getScrollY() == mTopViewHeight; //set listener 设置悬浮监听回调 if (listener != null) { // if(lastIsTopHidden!=isTopHidden){ // lastIsTopHidden=isTopHidden; listener.isStick(isTopHidden); // } listener.scrollPercent((float) getScrollY() / (float) mTopViewHeight); } } // private boolean lastIsTopHidden;//记录上次是否悬浮 @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(0, mScroller.getCurrY()); postInvalidate(); } } private void initVelocityTrackerIfNotExists() { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } } private void recycleVelocityTracker() { if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } } private OnStickStateChangeListener listener; /** * 悬浮状态回调 */ public interface OnStickStateChangeListener { /** * 是否悬浮的回调 * * @param isStick true 悬浮 ,false 没有悬浮 */ void isStick(boolean isStick); /** * 距离悬浮的距离的百分比 * * @param percent 0~1(向上) or 1~0(向下) 的浮点数 */ void scrollPercent(float percent); } public void setOnStickStateChangeListener(OnStickStateChangeListener listener) { this.listener = listener; } }