Create a smooth Jiugongge lottery draw


Because the company project needs to do a nine-square lottery draw, I have never done a similar function before. Although I accidentally saw a lot of lottery items when browsing the blogs of the great gods, but because there is no need in the project, there has never been one. Click to see. This time I don't think it will work. It was not until the company planned to build a lottery function that it had no choice but to search for demos online


After searching online for a long time, I finally found a few demos, downloaded them, and found that the package was empty, only a few pictures of Jiugongge, which caused me to waste a few CSDN points in vain. Later, I found a demo on the eoe website, so I was very happy. After downloading it, I immediately imported it into the project. After running it to see the effect, the Jiugongge came out, but the effect is really not flattering, mainly because the operation is not smooth. But I went in and took a look at the demo. The basic idea is this: define the Jiugongge interface, then start the sub-thread to continuously modify the state, and then send a message to the main thread through the handler to modify the interface (the sub-thread cannot directly modify the interface).


Although this demo is functionally realized, it is not the effect I want, because I can't even pass this level, let alone the product side. then what should we do?


So I thought of a control called SurfaceView. Comrades who are engaged in game development should be familiar with this control, right? First introduce this control:
1. SurfaceView inherits from View and is mostly used in game development
2. It can run directly in the sub-thread (other UI controls must run in the main thread).
3. Generally, the onDraw method is rewritten when customizing UI controls, but in SurfaceView, the Canvas is obtained through SurfaceHolder to draw graphics

Alright, come on everyone, let’s take a look at the renderings first:
Jiugongge

In this way, I will start to talk about the steps of customizing Jiugongge according to my ideas.
Steps:
1. Calculate the position of each block
2. Draw the block of each prize (mainly to make the interface more beautiful)
3. Draw the prize map
4. Calculate the next position of the rotating block
5. Draw the rotating block
6. Listen to the event of clicking the start button

Main core technologies:
SurfaceView, SurfaceHolder

OK, with the basic steps, the next step is to follow the steps step by step.
Before starting to draw the nine-square grid, we first rewrite the onMeasure method, mainly to make the nine-square grid a square, which looks better. The basic code is as follows:

public class LotteryView extends SurfaceView{
    
    

    /**
     * holder
     */
    private SurfaceHolder mHolder;


    private List<Prize>prizes;
    private boolean flags;    //抽奖开关

    private int lottery=6;   //设置中奖号码

    private int current=2;   //抽奖开始的位置

    private int count=0;   //旋转次数累计

    private int countDown;    //倒计次数,快速旋转完成后,需要倒计多少次循环才停止

    //旋转抽奖的方块默认颜色
    private int transfer= 0xffff0000;

    private int MAX=50;   //最大旋转次数
    /** 
     * 重新测量
     */  
    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  
    {  
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
        int width = Math.min(getMeasuredWidth(), getMeasuredHeight());  
        setMeasuredDimension(width, width);  
    }
}

SurfaceView generally does not draw controls by rewriting the onDraw method, so how to get Canvas? It is mainly obtained by listening to the Callback event through SurfaceHolder.
The basic code is as follows:

/**
     * holder
     */
    private SurfaceHolder mHolder;
    public LotteryView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mHolder = this.getHolder();
        //监听CallBack
        mHolder.addCallback(this);
    }

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

Now that we have the object SurfaceHolder object, we can get the Canvas object, and start the real drawing work.

1. Calculate the specific display position of the square
2. Draw the square for each prize

    //绘制背景
    private void drawBg(Canvas canvas) {
        //清除已绘制的图形
        canvas.drawColor(Color.WHITE, Mode.CLEAR);
        //获取控件的宽度,因为要绘制九宫格,所以要平局分成三列
        int width = getMeasuredWidth()/3;
        int x1=0;
        int y1=0;

        int x2=0;
        int y2=0;

        int len = (int) Math.sqrt(prizes.size());

        for(int x=0;x<len*len;x++){

            Prize prize = prizes.get(x);

            int index=x;
            x1=getPaddingLeft()+width*(Math.abs(index)%len);
            y1=getPaddingTop()+width*(index/len);

            x2=x1+width;
            y2=y1+width;
            Rect rect=new Rect(x1,y1,x2,y2);

            Paint paint=new Paint();
            //绘制方块
            canvas.drawRect(rect, paint);
        }
    }

Analysis: prizes is a collection, which encapsulates some basic information of the prizes, x1, y1, x2, y2 are the upper left vertex and lower right vertex of the prize container square, respectively.
Computation graph

Through observation, it is found that each square position has a certain relationship, that is, x1=getPaddingLeft()+width*(Math.abs(index)%len); y1=
getPaddingTop()+width*(index/len);
x2= x1+width;
y2=y1+width;
With the relationship between these points, you can draw a square through canvas.drawRect(rect, paint);

3. Draw the prize map

    //绘制奖品
    private void drawPrize(Canvas canvas) {
        int width = getMeasuredWidth()/3;
        int x1=0;
        int y1=0;

        int x2=0;
        int y2=0;

        int len = (int) Math.sqrt(prizes.size());

        for(int x=0;x<len*len;x++){

            Prize prize = prizes.get(x);

            int index=x;
            x1=getPaddingLeft()+width*(Math.abs(index)%len);
            y1=getPaddingTop()+width*(index/len);

            x2=x1+width;
            y2=y1+width;
            Rect rect=new Rect(x1+width/6,y1+width/6,x2-width/6,y2-width/6);
            prize.setRect(rect);
            canvas.drawBitmap(prize.getIcon(), null, rect, null);

        }
    }

After passing steps 1 and 2, knowing the position relationship of the squares, you can easily draw the prize according to these relationships, Rect rect=new Rect(x1+width/6,y1+width/6,x2-width/6,y2 -width/6); is to make the prize smaller than the box, which will look more natural.

4. Calculate the next position of the rotating cube

    //下一步
    public int next(int position,int len){
        int current=position;
        if(current+1<len){
            return ++current;
        }

        if((current+1)%len==0&&current<len*len-1){
            return current+=len;
        }

        if(current%len==0){
            return current-=len;
        }

        if(current<len*len){
            return --current;
        }

        return current;
    }

position is the position of the current rotating block, len is 3

5. Draw a rotating cube

    //绘制旋转的方块
    private void drawTransfer(Canvas canvas) {
        int width = getMeasuredWidth()/3;
        int x1;
        int y1;

        int x2;
        int y2;
        int len = (int) Math.sqrt(prizes.size());
        //得到下一步方块的位置
        current=next(current, len);
        x1=getPaddingLeft()+width*(Math.abs(current)%len);
        y1=getPaddingTop()+width*((current)/len);

        x2=x1+width;
        y2=y1+width;

        Rect rect=new Rect(x1,y1,x2,y2);
        Paint paint=new Paint();
        paint.setColor(transfer);
        canvas.drawRect(rect, paint);
    }

6. Listen to the event of clicking the start button

    private OnTransferWinningListener listener;

    public void setOnTransferWinningListener(OnTransferWinningListener listener){
        this.listener=listener;
    }

    public interface OnTransferWinningListener{
    
    
        /**
         * 中奖回调
         * @param position
         */
        void onWinning(int position);
    }
        @Override
    public boolean onTouchEvent(MotionEvent event) {
        handleTouch(event);
        return super.onTouchEvent(event);
    }
    /**
     * 触摸
     * @param event
     */
    public void handleTouch(MotionEvent event) {


        Point touchPoint=new Point((int)event.getX()-getLeft(),(int)event.getY());
        switch(event.getAction()){
        case MotionEvent.ACTION_DOWN:
            Prize prize = prizes.get(Math.round(prizes.size())/2);
            if(prize.isClick(touchPoint)){
                if(!flags){
                    setStartFlags(true);
                    prize.click();
                }
            }
            break ;
        default:
            break ;
        }
    }

//控制旋转
    private void controllerTransfer() {
        if(count>MAX){
            countDown++;
            SystemClock.sleep(count*5);
        }else{
            SystemClock.sleep(count*2);
        }

        count++;
        if(countDown>2){
            if(lottery==current){
                countDown=0;
                count=0;
                setStartFlags(false);

                if(listener!=null){
                    //切换到主线程中运行
                    post(new Runnable() {

                        @Override
                        public void run() {
                            listener.onWinning(current);
                        }
                    });

                }
            }
        }
    }

So far, the basic customization work is almost done, use the demo as follows:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.example.test.LotteryView
        android:id="@+id/nl"
         android:layout_width="match_parent"
        android:layout_height="match_parent"
        />


</RelativeLayout>
public class HomeActivity extends Activity {
    
    

    LotteryView nl;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.act_home);

        nl=(LotteryView) findViewById(R.id.nl);

        int[]prizesIcon={R.drawable.danfan,R.drawable.meizi,R.drawable.iphone,R.drawable.f015,R.drawable.arrow,R.drawable.f040,R.drawable.ipad,R.drawable.spree_icon,R.drawable.spree_success_icon};
        final List<Prize>prizes=new ArrayList<Prize>();
        for(int x=0;x<9;x++){
            Prize lottery=new Prize();
            lottery.setId(x+1);
            lottery.setName("Lottery"+(x+1));
            Bitmap bitmap = BitmapFactory.decodeResource(getResources(), prizesIcon[x]);
            lottery.setIcon(bitmap);
            if((x+1)%2==0){
                lottery.setBgColor(0xff4fccee);
            }else if(x==4){
                lottery.setBgColor(0xffffffff);
            }else{
                lottery.setBgColor(0xff00ff34);
            }

            prizes.add(lottery);
        }
        nl.setPrizes(prizes);
        nl.setOnTransferWinningListener(new OnTransferWinningListener() {

            @Override
            public void onWinning(int position) {
                Toast.makeText(getApplicationContext(), prizes.get(position).getName(), Toast.LENGTH_SHORT).show();
            }
        });
    }
}

Works very smoothly

LotteryView overall demo:

package com.example.test;

import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;


public class LotteryView extends SurfaceView implements Callback{
    
    

    /**
     * holder
     */
    private SurfaceHolder mHolder;


    private List<Prize>prizes;
    private boolean flags;

    private int lottery=6;   //设置中奖号码

    private int current=2;   //抽奖开始的位置

    private int count=0;   //旋转次数累计

    private int countDown;    //倒计次数,快速旋转完成后,需要倒计多少次循环才停止


    private int transfer= 0xffff0000;

    private int MAX=50;   //最大旋转次数

    private OnTransferWinningListener listener;

    public void setOnTransferWinningListener(OnTransferWinningListener listener){
        this.listener=listener;
    }

    public interface OnTransferWinningListener{
    
    
        /**
         * 中奖回调
         * @param position
         */
        void onWinning(int position);
    }


    /**
     * 设置中奖号码
     * @param lottery
     */
    public void setLottery(int lottery) {
        if(prizes!=null&&Math.round(prizes.size()/2)==0){
            throw new RuntimeException("开始抽奖按钮不能设置为中奖位置!");
        }
        this.lottery = lottery;
    }

    /**
     * 设置转盘颜色
     * @param transfer
     */
    public void setTransfer(int transfer) {
        this.transfer = transfer;
    }

    /**
     * 设置奖品集合
     * @param prizes
     */
    public void setPrizes(List<Prize>prizes){
        this.prizes=prizes;
    }


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

    /**
     * 触摸
     * @param event
     */
    public void handleTouch(MotionEvent event) {


        Point touchPoint=new Point((int)event.getX()-getLeft(),(int)event.getY());
        switch(event.getAction()){
        case MotionEvent.ACTION_DOWN:
            Prize prize = prizes.get(Math.round(prizes.size())/2);
            if(prize.isClick(touchPoint)){
                if(!flags){
                    setStartFlags(true);
                    prize.click();
                }
            }
            break ;
        default:
            break ;
        }
    }
    private class SurfaceRunnable implements Runnable{
    
    
        @Override
        public void run() {
            while(flags){
                Canvas canvas=null;
                try {
                    canvas = mHolder.lockCanvas();

                    drawBg(canvas);

                    drawTransfer(canvas);

                    drawPrize(canvas);

                    controllerTransfer();
                } catch (Exception e) {
                    e.printStackTrace();
                }finally{
                    //涓轰簡璁╂瘡娆$粯鍒跺浘褰㈡椂鑳藉椤哄埄杩涜锛屾渶濂藉皢瑙i攣鏀惧埌寮傚父涓繘琛屽鐞嗭紝涔熷氨鏄锛屽鏋渃anvas涓嶄负绌猴紝閮藉皢鍏跺叧闂紝璁╀笅涓�娆″惊鐜兘澶熼『鍒╄繘琛岀粯鍒�
                    if(canvas!=null)
                        mHolder.unlockCanvasAndPost(canvas);    
                }
            }
        }
    }

    //绘制背景
    private void drawBg(Canvas canvas) {
        canvas.drawColor(Color.WHITE, Mode.CLEAR);
        int width = getMeasuredWidth()/3;
        int x1=0;
        int y1=0;

        int x2=0;
        int y2=0;

        int len = (int) Math.sqrt(prizes.size());

        for(int x=0;x<len*len;x++){

            Prize prize = prizes.get(x);

            int index=x;
            x1=getPaddingLeft()+width*(Math.abs(index)%len);
            y1=getPaddingTop()+width*(index/len);

            x2=x1+width;
            y2=y1+width;
            Rect rect=new Rect(x1,y1,x2,y2);

            Paint paint=new Paint();
            paint.setColor(prize.getBgColor());
            canvas.drawRect(rect, paint);
        }
    }

    //绘制旋转的方块
    private void drawTransfer(Canvas canvas) {
        int width = getMeasuredWidth()/3;
        int x1;
        int y1;

        int x2;
        int y2;
        int len = (int) Math.sqrt(prizes.size());
        current=next(current, len);
        x1=getPaddingLeft()+width*(Math.abs(current)%len);
        y1=getPaddingTop()+width*((current)/len);

        x2=x1+width;
        y2=y1+width;

        Rect rect=new Rect(x1,y1,x2,y2);
        Paint paint=new Paint();
        paint.setColor(transfer);
        canvas.drawRect(rect, paint);
    }

    //控制旋转
    private void controllerTransfer() {
        if(count>MAX){
            countDown++;
            SystemClock.sleep(count*5);
        }else{
            SystemClock.sleep(count*2);
        }

        count++;
        if(countDown>2){
            if(lottery==current){
                countDown=0;
                count=0;
                setStartFlags(false);

                if(listener!=null){
                    //切换到主线程中运行
                    post(new Runnable() {

                        @Override
                        public void run() {
                            listener.onWinning(current);
                        }
                    });

                }
            }
        }
    }

    public void setStartFlags(boolean flags){
        this.flags=flags;
    }

    //绘制奖品
    private void drawPrize(Canvas canvas) {
        int width = getMeasuredWidth()/3;
        int x1=0;
        int y1=0;

        int x2=0;
        int y2=0;

        int len = (int) Math.sqrt(prizes.size());

        for(int x=0;x<len*len;x++){

            Prize prize = prizes.get(x);

            int index=x;
            x1=getPaddingLeft()+width*(Math.abs(index)%len);
            y1=getPaddingTop()+width*(index/len);

            x2=x1+width;
            y2=y1+width;
            Rect rect=new Rect(x1+width/6,y1+width/6,x2-width/6,y2-width/6);
            prize.setRect(rect);
            canvas.drawBitmap(prize.getIcon(), null, rect, null);

        }
    }


    public void start() {
        setLottery(getRandom());
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(new SurfaceRunnable());
    }

    //获取随机中奖数,实际开发中一般中奖号码是服务器告诉我们的
    private int getRandom(){
        Random r=new Random();
        int nextInt =r.nextInt(prizes.size());
        if(nextInt%(Math.round(prizes.size()/2))==0){
            //随机号码等于中间开始位置,需要继续摇随机号
            return getRandom();
        }
        return nextInt;
    }

    //下一步
    public int next(int position,int len){
        int current=position;
        if(current+1<len){
            return ++current;
        }

        if((current+1)%len==0&&current<len*len-1){
            return current+=len;
        }

        if(current%len==0){
            return current-=len;
        }

        if(current<len*len){
            return --current;
        }

        return current;
    }


    public LotteryView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mHolder = this.getHolder();
        mHolder.addCallback(this);
    }

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

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Canvas canvas=null;
        try {
            canvas = mHolder.lockCanvas();
            drawBg(canvas);
            drawPrize(canvas);

            Prize prize = prizes.get(Math.round(prizes.size()/2));
            prize.setListener(new Prize.OnClickListener() {

                @Override
                public void onClick() {
                    start();
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            if(canvas!=null)
                mHolder.unlockCanvasAndPost(canvas);    
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        setStartFlags(false);
    }

    /** 
     * 重新测量
     */  
    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  
    {  
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
        int width = Math.min(getMeasuredWidth(), getMeasuredHeight());  
        setMeasuredDimension(width, width);  
    }
}

download link

Guess you like

Origin blog.csdn.net/huangxuanheng/article/details/53046744