首页 > 系统 > Android > 正文

Android打造流畅九宫格抽奖活动效果

2019-12-12 04:46:46
字体:
来源:转载
供稿:网友

因为company项目中需要做九宫格抽奖活动,以前都没有做过类似的功能,虽然之前在浏览大神们的博客中,无意中也看到了好多关于抽奖的项目,但因为项目中没有需要,一直都没有点击进去看。这次不去看估计不行。直到公司计划要做抽奖功能,才迫不得已上网查找demo

网上找了大半天,好不容易找到了几个demo,下载下来,解压缩包发现竟然里面空空如也,只有几张九宫格的图片,害我白白浪费了几个CSDN积分。后面在eoe网站那发现了一个demo,于是好开心,下载下来后马上导入到工程中,运行看了效果,九宫格是出来了,但效果真不敢恭维,主要是运行不流畅。但我还是进去稍微看了一下demo,基本思路是这样的:定义好九宫格界面,然后开启子线程不断循环修改状态,再通过handler发送消息到主线程中修改界面(子线程不能直接修改界面)。

这个demo虽然功能上实现了,但不是我想要的效果,因为我这一关都不能通过,到了产品那边更加不用说了。那怎么办呢?

于是我想到了一个控件,叫做SurfaceView,做游戏开发的同志们,应该对这个控件不陌生吧?首先介绍一下这个控件:
1.SurfaceView继承于View,多用于游戏开发中
2.可以直接在子线程中运行(其他UI控件都必须在主线程中运行的)。
3.一般的UI控件自定义时都是重写onDraw方法,但在SurfaceView中是通过SurfaceHolder获取Canvas来绘制图形的

好了,来吧各位,先来看看效果图:

这样,下面我开始根据我的想法,把自定义九宫格的步骤说一下。

步骤:

1.计算各位方块的位置
2.绘制每个奖品的方块(主要让界面更加好看)
3.绘制奖品图
4.计算旋转方块的下一步位置
5.绘制旋转方块
6.监听点击开始按钮事件

主要核心技术:
SurfaceView,SurfaceHolder

OK,有了基本步骤,接下来就是根据步骤一步一步来进行了。
在开始绘制九宫格之前,我们先重写onMeasure方法,主要是为了让九宫格成为一个正方形,这样看起来体验更好,基本代码如下:

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一般不是通过重写onDraw方法来绘制控件的,那么怎么获取到Canvas呢?主要是通过SurfaceHolder监听Callback事件来获取的
基本代码如下:

/**   * 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);  }

现在有了对象SurfaceHolder对象,我们就可以获取到Canvas对象了,下面开始真正的绘制工作。

1.计算方块的具体显示位置
2.绘制每个奖品的方块

  //绘制背景  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);    }  }

解析:prizes 是一个集合,里面封装了奖品的一些基本信息,x1,y1,x2,y2分别是绘制奖品容器正方形的左上顶点和右下顶点,

通过观察发现,每一个方块位置都有一定的关系,即 x1=getPaddingLeft()+width*(Math.abs(index)%len);

y1=getPaddingTop()+width*(index/len); x2=x1+width; y2=y1+width; 

有了这些点的关系,就可以通过canvas.drawRect(rect, paint);绘制出方块了

3.绘制奖品图

  //绘制奖品  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);    }  }

通过了步骤1,2知道了方块的位置关系,就可以轻松的根据这些关系绘制出奖品来,Rect rect=new Rect(x1+width/6,y1+width/6,x2-width/6,y2-width/6);是让奖品比方块缩小一些,这样看起来会更自然一点。

4.计算旋转方块的下一步位置

  //下一步  public int next(int position,int len){    int current=position;    if(current+1<len){      return ++current;    }    if((current+1)%len==0&¤t<len*len-1){      return current+=len;    }    if(current%len==0){      return current-=len;    }    if(current<len*len){      return --current;    }    return current;  }

position是当前旋转方块的位置,len是3

5.绘制旋转方块

  //绘制旋转的方块  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.监听点击开始按钮事件

  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);            }          });        }      }    }  }

至此,基本的自定义工作已经差不多了,使用demo如下:

<?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();      }    });  }}

运行效果非常流畅

LotteryView整体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&¤t<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);  }}

 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表