Android自定義實現九宮格抽獎功能

最近的功能需求中需要實現用戶使用簽到獲取的積分,可以在九宮格中進行抽獎消耗積分,這裏使用的是自定義進行實現抽獎的功能,可以通過設置計算策略,來控制用戶 中哪些獎以及中大獎 的概率,話不多說,直接上代碼。

1.先看效果圖
在這裏插入圖片描述

2.自定義View實現九宮格抽獎功能

public class LuckyView extends View {
    private Paint mPaint;
    private float mStrokeWidth = 5;
    private int mRepeatCount = 5; // 轉的圈數
    private int mRectSize; // 矩形的寬和高(矩形爲正方形)
    private boolean mShouldStartFlag;
    private boolean mShouldStartNextTurn = true; // 標記是否應該開啓下一輪抽獎
    private int mStartLuckPosition = 0; // 開始抽獎的位置
    private int mCurrentPosition = -1; // 當前轉圈所在的位置

    private OnLuckAnimationEndListener mLuckAnimationEndListener;

    /**
     * 可以通過對 mLuckNum 設置計算策略,來控制用戶 中哪些獎 以及 中大獎 的概率
     */
    private int mLuckNum = 3; // 默認最終中獎位置

    private List<RectF> mRectFs; // 存儲矩形的集合
    private int[] mItemColor = {Color.parseColor("#ffefd6"), Color.parseColor("#ffefd6")}; // 矩形的顏色
    private String[] mPrizeDescription = {"滿20減1元券", "滿10減1元券", "滿30減2元券", "滿5減1元券", "免單", "滿300減40元券", "滿100減10元券", "滿500減50元券", "開始"};
    private int[] mLuckyPrizes = {R.drawable.aaa, R.drawable.bbb, R.drawable.ccc, R.drawable
            .ddd, R.drawable.aaa, R.drawable.bbb, R.drawable.ccc, R.drawable.ddd, R
            .drawable.center_lucky};
    private List<String> lettersBeans;
    private float left;
    private float top;
    private Bitmap[] bitmaps;
    private String[] luckyName;
    private String[] id;
    private int selectPos;


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

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

    public LuckyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 抗鋸齒
        mPaint.setStyle(Paint.Style.FILL);
        // mPaint.setStyle(Paint.Style.STROKE); // 設置樣式爲描邊
        mPaint.setStrokeWidth(mStrokeWidth); // 設置描邊的寬度

        mRectFs = new ArrayList<>();
    }

    public void setLuckAnimationEndListener(OnLuckAnimationEndListener luckAnimationEndListener) {
        mLuckAnimationEndListener = luckAnimationEndListener;
    }

    public int getLuckNum() {
        return mLuckNum;
    }

    public void setLuckNum(int luckNum) {
        mLuckNum = luckNum;
    }

    public int[] getLuckyPrizes() {
        return mLuckyPrizes;
    }

    public void setLuckyPrizes(int[] luckyPrizes) {
        mLuckyPrizes = luckyPrizes;
    }

    //設置圖片,文字數據
    public void setData(List<String> lettersBeans) {
        this.lettersBeans = lettersBeans;
        invalidate();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // 矩形的寬高
        mRectSize = (Math.min(w, h)) / 3;
        // 當控件大小改變的時候清空數據
        mRectFs.clear();
        initNineRect();
    }

    /**
     * 初始化 9 個矩形(正方形)的位置信息
     */
    private void initNineRect() {
        final float width = getWidth();

        // 加載前三個矩形
        for (int i = 0; i < 3; i++) {
            float left = i * mRectSize + 5;
            float right = (i + 1) * mRectSize;
            float top = 5;
            float bottom = mRectSize;
            RectF rectF = new RectF(left, top, right, bottom);
            mRectFs.add(rectF);
        }

        // 加載第 4 個矩形
        mRectFs.add(new RectF(width - mRectSize + 5, mRectSize + 5, width, 2 * mRectSize));

        // 加載第 5~7 個矩形
        for (int j = 3; j > 0; j--) {
            float left = width - (4 - j) * mRectSize + 5;
            float right = width - (3 - j) * mRectSize;
            float top = 2 * mRectSize + 5;
            float bottom = 3 * mRectSize;
            RectF rectF = new RectF(left, top, right, bottom);
            mRectFs.add(rectF);
        }

        // 加載第 8 個矩形
        mRectFs.add(new RectF(5, mRectSize + 5, mRectSize, 2 * mRectSize));

        // 加載中心第 9 個矩形
        mRectFs.add(new RectF(mRectSize + 5, mRectSize + 5, 2 * mRectSize, 2 * mRectSize));
    }

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

        //執行真正的繪製矩形操作
        drawNineRect(canvas);
        // 填充獎品圖片
        drawNineBitmaps(canvas);
        // 填充獎品文字
        drawNineText(canvas);
    }

    /**
     * 在每個矩形中填充獎品圖片
     * left:The position of the left side of the bitmap being drawn
     * top:The position of the top side of the bitmap being drawn
     */
    private void drawNineBitmaps(final Canvas canvas) {

        for (int i = 0; i < mRectFs.size(); i++) {
            RectF rectF = mRectFs.get(i);
            // 將圖片設置在每個矩形中央
            left = rectF.left + mRectSize / 4;
            top = rectF.top + mRectSize / 4;
            canvas.drawBitmap(Bitmap.createScaledBitmap(BitmapFactory.decodeResource(getResources(), mLuckyPrizes[i]), mRectSize / 2, mRectSize / 2, false), left, top, null);
        }
    }


    /**
     * 在每個矩形中央填充文字,代替抽獎圖片
     * x:he x-coordinate of the origin of the text being drawn
     * y:The y-coordinate of the baseline of the text being drawn
     */
    private void drawNineText(Canvas canvas) {
        for (int i = 0; i < mRectFs.size(); i++) {
            RectF rectF = mRectFs.get(i);
            float x = rectF.left + mRectSize / 4; // 將文字設置在每個矩形中央
            float y = rectF.top + mRectSize - 20;
            mPaint.setColor(Color.parseColor("#5e5448"));
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setTextSize(15); // unit px
            if (i == mRectFs.size() - 1) {
                canvas.drawText("", x, y, mPaint);
            } else {
                canvas.drawText(mPrizeDescription[i], x, y, mPaint);
            }
        }
    }

    /**
     * 執行真正的繪製矩形操作
     */
    private void drawNineRect(Canvas canvas) {
        for (int x = 0; x < mRectFs.size(); x++) {
            RectF rectF = mRectFs.get(x);
            if (x == 8) {
                mPaint.setColor(Color.WHITE);
            } else {
                if (mCurrentPosition == x) {
                    mPaint.setColor(Color.parseColor("#edcea9"));
                } else {
                    mPaint.setColor(mItemColor[x % 2]); // 標記當前轉盤經過的位置
                }
            }
            canvas.drawRect(rectF, mPaint);
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            mShouldStartFlag = mRectFs.get(8).contains(event.getX(), event.getY());
            return true;
        }
        if (event.getAction() == MotionEvent.ACTION_UP) {
            if (mShouldStartFlag) {
                if (mRectFs.get(8).contains(event.getX(), event.getY())) {
                    // mLuckAnimationEndListener.onClickLuck();
                    startAnim(); // 判斷只有手指落下和擡起都在中間的矩形內時纔開始執行動畫抽獎
                }
                mShouldStartFlag = false;
            }
        }
        return super.onTouchEvent(event);
    }

    private void startAnim() {
        if (!mShouldStartNextTurn) {
            return;
        }
        Random random = new Random();
        setLuckNum(random.nextInt(8)); // 生成 [0,8) 的隨機整數

        ValueAnimator animator = ValueAnimator.ofInt(mStartLuckPosition, mRepeatCount * 8 + mLuckNum)
                .setDuration(5000);

        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                final int position = (int) animation.getAnimatedValue();
                setCurrentPosition(position % 8);
                mShouldStartNextTurn = false;
            }
        });

        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                mShouldStartNextTurn = true;
                mStartLuckPosition = mLuckNum;
                //最終選中的位置
                if (mLuckAnimationEndListener != null) {
                    mLuckAnimationEndListener.onLuckAnimationEnd(mCurrentPosition,
                            mPrizeDescription[mCurrentPosition]);
                }
            }
        });

        animator.start();
    }

    private void setCurrentPosition(int position) {
        mCurrentPosition = position;
        invalidate(); // 強制刷新,在 UI 線程回調 onDraw()
    }

    public void setBitmap(Bitmap[] bitmaps1, String[] name, String[] strings) {
        bitmaps = bitmaps1;
        luckyName = name;
        id = strings;
        invalidate();
    }

    /**
     * 選中id
     *
     * @param datas
     */
    public void setSelectId(int datas) {
        String selectId = datas + "";

        if (id != null && id.length != 0) {
            for (int i = 0; i < id.length; i++) {
                if (id[i].equals(selectId)) {
                    selectPos = i;
                }
            }
        }

        startAnim();
    }

    /**
     * 用於抽獎結果回調
     */
    public interface OnLuckAnimationEndListener {
        void onLuckAnimationEnd(int pos, String msg);
    }
}

3.主界面佈局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#fff"
    tools:context=".MainActivity">

    <com.showly.luckyactivity.view.LuckyView
        android:id="@+id/lucky_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="50dp"/>

</RelativeLayout>

4.主程序
這裏比較簡單,就是輸出抽獎結果

public class MainActivity extends AppCompatActivity {

    private LuckyView luckyView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();
        initData();
        initListener();
    }

    private void initView() {
        luckyView = findViewById(R.id.lucky_view);
    }

    private void initData() {

    }

    private void initListener() {
        luckyView.setLuckAnimationEndListener(new LuckyView.OnLuckAnimationEndListener() {
            @Override
            public void onLuckAnimationEnd(int pos, String msg) {
                //打印抽獎結果
                Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

5,總結
實現九宮格抽獎重點在自定義View的處理,代碼中有文字解析,這裏就不重複說明了,還有這裏抽獎展示的圖片及文字是固定的,如果需要動態設置圖片及文字數據的話,可以自己更改自定義控件中的邏輯。

需要Demo源碼的童鞋可以在底部的公衆號回覆:"九宮格抽獎"即可獲取。


以下是個人公衆號(longxuanzhigu),之後發佈的文章會同步到該公衆號,方便交流學習Android知識及分享個人愛好文章,有問題可以留言哦:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章