/** * Created by Venn on 2016/4/11. * 水平滑動的ScrollView,內部可嵌套豎直滑動的佈局(ListView,ScrollView等) */ public class HorizontalScrollView extends ViewGroup { private Context mContext; private Scroller mScroller; private VelocityTracker mTracker; private int mLastX; private int mLastY; private int mInterceptedX; private int mInterceptedY; private int currentChildIndex; public HorizontalScrollView(Context context) { this(context, null); } public HorizontalScrollView(Context context, AttributeSet attrs) { super(context, attrs); this.mContext = context; init(); } public void init() { if (mScroller == null) { mScroller = new Scroller(mContext); } mTracker = VelocityTracker.obtain(); } //1 測量過程 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); measureChildren(widthMeasureSpec, heightMeasureSpec); final int childCount = getChildCount(); //child 的getLayoutParams()得到的爲parent裏面的 ViewGroup.LayoutParams params = getLayoutParams(); int defaultWidth = params.width; int childTotalWidth = 0; int defaultHeight = params.height; int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if (child != null) { LayoutParams marginParams = (LayoutParams) child.getLayoutParams(); int childHeight = child.getMeasuredHeight() + child.getPaddingTop() + child .getPaddingBottom() + marginParams.leftMargin + marginParams.rightMargin + getPaddingTop() + getPaddingBottom(); defaultHeight = Math.max(defaultHeight, childHeight); int childWidth = child.getMeasuredWidth() + child.getPaddingLeft() + marginParams .leftMargin + marginParams.rightMargin + child .getPaddingRight() + getPaddingLeft() + getPaddingRight(); childTotalWidth += childWidth; } } defaultWidth = Math.max(defaultWidth, childTotalWidth); if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) { setMeasuredDimension(defaultWidth, defaultHeight); } else if (widthMode == MeasureSpec.AT_MOST) { setMeasuredDimension(defaultWidth, heightSize); } else if (heightMode == MeasureSpec.AT_MOST) { setMeasuredDimension(widthSize, defaultHeight); } } //2 佈局過程 @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int childCount = getChildCount(); int childLeft = getPaddingLeft(); int childTop = getPaddingTop(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if (child != null && child.getVisibility() != View.GONE) { int childMeasuredWidth = child.getMeasuredWidth(); LayoutParams marginParams = (LayoutParams) child.getLayoutParams(); childLeft += marginParams.leftMargin; child.layout(childLeft, childTop + marginParams.topMargin, childLeft + childMeasuredWidth, childTop + marginParams.topMargin + child.getMeasuredHeight()); childLeft += childMeasuredWidth + marginParams .rightMargin; } } } //3 繪製過程 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); } //4 事件分發過程 @Override public boolean dispatchTouchEvent(MotionEvent ev) { return super.dispatchTouchEvent(ev); } //5 事件攔截過程 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean intercepted = false; int x = (int) ev.getX(); int y = (int) ev.getY(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: intercepted = false; if (!mScroller.isFinished()) { mScroller.abortAnimation(); intercepted = true; } break; case MotionEvent.ACTION_MOVE: if (Math.abs(x - mInterceptedX) > Math.abs(y - mInterceptedY)) { intercepted = true; } else { intercepted = false; } break; case MotionEvent.ACTION_UP: intercepted = false; break; default: break; } mLastX = x; mLastY = y; mInterceptedX = x; mInterceptedY = y; return intercepted; } //6 事件處理過程 @Override public boolean onTouchEvent(MotionEvent event) { int x = (int) event.getX(); int y = (int) event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (!mScroller.isFinished()) { mScroller.abortAnimation(); } break; case MotionEvent.ACTION_MOVE: int dX = x - mLastX; int dY = y - mLastY; scrollBy(-dX, 0); currentChildIndex = dX / 30; break; case MotionEvent.ACTION_UP: //使用彈性滑動,使得手指擡起後自動滑動到當前child int scrollX = getScrollX(); mTracker.computeCurrentVelocity(1000); int xVelocity = (int) mTracker.getXVelocity(); if (Math.abs(xVelocity) > 50) { currentChildIndex = xVelocity > 0 ? currentChildIndex-- : currentChildIndex++; } smoothScrollBy(-(30 * currentChildIndex - scrollX), 0, 3000); mTracker.clear(); break; } mLastX = x; mLastY = y; mTracker.clear(); return true; } //7 對View移除的處理 @Override protected void onDetachedFromWindow() { mTracker.recycle(); super.onDetachedFromWindow(); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } } //水平彈性滑動 private void smoothScrollBy(int dX, int dY, int duration) { mScroller.startScroll(mScroller.getStartX(), 0, dX, 0, duration); invalidate(); } //重載使得margin有效 @Override public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new HorizontalScrollView.LayoutParams(mContext, attrs); } //需要重寫layoutParams類使得支持當前ViewGroup的LayoutParams public static class LayoutParams extends MarginLayoutParams { public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); } } }
自定義View,包括事件分發,滑動衝突,測量以及佈局
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.