我拆了个轮子--ANDROID WHEEL的实现(二)
16lz
2021-01-26
第一篇搞定了UI,这一篇来处理下事件,ANDROID WHEEL有响应onscroll和onfling事件,这个该如何处理?这里有几个小知识点:
1.ontouchevent中调用GestureDetector.onTouchEvent(event)这个方法作为返回值,所以调用顺序就是ontouchevent–>onscroll或者ontouchevent–>onfling。
2.View.post(Runnable)也是修改UI的一种方式。
好了,上代码。
public class Wheel extends View implements GestureDetector.OnGestureListener,IFlingRunnable.FlingRunnableView { private int mTicksCount = 18; private float mTicksSize = 7.0f; private float mIndicatorX = 0; private Bitmap mIndicator; private Bitmap mTickBitmap; private Shader mShader3; private Matrix mDrawMatrix = new Matrix(); int mOriginalDeltaX = 0; private Paint mPaint; int mWidth, mHeight; float mTickSpace = 30; private Easing mTicksEasing = new Sine(); private GestureDetector mGestureDetector; private boolean mToLeft; private int mMaxX, mMinX; private int mWheelSizeFactor = 2; private IFlingRunnable mFlingRunnable; private int mAnimationDuration = 200; private boolean mIsFirstScroll; private int mTouchSlop;//这个值是判断是否为scroll的一个阈值,一般为8dp public Wheel(Context context) { this(context, null); } public Wheel(Context context, AttributeSet attrs) { this(context, attrs, 0); } public Wheel(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs, defStyleAttr); } public void init(Context context, AttributeSet attrs, int defStyleAttr) { Log.d("111", "wheel init"); //构造一个Runnable,VIEW.POST(Runnable)这个方法 mFlingRunnable = new Fling8Runnable( this, mAnimationDuration ); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Wheel, defStyleAttr, 0); mTicksCount = a.getInteger(R.styleable.Wheel_ticks, 18); mWheelSizeFactor = a.getInteger(R.styleable.Wheel_numRotations, 2); a.recycle(); mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); mGestureDetector = new GestureDetector(context, this); mGestureDetector.setIsLongpressEnabled(false); setFocusable(true); setFocusableInTouchMode(true); mTouchSlop = ViewConfiguration.get( context ).getScaledTouchSlop(); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); mWidth = right - left; mHeight = bottom - top; mTickSpace = (float) mWidth / mTicksCount; mTicksSize = mWidth / mTicksCount / 4.0f; mTicksSize = Math.min(Math.max(mTicksSize, 3.5f), 6.0f); mIndicatorX = (float) mWidth / 2.0f; mIndicator = makeBitmapIndicator((int) Math.ceil(mTicksSize), bottom - top); mTickBitmap = makeTickerBitmap((int) Math.ceil(mTicksSize), bottom - top); mShader3 = new BitmapShader(makeBitmap3(right - left, bottom - top), Shader.TileMode.CLAMP, Shader.TileMode.REPEAT); mMaxX = mWidth * mWheelSizeFactor; mMinX = -mMaxX; } private Bitmap makeBitmap3(int width, int height) { Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(bm); Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); int colors[] = {0xdd000000, 0x00000000, 0x00000000, 0xdd000000}; float positions[] = {0f, 0.2f, 0.8f, 1f}; LinearGradient gradient = new LinearGradient(0, 0, width, 0, colors, positions, Shader.TileMode.REPEAT); p.setShader(gradient); p.setDither(true); c.drawRect(0, 0, width, height, p); return bm; } @Override protected void onDraw(Canvas canvas) { final int w = mWidth; int total = mTicksCount; float x2; float scale, scale2; mPaint.setShader(null); for (int i = 0; i < total; i++) { float x = (mOriginalDeltaX + (((float) i / total) * w)); if (x < 0) { x = w - (-x % w); } else { x = x % w; } scale = (float) mTicksEasing.easeInOut(x, 0, 1.0, mWidth); scale2 = (float) (Math.sin(Math.PI * (x / mWidth))); mDrawMatrix.reset(); mDrawMatrix.setScale(scale2, 1); mDrawMatrix.postTranslate((int) (scale * mWidth) - (mTicksSize / 2), 0); canvas.drawBitmap(mTickBitmap, mDrawMatrix, mPaint); } float indicatorx = (mIndicatorX + mOriginalDeltaX); if (indicatorx < 0) { indicatorx = (mWidth * 2) - (-indicatorx % (mWidth * 2)); } else { indicatorx = indicatorx % (mWidth * 2); } if (indicatorx > 0 && indicatorx < mWidth) { x2 = (float) mTicksEasing.easeInOut(indicatorx, 0, mWidth, w); scale2 = (float) (Math.sin(Math.PI * (indicatorx / mWidth))); mDrawMatrix.reset(); mDrawMatrix.setScale(scale2, 1); mDrawMatrix.postTranslate(x2 - (mTicksSize / 2), 0); canvas.drawBitmap(mIndicator, mDrawMatrix, mPaint); } mPaint.setShader(mShader3); canvas.drawPaint(mPaint); } private Bitmap makeTickerBitmap(int width, int height) { float ellipse = width / 2; Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(bm); Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); p.setDither(true); p.setColor(0xFF888888); float h = (float) height; float y = (h + 10.0f) / 10.0f; float y2 = y * 2.5f; RectF rect = new RectF(0, y, width, height - y2); c.drawRoundRect(rect, ellipse, ellipse, p); p.setColor(0xFFFFFFFF); rect = new RectF(0, y2, width, height - y); c.drawRoundRect(rect, ellipse, ellipse, p); p.setColor(0xFFCCCCCC); rect = new RectF(0, y + 2, width, height - (y + 2)); c.drawRoundRect(rect, ellipse, ellipse, p); return bm; } private Bitmap makeBitmapIndicator(int width, int height) { float ellipse = width / 2; float h = (float) height; float y = (h + 10.0f) / 10.0f; float y2 = y * 2.5f; Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(bm); Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); p.setDither(true); p.setColor(0xFF666666); RectF rect = new RectF(0, y, width, height - y2); c.drawRoundRect(rect, ellipse, ellipse, p); p.setColor(0xFFFFFFFF); rect = new RectF(0, y2, width, height - y); c.drawRoundRect(rect, ellipse, ellipse, p); rect = new RectF(0, y + 2, width, height - (y + 2)); int colors[] = {0xFF0076E7, 0xFF00BBFF, 0xFF0076E7}; float positions[] = {0f, 0.5f, 1f}; LinearGradient gradient = new LinearGradient(0, 0, width, 0, colors, positions, Shader.TileMode.REPEAT); p.setShader(gradient); c.drawRoundRect(rect, ellipse, ellipse, p); return bm; } @Override public boolean onTouchEvent(MotionEvent event) { Log.d("111", "onTouchEvent"); return mGestureDetector.onTouchEvent(event); } @Override public boolean onDown(MotionEvent e) { //返回值为true mIsFirstScroll = true; return true; } @Override public void onShowPress(MotionEvent e) { } @Override public boolean onSingleTapUp(MotionEvent e) { return false; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { Log.d("111", "onScroll"); if ( mIsFirstScroll ) { if ( distanceX > 0 ) distanceX -= mTouchSlop; else distanceX += mTouchSlop; } mIsFirstScroll = false; float delta = -1 * distanceX; mToLeft = delta < 0; if (!mToLeft) { if (mOriginalDeltaX + delta > mMaxX) { delta /= (((float) mOriginalDeltaX + delta) - mMaxX) / 10; } } else { if (mOriginalDeltaX + delta < mMinX) { delta /= -(((float) mOriginalDeltaX + delta) - mMinX) / 10; } } trackMotionScroll((int) (mOriginalDeltaX + delta)); return true; } @Override public void onLongPress(MotionEvent e) { } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { Log.d("111", "onFling"); boolean toleft = velocityX < 0; if ( !toleft ) { if ( mOriginalDeltaX > mMaxX ) { mFlingRunnable.startUsingDistance( mOriginalDeltaX, mMaxX - mOriginalDeltaX ); return true; } } else { if ( mOriginalDeltaX < mMinX ) { mFlingRunnable.startUsingDistance( mOriginalDeltaX, mMinX - mOriginalDeltaX ); return true; } } mFlingRunnable.startUsingVelocity( mOriginalDeltaX, (int) velocityX / 2 ); return true; } @Override public void scrollIntoSlots() { } @Override public void trackMotionScroll(int newX) { mOriginalDeltaX = newX; invalidate(); } @Override public int getMinX() { return mMinX; } @Override public int getMaxX() { return mMaxX; }}
下面是IFlingRunnable
package thepoor.com.testmywheel;abstract class IFlingRunnable implements Runnable { public static interface FlingRunnableView { boolean removeCallbacks(Runnable action); boolean post(Runnable action); void scrollIntoSlots(); void trackMotionScroll(int newX); int getMinX(); int getMaxX(); } protected int mLastFlingX; protected boolean mShouldStopFling; protected FlingRunnableView mParent; protected int mAnimationDuration; protected static final String LOG_TAG = "fling"; public IFlingRunnable(FlingRunnableView parent, int animationDuration ) { mParent = parent; mAnimationDuration = animationDuration; } public int getLastFlingX() { return mLastFlingX; } protected void startCommon() { mParent.removeCallbacks( this ); } public void stop( boolean scrollIntoSlots ) { mParent.removeCallbacks( this ); endFling( scrollIntoSlots ); } public void startUsingDistance( int initialX, int distance ) { if ( distance == 0 ) return; startCommon(); mLastFlingX = initialX; _startUsingDistance( mLastFlingX, distance ); mParent.post( this ); } public void startUsingVelocity( int initialX, int initialVelocity ) { if ( initialVelocity == 0 ) return; startCommon(); mLastFlingX = initialX; _startUsingVelocity( mLastFlingX, initialVelocity ); mParent.post( this ); } protected void endFling( boolean scrollIntoSlots ) { forceFinished( true ); mLastFlingX = 0; if ( scrollIntoSlots ) { mParent.scrollIntoSlots(); } } @Override public void run() { mShouldStopFling = false; final boolean more = computeScrollOffset(); int x = getCurrX(); mParent.trackMotionScroll( x ); if ( more && !mShouldStopFling ) { mLastFlingX = x; mParent.post( this ); } else { endFling( true ); } } public abstract boolean springBack( int startX, int startY, int minX, int maxX, int minY, int maxY ); protected abstract boolean computeScrollOffset(); protected abstract int getCurrX(); public abstract float getCurrVelocity(); protected abstract void forceFinished( boolean finished ); protected abstract void _startUsingVelocity( int initialX, int velocity ); protected abstract void _startUsingDistance( int initialX, int distance ); public abstract boolean isFinished();}
下面是Fling8Runnable
package thepoor.com.testmywheel;import android.view.View;import android.view.animation.DecelerateInterpolator;import android.widget.Scroller;class Fling8Runnable extends IFlingRunnable { private Scroller mScroller; public Fling8Runnable(FlingRunnableView parent, int animationDuration ) { super( parent, animationDuration ); mScroller = new Scroller( ( (View) parent ).getContext(), new DecelerateInterpolator() ); } @Override public float getCurrVelocity() { return mScroller.getCurrVelocity(); } @Override public boolean isFinished() { return mScroller.isFinished(); } @Override protected void _startUsingVelocity( int initialX, int velocity ) { mScroller.fling( initialX, 0, velocity, 0, mParent.getMinX(), mParent.getMaxX(), 0, Integer.MAX_VALUE ); } @Override protected void _startUsingDistance( int initialX, int distance ) { mScroller.startScroll( initialX, 0, distance, 0, mAnimationDuration ); } @Override protected void forceFinished( boolean finished ) { mScroller.forceFinished( finished ); } @Override protected boolean computeScrollOffset() { return mScroller.computeScrollOffset(); } @Override protected int getCurrX() { return mScroller.getCurrX(); } @Override public boolean springBack( int startX, int startY, int minX, int maxX, int minY, int maxY ) { return false; }}
更多相关文章
- Android(安卓)dispatchTouchEvent介绍
- android 自定义log打印
- 上官网学android之五(Managing the Activity Lifecycle)
- android:用getIdentifier()获取资源Id
- Android(安卓)Sensor Framework(狠详)
- Android(安卓)API之onLayout, onMeasure
- android 模拟按键事件
- DialogFragment 报错汇总
- Android(安卓)UI开发详解之ActionBar