package yanzhikai.ruler.InnerRulers; import android.content.Context; import android.support.annotation.Px; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewGroup; import yanzhikai.ruler.BooheeRuler; /** * 垂直尺子抽象类 */ public abstract class VerticalRuler extends InnerRuler { private final String TAG = "ruler"; //记录落点 private float mLastY = 0; //一半高度 protected int mHalfHeight = 0; public VerticalRuler(Context context, BooheeRuler booheeRuler) { super(context, booheeRuler); } //处理滑动,主要是触摸的时候通过计算现在的event坐标和上一个的位移量来决定scrollBy()的多少 //滑动完之后计算速度是否满足Fling,满足则使用OverScroller来计算Fling滑动 @Override public boolean onTouchEvent(MotionEvent event) { float currentY = event.getY(); //开始速度检测 if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); ViewGroup parent = (ViewGroup) getParent();//为了解决刻度尺在scrollview这种布局里面滑动冲突问题 switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (!mOverScroller.isFinished()) { mOverScroller.abortAnimation(); } mLastY = currentY; parent.requestDisallowInterceptTouchEvent(true);//按下时开始让父控件不要处理任何touch事件 break; case MotionEvent.ACTION_MOVE: float moveY = mLastY - currentY; mLastY = currentY; scrollBy(0, (int) (moveY)); break; case MotionEvent.ACTION_UP: //处理松手后的Fling mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); int velocityY = (int) mVelocityTracker.getYVelocity(); if (Math.abs(velocityY) > mMinimumVelocity) { fling(-velocityY); } else { scrollBackToCurrentScale(); } //VelocityTracker回收 if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } releaseEdgeEffects(); parent.requestDisallowInterceptTouchEvent(false);//按下时开始让父控件不要处理任何touch事件 break; case MotionEvent.ACTION_CANCEL: if (!mOverScroller.isFinished()) { mOverScroller.abortAnimation(); } scrollBackToCurrentScale(); //VelocityTracker回收 if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } releaseEdgeEffects(); parent.requestDisallowInterceptTouchEvent(false);//按下时开始让父控件不要处理任何touch事件 break; } return true; } private void fling(int vY) { mOverScroller.fling(0, getScrollY(), 0, vY, 0, 0, mMinPosition - mEdgeLength, mMaxPosition + mEdgeLength); invalidate(); } //重写滑动方法,设置到边界的时候不滑。滑动完输出刻度 @Override public void scrollTo(@Px int x, @Px int y) { if (y < mMinPosition) { goStartEdgeEffect(y); y = mMinPosition; } if (y > mMaxPosition) { goEndEdgeEffect(y); y = mMaxPosition; } if (y != getScrollY()) { super.scrollTo(x, y); } mCurrentScale = scrollYtoScale(y); if (mRulerCallback != null) { mRulerCallback.onScaleChanging(Math.round(mCurrentScale)); } } //头部边缘效果处理 private void goStartEdgeEffect(int y) { if (mParent.canEdgeEffect()) { if (!mOverScroller.isFinished()) { mStartEdgeEffect.onAbsorb((int) mOverScroller.getCurrVelocity()); mOverScroller.abortAnimation(); } else { mStartEdgeEffect.onPull((float) (mMinPosition - y) / (mEdgeLength) * 3 + 0.3f); mStartEdgeEffect.setSize(mParent.getCursorWidth(), getHeight()); } postInvalidateOnAnimation(); } } //尾部边缘效果处理 private void goEndEdgeEffect(int y) { if (mParent.canEdgeEffect()) { if (!mOverScroller.isFinished()) { mEndEdgeEffect.onAbsorb((int) mOverScroller.getCurrVelocity()); mOverScroller.abortAnimation(); } else { mEndEdgeEffect.onPull((float) (y - mMaxPosition) / (mEdgeLength) * 3 + 0.3f); mEndEdgeEffect.setSize(mParent.getCursorWidth(), getHeight()); } postInvalidateOnAnimation(); } } //取消边缘效果动画 private void releaseEdgeEffects() { if (mParent.canEdgeEffect()) { mStartEdgeEffect.onRelease(); mEndEdgeEffect.onRelease(); } } //直接跳转到当前刻度 public void goToScale(float scale) { mCurrentScale = Math.round(scale); scrollTo(0, scaleToScrollY(mCurrentScale)); // if (mRulerCallback != null) { // mRulerCallback.onScaleChanging(mCurrentScale); // } } //把滑动偏移量scrollY转化为刻度Scale private float scrollYtoScale(int scrollY) { return ((float) (scrollY - mMinPosition) / mLength) * mMaxLength + mParent.getMinScale(); } //把Scale转化为ScrollY private int scaleToScrollY(float scale) { return Math.round((scale - mParent.getMinScale()) / mMaxLength * mLength + mMinPosition); } //把Scale转化为ScrollY,放大SCALE_TO_PX_FACTOR倍,以免精度丢失问题 private float scaleToScrollFloatY(float scale) { return ((scale - mParent.getMinScale()) / mMaxLength * mLength * SCALE_TO_PX_FACTOR + mMinPosition * SCALE_TO_PX_FACTOR); } //把移动后光标对准距离最近的刻度,就是回弹到最近刻度 @Override protected void scrollBackToCurrentScale() { scrollBackToCurrentScale(Math.round(mCurrentScale)); } @Override protected void scrollBackToCurrentScale(int currentIntScale) { int dy = Math.round((scaleToScrollFloatY(currentIntScale) - SCALE_TO_PX_FACTOR * getScrollY()) / SCALE_TO_PX_FACTOR); if (dy > minScrollerPx) { //渐变回弹 mOverScroller.startScroll(getScrollX(), getScrollY(), 0, dy, 500); invalidate(); } else { //立刻回弹 scrollBy(0, dy); } } @Override public void refreshSize() { mLength = (mParent.getMaxScale() - mParent.getMinScale()) * mParent.getInterval(); mHalfHeight = getHeight() / 2; mMinPosition = -mHalfHeight; mMaxPosition = mLength - mHalfHeight; } //获取控件宽高,设置相应信息 @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); refreshSize(); } }