1、序言
现在很多App为了让一个页面可以有更多展示的东西。于是乎有一个界面就有几个tab进行切换页面,同时滚动的时候为了方便用户切换tab,这时tab需要悬浮在布局的顶部。所以这样就有了这篇blog咯…….
2、实现原理
控件的实现原理,相对来还是比较简单的:
1、首先自定义一个GroupView,实现滑动的效果,同时进行一些判断,比如:当满足一些条件时,把事件处理交给ChildView来处理;当ChildView满足一些条件时(比如ListView滚动到了第一条数据,ScrollView滚动到了顶部),让GroupView滚动,ChildView停止滚动。
2、然后自定义一个ChildView,这个可以是ListView、ScrollView等等可滚动的控件,重写onTouchEvent方法,进行判断查看是否可以滚动,因为是否可以滚动是由GroupView来控制的。
3、通过接口的方式把两者之间判断是否可以滚动联系起来。
3、实现代码
看逻辑不清楚可以跳过直接看代码:
首先是GroupView的代码:
public class SideGroupLayout extends ViewGroup {
public static final String TAG = "android_xw";
private int mTouchSlop;
private float mLastMotionX;
private float mLastMotionY;
private boolean mIsBeingDragged;
protected int mFirstItemHeight;
private int mScrollY;
public boolean mScrollToEnd;
private VelocityTracker mVelocityTracker;
private int mMinimumFlingVelocity;
private int mMaximumFlingVelocity;
private Scroller mScroller;
private boolean mCanScroller;
public SideGroupLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mCanScroller = true;
ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity();
mScroller = new Scroller(context);
reset();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
height += child.getMeasuredHeight();
}
}
setMeasuredDimension(width, height);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int height = 0;
mFirstItemHeight = 0;
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
if (view.getVisibility() != View.GONE) {
view.layout(0, height, getWidth(), height + view.getMeasuredHeight());
height += view.getMeasuredHeight();
if (i == 0) {
mFirstItemHeight = height;
}
}
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mFirstItemHeight == 0 || !mCanScroller) {
mScrollToEnd = true;
return super.onInterceptTouchEvent(ev);
}
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) {
return true;
}
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
final float x = ev.getX();
final float y = ev.getY();
final int xDiff = (int) Math.abs(x - mLastMotionX);
final int yDiff = (int) Math.abs(y - mLastMotionY);
// Log.i("TAG", "mScrollY == mFirstItemHeight:" + (mScrollY ==
// mFirstItemHeight));
if (mScrollY == mFirstItemHeight) {
boolean isScrollY = yDiff > xDiff && y > mLastMotionY && mAction != null && mAction.isGroupScroll();
return isScrollY;
} else if (yDiff > mTouchSlop * 2 && yDiff >= xDiff) {
mIsBeingDragged = true;
mLastMotionY = y;
}
break;
}
case MotionEvent.ACTION_DOWN: {
mLastMotionX = ev.getX();
mLastMotionY = ev.getY();
mIsBeingDragged = false;
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mIsBeingDragged = false;
break;
}
return mIsBeingDragged;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mFirstItemHeight == 0 || !mCanScroller) {
mScrollToEnd = true;
return super.onTouchEvent(event);
}
addVelocityTracker(event);
final int action = event.getAction();
final float y = event.getY();
final float x = event.getX();
switch (action) {
case MotionEvent.ACTION_DOWN:
// 获取相对屏幕的坐标,即以屏幕左上角为原点
break;
case MotionEvent.ACTION_MOVE:
final float scrollX = mLastMotionX - x;
final float scrollY = mLastMotionY - y;
onScroll((int) scrollX, (int) scrollY);
scrollTo(