实现ScrollView的效果,且滚动带有“黏性”,滑动距离大于控件高度(屏幕高度)的1/3时,会自动滚动到上一个(下一个)控件
public class DiyViewGroup extends ViewGroup {
private int mScreenHeight;
private int mLastY;
private Scroller mScroller;
private int mStart;
private int mEnd;
public DiyViewGroup(Context context) {
super(context);
init(context);
}
public DiyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public DiyViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
// 初始化mScreenHeight和mScroller
private void init(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
mScreenHeight = outMetrics.heightPixels;
mScroller = new Scroller(context);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
// 对子View进行布局
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if(child.getVisibility() != GONE) {
child.layout(l, mScreenHeight * i, r, mScreenHeight * (i + 1));
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int count = getChildCount();
// 通知子View对自身进行测量
for (int i = 0; i <count ; i++) {
View childView = getChildAt(i);
// 可能子View的宽高是match_parent,所以要将父控件的宽高传进去
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
}
// 设置ViewGroup自己的宽高
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = mScreenHeight * count;
setMeasuredDimension(width, height);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int y = (int) event.getY();
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i("qqq", "onTouchEvent: down");
mLastY = y;
// 记录触摸起点
// getScrollY()从最原始位置向上滚动的距离
mStart = getScrollY();
break;
case MotionEvent.ACTION_MOVE:
Log.i("qqq", "onTouchEvent: move");
if(!mScroller.isFinished()) {
// 停止动画,立即移动到终点位置
mScroller.abortAnimation();
}
int dy = mLastY - y;
if(getScrollY() < 0) {
dy = 0;
}
if(getScrollY() + mScreenHeight > getHeight()) {
dy = 0;
}
scrollBy(0, dy);
mLastY = y;
break;
case MotionEvent.ACTION_UP:
Log.i("qqq", "onTouchEvent: up");
// 记录触摸终点
mEnd = getScrollY();
int dScrollY = mEnd - mStart;
if(dScrollY > 0) {
if(dScrollY < mScreenHeight / 3) {
// 第三个参数第四个参数分别是向左移动的距离和向上移动的距离
mScroller.startScroll(0, getScrollY(), 0, -dScrollY);
} else {
mScroller.startScroll(0, getScrollY(), 0, mScreenHeight - dScrollY);
}
} else {
if(-dScrollY < mScreenHeight / 3) {
mScroller.startScroll(0, getScrollY(), 0, -dScrollY);
} else {
mScroller.startScroll(0, getScrollY(), 0, -(mScreenHeight - (-dScrollY)));
}
}
break;
}
postInvalidate();
return true;
}
@Override
public void computeScroll() {
super.computeScroll();
if(mScroller.computeScrollOffset()) {
scrollTo(0, mScroller.getCurrY());
postInvalidate();
}
}
}