首先感谢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不会响应。