首先感谢http://blog.csdn.net/qiushi_1990/article/details/50288131的代码,这里我对代码进行了封装和完善(demo免费下载),处理了更多可能出现的场景,使得该组件更方便使用,最后会给出使用方法和注意事项,封装后源码如下:
package com.stickynavlistview.view; import android.content.Context; 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.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.stickynavlistview.R; public class StickyNavLayout extends LinearLayout { 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 isInControl = false; public StickyNavLayout(Context context, AttributeSet attrs) { super(context, attrs); setOrientation(LinearLayout.VERTICAL); mScroller = new OverScroller(context); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mMaximumVelocity = ViewConfiguration.get(context) .getScaledMaximumFlingVelocity(); mMinimumVelocity = ViewConfiguration.get(context) .getScaledMinimumFlingVelocity(); } @Override protected void onFinishInflate() { super.onFinishInflate(); if(getChildCount()!=3){ throw new RuntimeException("should include 3 child and the third view show be ViewPager"); } mTop = getChildAt(0); mNav = getChildAt(1); View view = getChildAt(2); if (!(view instanceof ViewPager)) { throw new RuntimeException( "the third view show be ViewPager !"); } mViewPager = (ViewPager) view; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); ViewGroup.LayoutParams params = mViewPager.getLayoutParams(); params.height = getMeasuredHeight() - mNav.getMeasuredHeight(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mTopViewHeight = mTop.getMeasuredHeight(); } @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); 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); return dispatchTouchEvent(ev2); } } break; } return super.dispatchTouchEvent(ev); } /** * */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { final 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==null){ return true; } else 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()); // 如果topView没有隐藏 // 或sc的listView在顶部 && topView隐藏 && 下拉,则拦截 if (!isTopHidden || // (c != null // && c.getTop() == 0// && 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 = (Fragment) fadapter.instantiateItem(mViewPager, currentItem); View view = item.getView(); if(view instanceof ListView || view instanceof ScrollView){ mInnerScrollView = (ViewGroup)view; }else{ if(((ViewGroup)view).getChildAt(0) instanceof ViewGroup){ mInnerScrollView = (ViewGroup) ((ViewGroup)view).getChildAt(0); } else{ mInnerScrollView = null; } } } else if (a instanceof FragmentStatePagerAdapter) { FragmentStatePagerAdapter fsAdapter = (FragmentStatePagerAdapter) a; Fragment item = (Fragment) fsAdapter.instantiateItem(mViewPager, currentItem); View view = item.getView(); if(view instanceof ListView || view instanceof ScrollView){ mInnerScrollView = (ViewGroup)view; }else{ if(((ViewGroup)view).getChildAt(0) instanceof ViewGroup){ mInnerScrollView = (ViewGroup) ((ViewGroup)view).getChildAt(0); } else{ mInnerScrollView = null; } } } } @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; Log.e("TAG", "dy = " + dy + " , y = " + y + " , mLastY = " + 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; } } 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; } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(0, mScroller.getCurrY()); // invalidate(); } } private void initVelocityTrackerIfNotExists() { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } } private void recycleVelocityTracker() { if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } } } 使用方式:首先是布局文件,源码如下:
<com.stickynavlistview.view.StickyNavLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <RelativeLayout android:id="@id/id_stickynavlayout_topview" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#4400ff00"> <TextView android:layout_width="match_parent" android:layout_height="240dp" android:gravity="center" android:text="软件介绍" android:textSize="30sp" android:textStyle="bold"/> </RelativeLayout> <com.stickynavlistview.view.SimpleViewPagerIndicator android:id="@id/id_stickynavlayout_indicator" android:layout_width="match_parent" android:layout_height="50dp" android:background="#ffffffff"> </com.stickynavlistview.view.SimpleViewPagerIndicator> <android.support.v4.view.ViewPager android:id="@id/id_stickynavlayout_viewpager" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#44ff0000"> </android.support.v4.view.ViewPager> </com.stickynavlistview.view.StickyNavLayout>注意3点,一个是该组件必须包含三个child,如果数量不对,会抛出异常,另一点是第三个child必须为viewpager,否则也会抛出异常,最后一点就是第一二个child的高度最好写死。在activity中注意2点,1.viewpager的适配器请选择FragmentPagerAdapter或者FragmentPagerAdapter中的一种。 2.fragment的布局文件如果包含listview或者scrollview中的一种,仅支持最外层使用或者嵌套在第一层的第一个child,其它情况会出现滑动问题,listview或者scrollview不会响应。