打造流畅九宫格抽奖活动
因为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();//监听CallBackmHolder.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);}@Overridepublic 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() {@Overridepublic 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;@Overrideprotected 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() {@Overridepublic 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;}@Overridepublic 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{@Overridepublic 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() {@Overridepublic 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);}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {}@Overridepublic 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() {@Overridepublic void onClick() {start();}});} catch (Exception e) {e.printStackTrace();}finally{if(canvas!=null)mHolder.unlockCanvasAndPost(canvas); }}@Overridepublic 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); }
}
下载地址
打造流畅九宫格抽奖活动相关推荐
- Android打造流畅九宫格抽奖
因为company项目中需要做九宫格抽奖活动,以前都没有做过类似的功能,虽然之前在浏览大神们的博客中,无意中也看到了好多关于抽奖的项目,但因为项目中没有需要,一直都没有点击进去看.这次不去看估计不行. ...
- 原生js实现运维小姐姐的九宫格抽奖活动、心跳快了
背景 带着我的小耳机坐在电脑前正YY着自己的女神(哦不,像我这么认真,负责,肯定是在狂飞乱舞的码着代码) 肩膀突然被人拍了一下,吓得我第一反应是Alt+Tab键切换了屏幕,屏幕正前方是显示着一行行代码 ...
- android 百度轻量输入法,百度发布Android手机输入法 打造流畅体验
日前,百度无线宣布正式推出Android版本手机输入法,凭借其拥有独家专利的多样输入.点划结合等创新技术,秉承百度一贯的"简单可依赖"的产品设计原则,将为Android用户带来最便 ...
- uni-app 九宫格抽奖活动
一.效果图 二.代码 <template><view class="container"><view class="container_ci ...
- 微信小程序九宫格抽奖活动制作
首先安装npm包 npm install @lucky-canvas/mini@latest 然后构建 npm 微信开发者工具 -> 找到左上角点击 -> 工具 点击 -> 构建 n ...
- 基于产生式的动物识别专家系统_钇鑫智通打造“极致流畅人脸识别闸机”
现在,人脸识别通道闸机越来越普及,但是由于技术的差异,导致人脸识别闸机的性能也有差异,人脸识别闸机其核心在人脸识别设备上,人脸机的配置,决定人脸识别的速度和准确率.人脸识别系统成功关键在于是否拥有尖端 ...
- 华为应用市场 AppGallery Connect 研习社·直播:百万开发者打造优质应用的奥秘
如何快速构建移动应用后端? 不升级应用,如何修改应用的行为和外观? 如何快速发现.定位并解决应用崩溃? 2月4日19点,华为应用市场AppGallery Connect专家团队将在线为大家带来丰富干货 ...
- 华为应用市场AppGallery Connect研习社·直播:百万开发者打造优质应用的奥秘
如何快速构建移动应用后端? 不升级应用,如何修改应用的行为和外观? 如何快速发现.定位并解决应用崩溃? 2月4日19点,华为应用市场AppGallery Connect专家团队将在线为大家带来丰富干货 ...
- 一本讲 Android 流畅性的书,应该有什么内容?
写在前面 最近读了一本新书:<打造流畅的 Android App>,京东链接:https://item.jd.com/10035215362170.html .因为书名所以买了这本书,读完 ...
最新文章
- lsa五类_OSPF外部路由4类5类LSA
- 黑白青春-纪念那年我的秋天
- python查询mysql表名字动态日期_Python之路day11作业-MySQL表查询
- php + mssql乱码
- Python 2.x 与Python 3.x的差别总结
- GUI编程与CLI编程
- IBASE write buffer
- linux视音频解码教程,音视频编解码:NVIDIA Jetson Linux Multimedia API(总结)
- VMware卸载有残留,再安装时报错提示MSI Failed
- 码农不得不做的P图辨识能力
- STM32学习心得三十一:485通信原理及实验
- 常见的协议号和端口号
- 查看电脑连接的wifi密码
- 非空子集的生成(二进制法)
- [位压] 高精度加法
- 让你的工作事半功倍的语音转文字转换器
- 微信公共服务平台开发(.Net 的实现)2-------获得ACCESSTOKEN
- 安服仔10大灵魂拷问
- 数学建模之SPSS的使用
- IPHONE 设置 铃声 方法
热门文章
- 用计算机解决问题时 为什么要用计算思维,在亲历计算机解决问题的全过程中发展计算思维...
- 怎么从微信群聊中引流?如何从群聊进行引流?微信群怎么引流
- 抖音广告营销被处罚限流,减少作品推荐该怎么办丨国仁网络资讯
- 使用java在后台将数据导出为excel文件
- c语言错误c2198,这段哪里错了。。。
- 线程安全的ArrayList——CopyOnWriteArrayList
- SSH介绍与神器Tabby
- 图文详解 HBase 的读写流程
- 硬核又实用,4款人气爆棚的电脑软件,每一款都堪称黑科技
- android创建以及使用SDcard镜像文件