/** * 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,包括事件分发,滑动冲突,测量以及布局
最新推荐文章于 2021-09-10 14:21:43 发布