Android打造流畅九宫格抽奖活动效果
因为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); } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: Android自定义商品购买数量加减控件