Android打造流暢九宮格抽獎(jiǎng)活動(dòng)效果
因?yàn)閏ompany項(xiàng)目中需要做九宮格抽獎(jiǎng)活動(dòng),以前都沒有做過(guò)類似的功能,雖然之前在瀏覽大神們的博客中,無(wú)意中也看到了好多關(guān)于抽獎(jiǎng)的項(xiàng)目,但因?yàn)轫?xiàng)目中沒有需要,一直都沒有點(diǎn)擊進(jìn)去看。這次不去看估計(jì)不行。直到公司計(jì)劃要做抽獎(jiǎng)功能,才迫不得已上網(wǎng)查找demo
網(wǎng)上找了大半天,好不容易找到了幾個(gè)demo,下載下來(lái),解壓縮包發(fā)現(xiàn)竟然里面空空如也,只有幾張九宮格的圖片,害我白白浪費(fèi)了幾個(gè)CSDN積分。后面在eoe網(wǎng)站那發(fā)現(xiàn)了一個(gè)demo,于是好開心,下載下來(lái)后馬上導(dǎo)入到工程中,運(yùn)行看了效果,九宮格是出來(lái)了,但效果真不敢恭維,主要是運(yùn)行不流暢。但我還是進(jìn)去稍微看了一下demo,基本思路是這樣的:定義好九宮格界面,然后開啟子線程不斷循環(huán)修改狀態(tài),再通過(guò)handler發(fā)送消息到主線程中修改界面(子線程不能直接修改界面)。
這個(gè)demo雖然功能上實(shí)現(xiàn)了,但不是我想要的效果,因?yàn)槲疫@一關(guān)都不能通過(guò),到了產(chǎn)品那邊更加不用說(shuō)了。那怎么辦呢?
于是我想到了一個(gè)控件,叫做SurfaceView,做游戲開發(fā)的同志們,應(yīng)該對(duì)這個(gè)控件不陌生吧?首先介紹一下這個(gè)控件:
1.SurfaceView繼承于View,多用于游戲開發(fā)中
2.可以直接在子線程中運(yùn)行(其他UI控件都必須在主線程中運(yùn)行的)。
3.一般的UI控件自定義時(shí)都是重寫onDraw方法,但在SurfaceView中是通過(guò)SurfaceHolder獲取Canvas來(lái)繪制圖形的
好了,來(lái)吧各位,先來(lái)看看效果圖:
這樣,下面我開始根據(jù)我的想法,把自定義九宮格的步驟說(shuō)一下。
步驟:
1.計(jì)算各位方塊的位置
2.繪制每個(gè)獎(jiǎng)品的方塊(主要讓界面更加好看)
3.繪制獎(jiǎng)品圖
4.計(jì)算旋轉(zhuǎn)方塊的下一步位置
5.繪制旋轉(zhuǎn)方塊
6.監(jiān)聽點(diǎn)擊開始按鈕事件
主要核心技術(shù):
SurfaceView,SurfaceHolder
OK,有了基本步驟,接下來(lái)就是根據(jù)步驟一步一步來(lái)進(jìn)行了。
在開始繪制九宮格之前,我們先重寫onMeasure方法,主要是為了讓九宮格成為一個(gè)正方形,這樣看起來(lái)體驗(yàn)更好,基本代碼如下:
public class LotteryView extends SurfaceView{ /** * holder */ private SurfaceHolder mHolder; private List<Prize>prizes; private boolean flags; //抽獎(jiǎng)開關(guān) private int lottery=6; //設(shè)置中獎(jiǎng)號(hào)碼 private int current=2; //抽獎(jiǎng)開始的位置 private int count=0; //旋轉(zhuǎn)次數(shù)累計(jì) private int countDown; //倒計(jì)次數(shù),快速旋轉(zhuǎn)完成后,需要倒計(jì)多少次循環(huán)才停止 //旋轉(zhuǎn)抽獎(jiǎng)的方塊默認(rèn)顏色 private int transfer= 0xffff0000; private int MAX=50; //最大旋轉(zhuǎn)次數(shù) /** * 重新測(cè)量 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = Math.min(getMeasuredWidth(), getMeasuredHeight()); setMeasuredDimension(width, width); } }
SurfaceView一般不是通過(guò)重寫onDraw方法來(lái)繪制控件的,那么怎么獲取到Canvas呢?主要是通過(guò)SurfaceHolder監(jiān)聽Callback事件來(lái)獲取的
基本代碼如下:
/** * holder */ private SurfaceHolder mHolder; public LotteryView(Context context, AttributeSet attrs) { super(context, attrs); mHolder = this.getHolder(); //監(jiān)聽CallBack mHolder.addCallback(this); } public LotteryView(Context context) { this(context,null); }
現(xiàn)在有了對(duì)象SurfaceHolder對(duì)象,我們就可以獲取到Canvas對(duì)象了,下面開始真正的繪制工作。
1.計(jì)算方塊的具體顯示位置
2.繪制每個(gè)獎(jiǎng)品的方塊
//繪制背景 private void drawBg(Canvas canvas) { //清除已繪制的圖形 canvas.drawColor(Color.WHITE, Mode.CLEAR); //獲取控件的寬度,因?yàn)橐L制九宮格,所以要平局分成三列 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 是一個(gè)集合,里面封裝了獎(jiǎng)品的一些基本信息,x1,y1,x2,y2分別是繪制獎(jiǎng)品容器正方形的左上頂點(diǎn)和右下頂點(diǎn),
通過(guò)觀察發(fā)現(xiàn),每一個(gè)方塊位置都有一定的關(guān)系,即 x1=getPaddingLeft()+width*(Math.abs(index)%len);
y1=getPaddingTop()+width*(index/len); x2=x1+width; y2=y1+width;
有了這些點(diǎn)的關(guān)系,就可以通過(guò)canvas.drawRect(rect, paint);繪制出方塊了
3.繪制獎(jiǎng)品圖
//繪制獎(jiǎng)品 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); } }
通過(guò)了步驟1,2知道了方塊的位置關(guān)系,就可以輕松的根據(jù)這些關(guān)系繪制出獎(jiǎng)品來(lái),Rect rect=new Rect(x1+width/6,y1+width/6,x2-width/6,y2-width/6);是讓獎(jiǎng)品比方塊縮小一些,這樣看起來(lái)會(huì)更自然一點(diǎn)。
4.計(jì)算旋轉(zhuǎn)方塊的下一步位置
//下一步 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是當(dāng)前旋轉(zhuǎn)方塊的位置,len是3
5.繪制旋轉(zhuǎn)方塊
//繪制旋轉(zhuǎn)的方塊 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.監(jiān)聽點(diǎn)擊開始按鈕事件
private OnTransferWinningListener listener; public void setOnTransferWinningListener(OnTransferWinningListener listener){ this.listener=listener; } public interface OnTransferWinningListener{ /** * 中獎(jiǎng)回調(diào) * @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 ; } } //控制旋轉(zhuǎn) 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){ //切換到主線程中運(yùn)行 post(new Runnable() { @Override public void run() { listener.onWinning(current); } }); } } } }
至此,基本的自定義工作已經(jīng)差不多了,使用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(); } }); } }
運(yùn)行效果非常流暢
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; //設(shè)置中獎(jiǎng)號(hào)碼 private int current=2; //抽獎(jiǎng)開始的位置 private int count=0; //旋轉(zhuǎn)次數(shù)累計(jì) private int countDown; //倒計(jì)次數(shù),快速旋轉(zhuǎn)完成后,需要倒計(jì)多少次循環(huán)才停止 private int transfer= 0xffff0000; private int MAX=50; //最大旋轉(zhuǎn)次數(shù) private OnTransferWinningListener listener; public void setOnTransferWinningListener(OnTransferWinningListener listener){ this.listener=listener; } public interface OnTransferWinningListener{ /** * 中獎(jiǎng)回調(diào) * @param position */ void onWinning(int position); } /** * 設(shè)置中獎(jiǎng)號(hào)碼 * @param lottery */ public void setLottery(int lottery) { if(prizes!=null&&Math.round(prizes.size()/2)==0){ throw new RuntimeException("開始抽獎(jiǎng)按鈕不能設(shè)置為中獎(jiǎng)位置!"); } this.lottery = lottery; } /** * 設(shè)置轉(zhuǎn)盤顏色 * @param transfer */ public void setTransfer(int transfer) { this.transfer = transfer; } /** * 設(shè)置獎(jiǎng)品集合 * @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{ //涓轟簡(jiǎn)璁╂瘡嬈$粯鍒跺浘褰㈡椂鑳藉欏哄埄榪涜錛屾渶濂藉皢瑙i攣鏀懼埌寮傚父涓繘琛屽鐞嗭紝涔熷氨鏄錛屽鏋渃anvas涓嶄負(fù)絀猴紝閮藉皢鍏跺叧闂紝璁╀笅涓�嬈″驚鐜兘澶熼『鍒╄繘琛岀粯鍒� 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); } } //繪制旋轉(zhuǎn)的方塊 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); } //控制旋轉(zhuǎn) 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){ //切換到主線程中運(yùn)行 post(new Runnable() { @Override public void run() { listener.onWinning(current); } }); } } } } public void setStartFlags(boolean flags){ this.flags=flags; } //繪制獎(jiǎng)品 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()); } //獲取隨機(jī)中獎(jiǎng)數(shù),實(shí)際開發(fā)中一般中獎(jiǎng)號(hào)碼是服務(wù)器告訴我們的 private int getRandom(){ Random r=new Random(); int nextInt =r.nextInt(prizes.size()); if(nextInt%(Math.round(prizes.size()/2))==0){ //隨機(jī)號(hào)碼等于中間開始位置,需要繼續(xù)搖隨機(jī)號(hào) 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); } /** * 重新測(cè)量 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = Math.min(getMeasuredWidth(), getMeasuredHeight()); setMeasuredDimension(width, width); } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android 九宮格的實(shí)現(xiàn)方法
- android 九宮格滑動(dòng)解鎖開機(jī)實(shí)例源碼學(xué)習(xí)
- Android實(shí)現(xiàn)九宮格(GridView中各項(xiàng)平分空間)的方法
- 輕松實(shí)現(xiàn)Android自定義九宮格圖案解鎖
- Android實(shí)現(xiàn)九宮格解鎖
- Android實(shí)現(xiàn)九宮格橫向左右滑動(dòng)
- Android開發(fā)之實(shí)現(xiàn)GridView支付寶九宮格
- Android編程之九宮格實(shí)現(xiàn)方法實(shí)例分析
- 輕松實(shí)現(xiàn)安卓(Android)九宮格解鎖
- Android實(shí)現(xiàn)圖片九宮格
相關(guān)文章
android中可以通過(guò)兩種方式調(diào)用接口發(fā)送短信
調(diào)用系統(tǒng)短信接口直接發(fā)送短信;調(diào)起系統(tǒng)發(fā)短信功能,本文將給出兩種方式的實(shí)現(xiàn)代碼,感興趣的朋友可以了解下,或許對(duì)你有所幫助2013-02-02Android實(shí)現(xiàn)動(dòng)態(tài)添加標(biāo)簽及其點(diǎn)擊事件
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)動(dòng)態(tài)添加標(biāo)簽及其點(diǎn)擊事件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12Android實(shí)現(xiàn)指針刻度轉(zhuǎn)盤
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)指針刻度轉(zhuǎn)盤,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-08-08Android編程實(shí)現(xiàn)切換imageView的方法分析
這篇文章主要介紹了Android編程實(shí)現(xiàn)切換imageView的方法,結(jié)合具體實(shí)例形式分析了切換imageView的相關(guān)設(shè)置技巧與注意事項(xiàng),需要的朋友可以參考下2017-09-09Android activity堆棧及管理實(shí)例詳解
這篇文章主要介紹了Android activity堆棧及管理實(shí)例詳解的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,對(duì)android activity堆棧相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)吧2016-09-09Android熱更新開源項(xiàng)目Tinker集成實(shí)踐總結(jié)
最近項(xiàng)目集成了Tinker,開始認(rèn)為集成會(huì)比較簡(jiǎn)單,但是在實(shí)際操作的過(guò)程中還是遇到了一些問(wèn)題,本文就會(huì)介紹在集成過(guò)程大家基本會(huì)遇到的主要問(wèn)題。下面跟著小編一起來(lái)看下吧2017-01-01Android獲取手機(jī)屏幕寬高、狀態(tài)欄高度以及字符串寬高信息的方法
這篇文章主要介紹了Android獲取手機(jī)屏幕寬高、狀態(tài)欄高度以及字符串寬高信息的方法,涉及Android獲取文字寬高、狀態(tài)欄高度、textView寬度及屏幕尺寸的相關(guān)技巧,需要的朋友可以參考下2015-04-04