因為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{ //涓轟
主站蜘蛛池模板:
靖州|
恩施市|
博乐市|
南江县|
商都县|
泊头市|
永昌县|
衡南县|
渭源县|
华容县|
开封县|
南川市|
富川|
玉溪市|
岗巴县|
新蔡县|
大竹县|
胶南市|
汤原县|
泰兴市|
卢氏县|
蒲江县|
三原县|
麦盖提县|
江源县|
井研县|
莱阳市|
陇西县|
商河县|
丰镇市|
宝山区|
华安县|
黔南|
五家渠市|
裕民县|
盱眙县|
安吉县|
东平县|
奎屯市|
筠连县|
卫辉市|