我拆了个轮子--ANDROID WHEEL的实现(二)

第一篇搞定了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,UI)