1. 程式人生 > >Android 自定義控制元件之命運之輪(抽獎轉盤)

Android 自定義控制元件之命運之輪(抽獎轉盤)

1 思路

首先肯定是要繪製扇形的,每一個獎品為一個扇形區分開,然後在扇形中得有當前獎品的說明,最後讓這個輪盤轉起來就行了。說起來很簡單,但是在繪製的時候,特別是繪製文字的時候還有有一些細節需要注意的,也不是難點,只是要理清楚那些地方應該怎麼去畫,怎麼獲取需要繪製的座標。

 

2 繪製扇形

首先是一些初始化工作:

public class WheelOfFortuneView extends View {
    private static final String TAG = "WheelOfFortuneView";
    private int mWidth; //控制元件寬,為螢幕寬和高的較小值
    private int mHeight;    //控制元件高,為螢幕寬和高的較小值
    private Paint mPaint;   //畫筆
    private TextPaint mTextPaint;   //繪製文字的畫筆
    private RectF mRectF;   //扇形繪製區域
    private Path mPath; //路徑
    private PathMeasure mPathMeasure;   //路徑測量的物件
    private Rect mTextBounds;   //文字繪製區域
    private float[] pos = new float[2]; //記錄路徑中某個點的座標陣列
    private float[] tan = new float[2];

    private List<Prize> mPrizeList = new ArrayList<>();

    public WheelOfFortuneView(Context context) {
        this(context, null);
    }

    public WheelOfFortuneView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public WheelOfFortuneView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);

        mTextPaint = new TextPaint();
        mTextPaint.setAntiAlias(true);

        mRectF = new RectF();
        mPath = new Path();
        mPathMeasure = new PathMeasure();
        mTextBounds = new Rect();

        mPrizeList.add(new Prize("一等獎", Color.RED));
        mPrizeList.add(new Prize("二等獎", Color.YELLOW));
        mPrizeList.add(new Prize("三等獎", Color.BLUE));
        mPrizeList.add(new Prize("謝謝你", Color.GREEN));
        mPrizeList.add(new Prize("一等獎", Color.RED));
        mPrizeList.add(new Prize("二等獎", Color.YELLOW));
        mPrizeList.add(new Prize("三等獎", Color.BLUE));
        mPrizeList.add(new Prize("謝謝你", Color.GREEN));
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = mHeight = Math.min(getContext().getResources().getDisplayMetrics().widthPixels, getContext().getResources().getDisplayMetrics().heightPixels);
        setMeasuredDimension(mWidth, mHeight);

        //繪製扇形的區域
        mRectF.set(0, 0, mWidth, mHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }

    private static int dp2px(Context context, float dpValue) {
        float density = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * density + 0.5f);
    }

    private static int sp2px(Context context, float spValue) {
        float scaleDensity = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * scaleDensity + 0.5f);
    }

    public static class Prize {
        private String text;
        private int backgroundColor;

        public Prize() {
        }

        public Prize(String text, int backgroundColor) {
            this.text = text;
            this.backgroundColor = backgroundColor;
        }

        public String getText() {
            return text;
        }


        public int getBackgroundColor() {
            return backgroundColor;
        }
    }
}

 

在 View 的構造方法中初始化了一些畫筆、測量範圍的矩形、Path 和 PathMeasure 等,然後在 onMeasure() 方法中目前是將寬高現在寫死了,為螢幕寬高中的較小值,最後定義了一個簡單的獎品內部類,並且構造方法中添加了一些預設的獎品。基本準備工作就是這些,後面寫的時候需要新增什麼再新增好了。接下來就可以開始拿起畫筆愉快的畫這個命運之輪了。

 

根據獎品個數,計算一下扇形掃過的角度,來個 for 迴圈就可以將每個獎品對應的扇形繪製出來了:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < mPrizeList.size(); i++) {
            //繪製扇形
            mPaint.setColor(mPrizeList.get(i).getBackgroundColor());
            //計算每個扇形開始的角度,Android 中在繪製扇形時從順時針 90 度開始繪製,所以起始角度要減 90
            float startAngle = (float) 360 / (float) mPrizeList.size() * i - 90;
            float sweepAngle = (float) 360 / (float) mPrizeList.size();
            canvas.drawArc(mRectF, startAngle, sweepAngle, true, mPaint);
        }
    }

 

 

3 繪製文字

接下來需要在扇形中繪製獎品的說明文字,剛開始以為這個很簡單,以為找到扇形中的中間位置的座標直接  drawText() 就行了,但是繪製文字沒有改變文字方向的 API,所以不能直接繪製,但是 Canvas 這樣一個 API:

public void drawTextOnPath(@NonNull String text, @NonNull Path path, float hOffset, float vOffset, @NonNull Paint paint)

它可以沿著指定 path 來繪製文字,也就可以改變文字方向了。在最後思考後,我的思路是這樣的:

 

在剛才繪製扇形下面加入繪製文字的程式碼:

            //處理文字,因為想豎著繪製文字,一個字一個字的話
            String text = mPrizeList.get(i).getText();
            text = new StringBuilder(text).reverse().toString();
            for (int j = 0; j < text.length(); j++) {
                //重置 path,首先獲取扇形的弧形部分的路徑
                mPath.reset();
                mPath.addArc(mRectF, startAngle, sweepAngle);
                //獲取扇形路徑的起點
                mPathMeasure.setPath(mPath, false);
                mPathMeasure.getPosTan(0, pos, tan);
                //重置 path,連線圓心和扇形路徑的起點
                mPath.reset();
                mPath.moveTo(mWidth / 2, mHeight / 2);
                mPath.lineTo(pos[0], pos[1]);
                //為了好看一點只在扇形的中間部分繪製文字,兩頭各留 1/4 的空間,獲取中間一段的第 j 個等分點的座標
                mPathMeasure.setPath(mPath, false);
                float distance = (mPathMeasure.getLength() / 2) / (text.length() - 1) * j
                        + mPathMeasure.getLength() / 4;
                mPathMeasure.getPosTan(distance, pos, tan);
                float x1 = pos[0];
                float y1 = pos[1];

                //同上,只是這裡連線圓心和扇形路徑的中點
                //重置 path,首先獲取扇形的弧形部分的路徑
                mPath.reset();
                mPath.addArc(mRectF, startAngle, sweepAngle);
                //獲取扇形路徑的起點
                mPathMeasure.setPath(mPath, false);
                mPathMeasure.getPosTan(mPathMeasure.getLength(), pos, tan);
                //重置 path,連線圓心和扇形路徑的起點
                mPath.reset();
                mPath.moveTo(mWidth / 2, mHeight / 2);
                mPath.lineTo(pos[0], pos[1]);
                //為了好看一點只在扇形的中間部分繪製文字,兩頭各留 1/4 的空間,獲取中間一段的第 j 個等分點的座標
                mPathMeasure.setPath(mPath, false);
                mPathMeasure.getPosTan(distance, pos, tan);
                float x2 = pos[0];
                float y2 = pos[1];

                //重置 path,連接獲取的兩個第 j 個等分點,作為繪製文字的path
                mPath.reset();
                mPath.moveTo(x1, y1);
                mPath.lineTo(x2, y2);
                //修改文字畫筆的顏色,字型大小等屬性
                mPathMeasure.setPath(mPath, false);
                //這裡 drawPath() 是為了看看文字到底是沿著哪條 path 繪製的,實際執行後要註釋掉
                mPaint.setColor(Color.BLACK);
                mPaint.setStrokeWidth(5f);
                mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
                canvas.drawPath(mPath, mPaint);
                //TextSize 會影響下面測量文字整體的方法 getTextBounds(),所以要在 getTextBounds() 方法前呼叫
                mTextPaint.setTextSize(sp2px(getContext(), 20f));
                mTextPaint.setColor(Color.BLACK);
                mTextPaint.setTypeface(Typeface.DEFAULT);
                mTextPaint.getTextBounds(text, j, j + 1, mTextBounds);
                //沿著 path 繪製文字,並且將文字繪製在路徑中間,即居中繪製
                canvas.drawTextOnPath(text.substring(j, j + 1), mPath, mPathMeasure.getLength() / 2 - mTextBounds.width() / 2, 0, mTextPaint);
            }

 

現在效果是這樣:

 

文字下面的線只是為了看看文字到底是沿著哪條 path 繪製的,實際執行後要註釋掉。

 

4 繪製中心按鈕及結果箭頭

到現在,輪盤的基本雛形已經有了,但要想知道抽獎結果,還需要一個箭頭指示旋轉後最後停下來的結果,再在中心加個按鈕到時候來觸發抽獎事件:

給中間按鈕一個預設文字:


    private String centerText = "GO";  //輪盤中心的文字

 

然後將下列程式碼新增到剛才繪製扇形的 for 迴圈後:

        //繪製以輪盤圓心為圓心,以輪盤半徑的 1/10 為半徑的白色圓
        int radius = mWidth / 10;
        mPaint.setColor(Color.WHITE);
        canvas.drawCircle(mWidth / 2, mHeight / 2, radius, mPaint);

        //繪製以輪盤圓心為圓心,以白色圓半徑再減去 5dp 為半徑的紅色圓
        radius = mWidth / 10 - dp2px(getContext(), 5f);
        mPaint.setColor(Color.RED);
        canvas.drawCircle(mWidth / 2, mHeight / 2, radius, mPaint);

        //在紅色圓上繪製一個箭頭
        mPath.reset();
        mPaint.setColor(Color.RED);
        mPath.moveTo(mWidth / 2 - (float) (Math.sin(Math.PI / 12) * radius)
                , mHeight / 2 - (float) (Math.cos(Math.PI / 12) * radius));
        mPath.lineTo(mWidth / 2
                , mHeight / 2 - radius * 2f);
        mPath.lineTo(mWidth / 2 + (float) (Math.sin(Math.PI / 12) * radius)
                , mHeight / 2 - (float) (Math.cos(Math.PI / 12) * radius));
        canvas.drawPath(mPath, mPaint);

        //在紅色圓中繪製文字,預設為 Go
        mTextPaint.setTextSize(sp2px(getContext(), 40f));
        mTextPaint.setColor(Color.WHITE);
        mTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
        mTextPaint.getTextBounds(centerText, 0, centerText.length(), mTextBounds);
        canvas.drawText(centerText, mWidth / 2 - mTextBounds.width() / 2, mHeight / 2 + mTextBounds.height() / 2, mTextPaint);

 

如果感覺不知道新增到什麼地方的話不要方,最後會貼出原始碼,現在是這樣的效果:

 

5 旋轉

至此,這個命運之輪的靜態樣子已經繪製完成了,最後只需要給它中間的按鈕增加一個點選事件,點選後輪盤進行旋轉,旋轉一段時間或者一定角度後停下來,當然,這個旋轉角度還是要隨機一下的,不然每次結果都是一樣的了。旋轉的方法有很多,這裡我介紹我的思路:

給一個旋轉角度的變數 mRotateDegrees 用以在 onDraw() 方法中旋轉畫布。在觸發抽獎事件時通過 Random 類獲取一個在 mPrizeList 元素下標範圍內的隨機數 ,這個隨機數就是最後的抽獎結果。計算出這個抽獎結果所在扇形的開始度數,用 360 一減它就是我們需要旋轉的度數,為了效果看起來好一點可以多再加幾個 360 讓它多旋轉幾圈。然後開啟一個動畫,在動畫監聽中修改 mRotateDegrees 並重繪。

在 onDraw() 方法中繪製扇形前根據 mRotateDegrees 旋轉畫布,就能實現輪盤旋轉的效果。當然輪盤中心的白色圓、開始抽獎的按鈕和文字不用旋轉,所以在繪製扇形和扇形中的文字前要儲存一下畫布狀態,在繪製扇形和扇形中的文字完成後恢復畫布狀態。

下面是程式碼,也是整個原始碼:

public class WheelOfFortuneView extends View {
    private static final String TAG = "WheelOfFortuneView";
    private int mWidth; //控制元件寬,為螢幕寬和高的較小值
    private int mHeight;    //控制元件高,為螢幕寬和高的較小值
    private Paint mPaint;   //畫筆
    private TextPaint mTextPaint;   //繪製文字的畫筆
    private RectF mRectF;   //扇形繪製區域
    private Path mPath; //路徑
    private PathMeasure mPathMeasure;   //路徑測量的物件
    private Rect mTextBounds;   //文字繪製區域
    private float[] pos = new float[2]; //記錄路徑中某個點的座標陣列
    private float[] tan = new float[2];
    private float mRotateDegrees = 0;   //旋轉角度
    private GestureDetector mGestureDetector;   //手勢監聽器,其實也就監聽了一個單擊事件

    private List<Prize> mPrizeList = new ArrayList<>();
    private String centerText = "GO";  //輪盤中心的文字
    private IOnPrizeDrawListener mOnPrizeDrawListener;

    private GestureDetector.OnGestureListener mOnGestureListener = new GestureDetector.OnGestureListener() {
        @Override
        public boolean onDown(MotionEvent e) {
            //return true 才會響應後續事件,如 onSingleTapUp、onFling 等
            return true;
        }

        @Override
        public void onShowPress(MotionEvent e) {

        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            //單擊事件的觸發範圍在 Go 按鈕的範圍內才響應抽獎事件
            int goButtonRadius = mWidth / 10 - dp2px(getContext(), 5f);
            if (e.getX() > mWidth / 2 - goButtonRadius
                    && e.getX() < mWidth / 2 + goButtonRadius
                    && e.getY() > mHeight / 2 - goButtonRadius
                    && e.getY() < mHeight / 2 + goButtonRadius) {
                startPrizeDraw();
            }
            return false;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            return false;
        }

        @Override
        public void onLongPress(MotionEvent e) {

        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            return false;
        }
    };

    public WheelOfFortuneView(Context context) {
        this(context, null);
    }

    public WheelOfFortuneView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public WheelOfFortuneView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);

        mTextPaint = new TextPaint();
        mTextPaint.setAntiAlias(true);

        mRectF = new RectF();
        mPath = new Path();
        mPathMeasure = new PathMeasure();
        mTextBounds = new Rect();

        mGestureDetector = new GestureDetector(getContext(), mOnGestureListener);
        mPrizeList.add(new Prize("一等獎", Color.RED));
        mPrizeList.add(new Prize("二等獎", Color.YELLOW));
        mPrizeList.add(new Prize("三等獎", Color.BLUE));
        mPrizeList.add(new Prize("謝謝你", Color.GREEN));
        mPrizeList.add(new Prize("一等獎", Color.RED));
        mPrizeList.add(new Prize("二等獎", Color.YELLOW));
        mPrizeList.add(new Prize("三等獎", Color.BLUE));
        mPrizeList.add(new Prize("謝謝你", Color.GREEN));
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = mHeight = Math.min(getContext().getResources().getDisplayMetrics().widthPixels, getContext().getResources().getDisplayMetrics().heightPixels);
        setMeasuredDimension(mWidth, mHeight);

        //繪製扇形的區域
        mRectF.set(0, 0, mWidth, mHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //儲存畫布狀態,在繪製完扇形和文字後恢復
        canvas.save();
        //點選抽獎後開始旋轉
        canvas.rotate(mRotateDegrees, (mRectF.right - mRectF.left) / 2, (mRectF.bottom - mRectF.top) / 2);

        for (int i = 0; i < mPrizeList.size(); i++) {
            //繪製扇形
            mPaint.setColor(mPrizeList.get(i).getBackgroundColor());
            //計算每個扇形開始的角度,Android 中在繪製扇形時從順時針 90 度開始繪製,所以起始角度要減 90
            float startAngle = (float) 360 / (float) mPrizeList.size() * i - 90;
            float sweepAngle = (float) 360 / (float) mPrizeList.size();
            canvas.drawArc(mRectF, startAngle, sweepAngle, true, mPaint);

            //處理文字,因為想豎著繪製文字,一個字一個字的話
            String text = mPrizeList.get(i).getText();
            text = new StringBuilder(text).reverse().toString();
            for (int j = 0; j < text.length(); j++) {
                //重置 path,首先獲取扇形的弧形部分的路徑
                mPath.reset();
                mPath.addArc(mRectF, startAngle, sweepAngle);
                //獲取扇形路徑的起點
                mPathMeasure.setPath(mPath, false);
                mPathMeasure.getPosTan(0, pos, tan);
                //重置 path,連線圓心和扇形路徑的起點
                mPath.reset();
                mPath.moveTo(mWidth / 2, mHeight / 2);
                mPath.lineTo(pos[0], pos[1]);
                //為了好看一點只在扇形的中間部分繪製文字,兩頭各留 1/4 的空間,獲取中間一段的第 j 個等分點的座標
                mPathMeasure.setPath(mPath, false);
                float distance = (mPathMeasure.getLength() / 2) / (text.length() - 1) * j
                        + mPathMeasure.getLength() / 4;
                mPathMeasure.getPosTan(distance, pos, tan);
                float x1 = pos[0];
                float y1 = pos[1];

                //同上,只是這裡連線圓心和扇形路徑的中點
                //重置 path,首先獲取扇形的弧形部分的路徑
                mPath.reset();
                mPath.addArc(mRectF, startAngle, sweepAngle);
                //獲取扇形路徑的起點
                mPathMeasure.setPath(mPath, false);
                mPathMeasure.getPosTan(mPathMeasure.getLength(), pos, tan);
                //重置 path,連線圓心和扇形路徑的起點
                mPath.reset();
                mPath.moveTo(mWidth / 2, mHeight / 2);
                mPath.lineTo(pos[0], pos[1]);
                //為了好看一點只在扇形的中間部分繪製文字,兩頭各留 1/4 的空間,獲取中間一段的第 j 個等分點的座標
                mPathMeasure.setPath(mPath, false);
                mPathMeasure.getPosTan(distance, pos, tan);
                float x2 = pos[0];
                float y2 = pos[1];

                //重置 path,連接獲取的兩個第 j 個等分點,作為繪製文字的path
                mPath.reset();
                mPath.moveTo(x1, y1);
                mPath.lineTo(x2, y2);
                //修改文字畫筆的顏色,字型大小等屬性
                mPathMeasure.setPath(mPath, false);
                //這裡 drawPath() 是為了看看文字到底是沿著哪條 path 繪製的,實際執行後要註釋掉
//                mPaint.setColor(Color.BLACK);
//                mPaint.setStrokeWidth(5f);
//                mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
//                canvas.drawPath(mPath, mPaint);
                //TextSize 會影響下面測量文字整體的方法 getTextBounds(),所以要在 getTextBounds() 方法前呼叫
                mTextPaint.setTextSize(sp2px(getContext(), 20f));
                mTextPaint.setColor(Color.BLACK);
                mTextPaint.setTypeface(Typeface.DEFAULT);
                mTextPaint.getTextBounds(text, j, j + 1, mTextBounds);
                //沿著 path 繪製文字,並且將文字繪製在路徑中間,即居中繪製
                canvas.drawTextOnPath(text.substring(j, j + 1), mPath, mPathMeasure.getLength() / 2 - mTextBounds.width() / 2, 0, mTextPaint);
            }
        }
        //輪盤中心的白色圓、開始抽獎的按鈕和文字不用旋轉,所以在這裡恢復畫布狀態
        canvas.restore();

        //繪製以輪盤圓心為圓心,以輪盤半徑的 1/10 為半徑的白色圓
        int radius = mWidth / 10;
        mPaint.setColor(Color.WHITE);
        canvas.drawCircle(mWidth / 2, mHeight / 2, radius, mPaint);

        //繪製以輪盤圓心為圓心,以白色圓半徑再減去 5dp 為半徑的紅色圓
        radius = mWidth / 10 - dp2px(getContext(), 5f);
        mPaint.setColor(Color.RED);
        canvas.drawCircle(mWidth / 2, mHeight / 2, radius, mPaint);

        //在紅色圓上繪製一個箭頭
        mPath.reset();
        mPaint.setColor(Color.RED);
        mPath.moveTo(mWidth / 2 - (float) (Math.sin(Math.PI / 12) * radius)
                , mHeight / 2 - (float) (Math.cos(Math.PI / 12) * radius));
        mPath.lineTo(mWidth / 2
                , mHeight / 2 - radius * 2f);
        mPath.lineTo(mWidth / 2 + (float) (Math.sin(Math.PI / 12) * radius)
                , mHeight / 2 - (float) (Math.cos(Math.PI / 12) * radius));
        canvas.drawPath(mPath, mPaint);

        //在紅色圓中繪製文字,預設為 Go
        mTextPaint.setTextSize(sp2px(getContext(), 40f));
        mTextPaint.setColor(Color.WHITE);
        mTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
        mTextPaint.getTextBounds(centerText, 0, centerText.length(), mTextBounds);
        canvas.drawText(centerText, mWidth / 2 - mTextBounds.width() / 2, mHeight / 2 + mTextBounds.height() / 2, mTextPaint);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return mGestureDetector.onTouchEvent(event);
    }

    private void startPrizeDraw() {
        //抽獎結果的下標
        final int random = new Random().nextInt(mPrizeList.size() - 1);
        //計算出抽獎結果所在扇形的開始度數
        float endRotateDegrees = 360 - 360 / mPrizeList.size() * random;
        //再減去扇形掃過度數的一半,讓其最後旋轉到抽獎結果所在扇形的中間
        endRotateDegrees -= 360 / mPrizeList.size() / 2;
        //多旋轉幾圈
        endRotateDegrees += 360 * 5;
        //以 endRotateDegrees 開始動畫,在動畫監聽中更新 mRotateDegrees 的值並讓其重繪
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, endRotateDegrees);
        valueAnimator.setDuration(3 * 1000);
        valueAnimator.setInterpolator(new DecelerateInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mRotateDegrees = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        valueAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                mRotateDegrees = 0;
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                Toast.makeText(getContext(), "恭喜你抽中了" + mPrizeList.get(random).getText(), Toast.LENGTH_SHORT).show();
                if (mOnPrizeDrawListener != null) {
                    mOnPrizeDrawListener.onPrizeDrawResult(mPrizeList.get(random));
                }
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
        valueAnimator.start();
    }

    private static int dp2px(Context context, float dpValue) {
        float density = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * density + 0.5f);
    }

    private static int sp2px(Context context, float spValue) {
        float scaleDensity = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * scaleDensity + 0.5f);
    }

    /**
     * Description:獎品內部類
     * Date:2019/1/11
     */
    public static class Prize {
        private String text;    //獎品名
        private int backgroundColor;    //對應扇形的背景色

        public Prize() {
        }

        public Prize(String text, int backgroundColor) {
            this.text = text;
            this.backgroundColor = backgroundColor;
        }

        public String getText() {
            return text;
        }


        public int getBackgroundColor() {
            return backgroundColor;
        }
    }

    /**
     * Description:抽獎結果回撥介面
     * Date:2019/1/11
     */
    public interface IOnPrizeDrawListener {
        void onPrizeDrawResult(Prize prize);
    }

    public void setPrizeList(List<Prize> prizeList) {
        this.mPrizeList = prizeList;
        invalidate();
    }

    public void setCenterText(String centerText) {
        this.centerText = centerText;
    }

    public void setOnPrizeDrawListener(IOnPrizeDrawListener onPrizeDrawListener) {
        mOnPrizeDrawListener = onPrizeDrawListener;
    }
}

 

下面是最終效果:

 

6 總結

手勢旋轉有點複雜,要考慮的情況太多所以就沒有加。從效果上來說這個自定義 View 並不複雜,不過我覺得這次自定義 View 重要的是在繪製文字的時候,剛開始不知道如何能把文字排列得好看一點,在後來換了一個方式思考後一下邏輯就清晰了,所以說思路真的很重要。

今後在選擇困難症犯病的時候就用這個輪盤來決定一下結果吧。