今天學習了以下抽獎轉盤的實現
首先學習了以下 SurfaceView 的一般使用方法
下面的代碼是 寫 SurfaceView 的一個模板
package com.negro.myluckypan; import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.SurfaceHolder; import android.view.SurfaceView; /** * Created by Administrator on 2015/8/4 0004. */ public class SurfaceViewTempalte extends SurfaceView implements SurfaceHolder.Callback, Runnable { private SurfaceHolder mHolder ; private Canvas mCanvas ; private Thread mThread ; private boolean isRunning ; public SurfaceViewTempalte(Context context) { this(context, null); } public SurfaceViewTempalte(Context context, AttributeSet attrs) { super(context, attrs); mHolder = getHolder() ; mHolder.addCallback(this); // 可獲取焦點 setFocusable(true); setFocusableInTouchMode(true); // 設置常亮 setKeepScreenOn(true); } @Override public void surfaceCreated(SurfaceHolder holder) { isRunning = true ; mThread = new Thread(this) ; mThread.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { isRunning = false ; } @Override public void run() { while(isRunning) { draw() ; } } private void draw() { try { mCanvas = mHolder.lockCanvas() ; if(mCanvas == null) return ; // TODO draw something } catch (Exception e) { e.printStackTrace(); } finally { if(mCanvas != null) { mHolder.unlockCanvasAndPost(mCanvas); } } } }
想繪制什么東西,就用 canvas 在 draw 方法里面進行繪制
package com.negro.myluckypan; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.RectF; import android.text.TextPaint; import android.util.AttributeSet; import android.util.TypedValue; import android.view.SurfaceHolder; import android.view.SurfaceView; /** * Created by Administrator on 2015/8/4 0004. */ public class LuckyPan extends SurfaceView implements SurfaceHolder.Callback, Runnable { private SurfaceHolder mHolder ; private Canvas mCanvas ; private Thread mThread ; private boolean isRunning ; // 盤快的獎項 private String[] mStrs = new String[] { "單反相機", "IPAD", "恭喜發財", "IPHONE", "服裝一套", "恭喜發財" }; // 盤快的圖片 private int[] mImgs = new int[] { R.drawable.danfan, R.drawable.ipad, R.drawable.f015, R.drawable.iphone, R.drawable.meizi, R.drawable.f040, } ; // 與圖片對應的 bitmap 數組 private Bitmap[] mImgBitmap ; // 盤快的顏色 private int[] mColors = new int[] { 0xffffc300, 0xfff17e01, 0xffffc300, 0xfff17e01, 0xffffc300, 0xfff17e01 } ; private int mItemCount = 6 ; /// 整個盤快的范圍 private RectF mRange = new RectF(); // 整個盤快的直徑 private int mRadius ; // 繪制盤快的畫筆 private Paint mArcPaint ; // 繪制文本的畫筆 private Paint mTextPaint ; // 盤快滾動的速度 private double mSpeed = 0 ; // 初始的角度 線程間的可見性 private volatile float mStartAngle = 0 ; // 判斷是否點擊了停止按鈕 避免用戶一直點 private boolean isShouldEnd ; // 轉盤的中心位置 private int mCenter ; // 這里我們以 paddingleft 為准 private int mPadding ; // 背景圖片 private Bitmap mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.bg2) ; // 字體大小 private float mTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics()) ; public LuckyPan(Context context) { this(context, null); } public LuckyPan(Context context, AttributeSet attrs) { super(context, attrs); mHolder = getHolder() ; mHolder.addCallback(this); // 可獲取焦點 setFocusable(true); setFocusableInTouchMode(true); // 設置常亮 setKeepScreenOn(true); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = Math.min(getMeasuredWidth(), getMeasuredHeight()) ; mPadding = getPaddingLeft() ; // 直徑 mRadius = width - mPadding * 2 ; // 中心點 mCenter = width / 2 ; setMeasuredDimension(width, width); } @Override public void surfaceCreated(SurfaceHolder holder) { // 初始化繪制盤快的畫筆 mArcPaint = new Paint(); mArcPaint.setAntiAlias(true); mArcPaint.setDither(true); // 初始化繪制文本的畫筆 mTextPaint = new TextPaint(); mTextPaint.setColor(0xffffffff); mTextPaint.setTextSize(mTextSize); // 初始化盤快繪制的范圍 mRange = new RectF(mPadding, mPadding, mPadding + mRadius, mPadding + mRadius); // 初始化圖片 mImgBitmap = new Bitmap[mItemCount]; for(int i = 0; i < mItemCount; i ++) { mImgBitmap[i] = BitmapFactory.decodeResource(getResources(), mImgs[i]) ; } isRunning = true ; mThread = new Thread(this) ; mThread.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { isRunning = false ; } @Override public void run() { while(isRunning) { // 1秒繪制20次就可以了 long start = System.currentTimeMillis() ; draw() ; long end = System.currentTimeMillis() ; if(end - start < 50) { try { Thread.sleep(50 - (end - start)); } catch (InterruptedException e) { e.printStackTrace(); } } } } private void draw() { try { mCanvas = mHolder.lockCanvas() ; if(mCanvas == null) return ; // TODO draw something /////////////////// 繪制 ///////////////////////////// drawBg() ; float tmpAngle = mStartAngle ; float sweepAngle = 360 / mItemCount ; for(int i = 0; i < mItemCount; i ++) { mArcPaint.setColor(mColors[i]); // 繪制盤快 mCanvas.drawArc(mRange, tmpAngle, sweepAngle, true, mArcPaint); // 繪制文本 drawText(tmpAngle, sweepAngle, mStrs[i]) ; // 繪制圖片 drawIcon(tmpAngle, mImgBitmap[i]) ; tmpAngle += sweepAngle ; } mStartAngle += mSpeed ; // 如果點擊了 停止按鈕 if(isShouldEnd) { mSpeed -= 1 ; } if(mSpeed <= 0) { mSpeed = 0 ; isShouldEnd = false ; } } catch (Exception e) { e.printStackTrace(); } finally { if(mCanvas != null) { mHolder.unlockCanvasAndPost(mCanvas); } } } // 繪制 icon private void drawIcon(float tmpAngle, Bitmap bitmap) { // 設置圖片的寬度,為 直徑的 二分之一 int imgWidth = mRadius / 8 ; float angle = (float) ((tmpAngle + 360 / mItemCount / 2) * Math.PI / 180); // 確定圖片中心點的坐標 int x = (int) (mCenter + mRadius / 2 / 2 * Math.cos(angle)); int y = (int) (mCenter + mRadius / 2 / 2 * Math.sin(angle)); Rect rect = new Rect(x - imgWidth / 2, y - imgWidth / 2, x + imgWidth / 2, y + imgWidth / 2); mCanvas.drawBitmap(bitmap, null, rect, null); } // 繪制每個盤快的文本 private void drawText(float tmpAngle, float sweepAngle, String mStr) { Path path = new Path(); path.addArc(mRange, tmpAngle, sweepAngle); // 利用水平偏移量,讓文字居中 float textWidth = mTextPaint.measureText(mStr) ; int hOffset = (int) (mRadius * Math.PI / mItemCount / 2 - textWidth / 2); // 垂直偏移量 int vOffset = mRadius / 2 / 6 ; mCanvas.drawTextOnPath(mStr, path, hOffset, vOffset, mTextPaint); } // 繪制背景 private void drawBg() { mCanvas.drawColor(0xffffffff); mCanvas.drawBitmap(mBgBitmap, null, new Rect(mPadding / 2, mPadding / 2, getMeasuredWidth() - mPadding / 2, getMeasuredHeight() - mPadding / 2), null); } // 點擊 啟動旋轉 public void luckyStart(int index) { // 計算每一項的角度 float angle = 360 / mItemCount ; // 計算每一項的中獎范圍(當前index) // 1 -> 150 ~ 210 // 0 -> 210 ~ 270 float from = 270 - (index + 1) * angle ; float end = from + angle ; // 設置停下來需要旋轉的距離 float targetFrom = 4 * 360 + from ; float targetEnd = 4 * 360 + end ; /** * 速度是遞減的,每個周期 減一 * v1 v1 - 1 v1 - 2 ..... 0 * v1 * (v1 + 1) / 2 == targetFrom * 可以計算出 v1 = (-1 + Math.sqrt(1 + 8 * targetFrom)) / 2 */ float v1 = (float) ((-1 + Math.sqrt(1 + 8 * targetFrom)) / 2); float v2 = (float) ((-1 + Math.sqrt(1 + 8 * targetEnd)) / 2); mSpeed = v1 + Math.random() * (v2 - v1) ; isShouldEnd = false ; } // 點擊 停止旋轉 public void luckyEnd() { mStartAngle = 0 ; isShouldEnd = true ; } // 轉盤是否在旋轉 public boolean isStart() { return mSpeed != 0 ; } // 判斷 轉盤是否停止旋轉 public boolean isShouldEnd() { return isShouldEnd ; } }
在 MainActivity 里面
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mLuckyPan = (LuckyPan) findViewById(R.id.id_luckypan); mStartBtn = (ImageView) findViewById(R.id.id_start_btn); mStartBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if(!mLuckyPan.isStart()) { mLuckyPan.luckyStart(1); mStartBtn.setImageResource(R.drawable.stop); } else { if(!mLuckyPan.isShouldEnd()) { mLuckyPan.luckyEnd(); mStartBtn.setImageResource(R.drawable.start); } } } }); }