android自定义view抽奖效果,Android 自定义View 之九宫格抽奖View

最近在项目中要实现一个九宫格抽奖view 。中间是抽奖按钮,八个格子是奖品。效果图如下:

5af12da9a8b2

九宫格抽奖View

接下来我就分析一下实现这个View的步骤:

1.绘制出外框(此处难点是绘制闪光点的效果);

2.绘制九个格子,这个就是计算均分的逻辑,比较简单。

3.实现抽奖动效,以及点击中间start按钮有个缩放效果的实现。

我一一分析一下。

1.绘制外边框:

见代码:核心是使用  canvas.drawRoundRect(rectF, radiusBg, radiusBg, bgPaint);方法绘制圆角边框矩形,绘制内外两个边框矩形,重叠在一起(此处起初想使用画笔画边框,但实现起来只能内边框才有圆角,外边框是直角)。

接下来就是绘制四个角上的小原点(原点是图片),这样做是保证四个角的图片一致,此处逻辑就是计算四个角上的位置稍微麻烦点;然后计算四条边上的点。最后,使用postDelayed重复绘制,达到闪烁的效果。

public class LuckyDrawLayout extends RelativeLayout {

private static final StringTAG ="LuckyDrawLayout";

private Paint bgPaint;

private int mWidth, mHeight;

private int radiusBg;

private Rect FrectF =new RectF();

private Bitmap smallGreenBitmap;

private Bitmap smallRedBitmap;

private int ballWidth, ballHeight;

private int redBallWidth, redBallHeight;

private RectF ballRectf;

private int innerPadding =dip2px(15);

private boolean isChanged =true;

private int eachRow =13;

public LuckyDrawLayout(Context context) {

this(context, null);

}

public LuckyDrawLayout(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public LuckyDrawLayout(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

bgPaint =new Paint(Paint.ANTI_ALIAS_FLAG);

bgPaint.setStyle(Paint.Style.FILL);

bgPaint.setStrokeCap(Paint.Cap.ROUND);

bgPaint.setStrokeJoin(Paint.Join.ROUND);

bgPaint.setAntiAlias(true);

bgPaint.setDither(true);

bgPaint.setColor(0xFFFF356B);

smallGreenBitmap = BitmapFactory.decodeResource(getContext().getResources(), R.mipmap.ic_small_green);

smallRedBitmap = BitmapFactory.decodeResource(getContext().getResources(), R.mipmap.ic_small_red);

ballWidth =smallGreenBitmap.getWidth();

ballHeight =smallGreenBitmap.getHeight();

redBallWidth =smallRedBitmap.getWidth();

redBallHeight =smallRedBitmap.getHeight();

ballRectf =new RectF();

setWillNotDraw(false);

changeBall();

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

int mWidth = MeasureSpec.getSize(widthMeasureSpec);

int mHeight = MeasureSpec.getSize(heightMeasureSpec);

int size = Math.min(mWidth, mHeight);

setMeasuredDimension(size, size);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

mWidth = getWidth();

mHeight = getHeight();

radiusBg =mWidth /40;

rectF.set(0, 0, mWidth, mHeight);

bgPaint.setColor(0xFFFF356B);

canvas.drawRoundRect(rectF, radiusBg, radiusBg, bgPaint);

rectF.set(innerPadding, innerPadding, mWidth -innerPadding, mHeight -innerPadding);

bgPaint.setColor(0xFFCE0037);

canvas.drawRoundRect(rectF, radiusBg, radiusBg, bgPaint);

drawFourCorner(canvas);

int ballGapDp = (mWidth -innerPadding *2) /eachRow;

for (int i =0; i

if (getGreen(i)) {

//上

ballRectf.set(innerPadding *2 + i * ballGapDp -ballWidth /2, innerPadding /2 -ballHeight /2, innerPadding *2 +ballWidth /2 + i * ballGapDp, innerPadding /2 +ballHeight /2);

canvas.drawBitmap(smallGreenBitmap, null, ballRectf, null);

//下

ballRectf.set(innerPadding *2 + i * ballGapDp -ballWidth /2, mHeight - (innerPadding /2 +ballHeight /2), innerPadding *2 +ballWidth /2 + i * ballGapDp, mHeight - (innerPadding /2 -ballHeight /2));

canvas.drawBitmap(smallGreenBitmap, null, ballRectf, null);

//左

ballRectf.set(innerPadding /2 -ballWidth /2, innerPadding *2 + i * ballGapDp -ballHeight /2, innerPadding /2 +ballWidth /2, innerPadding *2 + i * ballGapDp +ballHeight /2);

canvas.drawBitmap(smallGreenBitmap, null, ballRectf, null);

//右

ballRectf.set(mWidth - (innerPadding /2 +ballWidth /2), innerPadding *2 + i * ballGapDp -ballHeight /2, mWidth - (innerPadding /2 -ballWidth /2), innerPadding *2 + i * ballGapDp +ballHeight /2);

canvas.drawBitmap(smallGreenBitmap, null, ballRectf, null);

}else {

ballRectf.set(innerPadding *2 + i * ballGapDp -redBallWidth /2, innerPadding /2 -redBallHeight /2, innerPadding *2 +redBallWidth /2 + i * ballGapDp, innerPadding /2 +redBallHeight /2);

canvas.drawBitmap(smallRedBitmap, null, ballRectf, null);

ballRectf.set(innerPadding *2 + i * ballGapDp -redBallWidth /2, mHeight - (innerPadding /2 +redBallHeight /2), innerPadding *2 +redBallWidth /2 + i * ballGapDp, mHeight - (innerPadding /2 -redBallHeight /2));

canvas.drawBitmap(smallRedBitmap, null, ballRectf, null);

ballRectf.set(innerPadding /2 -redBallWidth /2, innerPadding *2 + i * ballGapDp -redBallHeight /2, innerPadding /2 +redBallWidth /2, innerPadding *2 + i * ballGapDp +redBallHeight /2);

canvas.drawBitmap(smallRedBitmap, null, ballRectf, null);

ballRectf.set(mWidth - (innerPadding /2 +redBallWidth /2), innerPadding *2 + i * ballGapDp -redBallHeight /2, mWidth - (innerPadding /2 -redBallWidth /2), innerPadding *2 + i * ballGapDp +redBallHeight /2);

canvas.drawBitmap(smallRedBitmap, null, ballRectf, null);

}

}

}

@Override

protected void onLayout(boolean changed, int l, int t, int r, int b) {

super.onLayout(changed, l, t, r, b);

for (int i =0; i < getChildCount(); i++) {

View child = getChildAt(i);

if (childinstanceof LuckyDrawView) {

child.layout(innerPadding, innerPadding, getWidth() -innerPadding, getHeight() -innerPadding);

}

}

}

private void changeBall() {

postDelayed(new Runnable() {

@Override

public void run() {

isChanged = !isChanged;

Log.d(TAG, "run: changeBall()");

invalidate();

postDelayed(this, 300);

}

}, 300);

}

private boolean getGreen(int i) {

if (isChanged) {

return i %2 !=0;

}else {

return i %2 ==0;

}

}

private void drawFourCorner(Canvas canvas) {

if (isChanged) {

RectF leftTopRectf =new RectF(innerPadding /2 -ballWidth /2, innerPadding /2 -ballHeight /2, innerPadding /2 +ballWidth /2, innerPadding /2 +ballHeight /2);

canvas.drawBitmap(smallGreenBitmap, null, leftTopRectf, null);

RectF rightTopRectf =new RectF(mWidth - (innerPadding /2 +ballWidth /2), innerPadding /2 -ballHeight /2, mWidth - (innerPadding /2 -ballWidth /2), innerPadding /2 +ballHeight /2);

canvas.drawBitmap(smallGreenBitmap, null, rightTopRectf, null);

RectF leftBottomRectf =new RectF(innerPadding /2 -ballWidth /2, mHeight - (innerPadding /2 +ballHeight /2), innerPadding /2 +ballWidth /2, mHeight - (innerPadding /2 -ballHeight /2));

canvas.drawBitmap(smallGreenBitmap, null, leftBottomRectf, null);

RectF rightBottomRectf =new RectF(mWidth - (innerPadding /2 +ballWidth /2), mHeight - (innerPadding /2 +ballHeight /2), mWidth - (innerPadding /2 -ballWidth /2), mHeight - (innerPadding /2 -ballHeight /2));

canvas.drawBitmap(smallGreenBitmap, null, rightBottomRectf, null);

}else {

RectF leftTopRectf =new RectF(innerPadding /2 -redBallWidth /2, innerPadding /2 -redBallHeight /2, innerPadding /2 +redBallWidth /2, innerPadding /2 +redBallHeight /2);

canvas.drawBitmap(smallRedBitmap, null, leftTopRectf, null);

RectF rightTopRectf =new RectF(mWidth - (innerPadding /2 +redBallWidth /2), innerPadding /2 -redBallHeight /2, mWidth - (innerPadding /2 -redBallWidth /2), innerPadding /2 +redBallHeight /2);

canvas.drawBitmap(smallRedBitmap, null, rightTopRectf, null);

RectF leftBottomRectf =new RectF(innerPadding /2 -redBallWidth /2, mHeight - (innerPadding /2 +redBallHeight /2), innerPadding /2 +redBallWidth /2, mHeight - (innerPadding /2 -redBallHeight /2));

canvas.drawBitmap(smallRedBitmap, null, leftBottomRectf, null);

RectF rightBottomRectf =new RectF(mWidth - (innerPadding /2 +redBallWidth /2), mHeight - (innerPadding /2 +redBallHeight /2), mWidth - (innerPadding /2 -redBallWidth /2), mHeight - (innerPadding /2 -redBallHeight /2));

canvas.drawBitmap(smallRedBitmap, null, rightBottomRectf, null);

}

}

public static int dip2px(float dipValue) {

final float scale = Resources.getSystem().getDisplayMetrics().density;

return (int) (dipValue * scale +0.5f);

}

/**

* 将px值转换为sp值,保证文字大小不变

*/

public static int px2sp(Context context, float pxValue) {

final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;

return (int) (pxValue / fontScale +0.5f);

}

/**

* 将sp值转换为px值,保证文字大小不变

*/

public static int sp2px(Context context, float spValue) {

final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;

return (int) (spValue * fontScale +0.5f);

}

}

2.绘制九宫格:

先见代码:主要就是计算每个格子的宽高,要均分,针对中间的格子做特殊处理,具体逻辑计算看下面drawNineCell 方法;然后绘制文本,居中显示即可。接下来,实现转动的逻辑,private int[]positions = {0, 1, 2, 5, 8, 7, 6, 3}; //顺时针 这个记录了转动的顺序,然后用变量currentPosition 记录当前的位置下标,通过positions[currentPosition]取出对应的格子,然后绘制该格子的显示样式。开启了一个线程计算currentPosition的值,为了实现一个快要中奖停顿的效果,在线程中转动最后一圈的时候,使用SystemClock.sleep(100 * (currentPosition +1));让子线程睡眠时间递增,currentLoopCount记录转动的圈数,默认4圈;stopPosition停止的位置。最后实现点击中间按钮缩放的效果,先计算中间按钮的矩形位置mCenterButtonRectF,设置onTouchListener事件,计算点击的区域是否是在mCenterButtonRectF中,mCenterButtonRectF.contains(x, y)。如果在该区域中就执行缩放效果。具体看代码。

public class LuckyDrawView extends View {

private static final StringTAG ="LuckyDrawView";

//0->1->2->3->5->6->7->8

//0-1-2-5-8-7-6-3

private int currentPosition =0;

private int stopPosition = -1;

private final static int LOOP_COUNT =4;

private int currentLoopCount =0;

private Paint bgPaint;

private int mWidth, mHeight;

private int radiusBg;

private Paint cellPaint;

private Paint cellTextPaint;

private int innerEachGap =dip2px(6);

private int innerWidth, innerHeight;

private int eachWidth, eachHeight;

private boolean onTouchCenter =false;

private RectFmCenterButtonRectF;

private String[]rewardTexts = {"$0.04", "$0.10", "$0.80", "$0.85", "", "$3.00", "$5.00", "$0.15", "$0.10"};

private int[]positions = {0, 1, 2, 5, 8, 7, 6, 3}; //顺时针

String start ="Start";

float scale =1.0f;

private boolean isRuning =false;

public LuckyDrawView(Context context) {

this(context, null);

}

public LuckyDrawView(Context context, AttributeSet attrs) {

this(context, attrs, 0);

}

public LuckyDrawView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

bgPaint =new Paint(Paint.ANTI_ALIAS_FLAG);

bgPaint.setStyle(Paint.Style.FILL);

bgPaint.setStrokeCap(Paint.Cap.ROUND);

bgPaint.setStrokeJoin(Paint.Join.ROUND);

bgPaint.setAntiAlias(true);

bgPaint.setDither(true);

bgPaint.setColor(0xFFFF356B);

cellPaint =new Paint(Paint.ANTI_ALIAS_FLAG);

cellPaint.setStyle(Paint.Style.FILL);

cellPaint.setColor(Color.WHITE);

cellTextPaint =new Paint(Paint.ANTI_ALIAS_FLAG);

cellTextPaint.setTextSize(sp2px(context, 26));

cellTextPaint.setColor(Color.WHITE);

cellTextPaint.setTypeface(Typeface.DEFAULT_BOLD);

cellTextPaint.setAntiAlias(true);

setOnTouchListener(new OnTouchListener() {

@Override

public boolean onTouch(View v, MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

onTouchCenter =false;

int x = (int) event.getX();

int y = (int) event.getY();

if (mCenterButtonRectF.contains(x, y) && !isRuning) {

if (scale !=0.8f) {

scale =0.8f;

invalidate();

}

onTouchCenter =true;

}

break;

case MotionEvent.ACTION_UP:

if (onTouchCenter) {

startPressScaleAnim();

startLoop();

}

onTouchCenter =false;

break;

}

return true;

}

});

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

mWidth = getWidth();

mHeight = getHeight();

radiusBg =mWidth /40;

innerWidth =mWidth -innerEachGap *4;

innerHeight =mHeight -innerEachGap *4;

eachWidth =innerWidth /3;

eachHeight =innerHeight /3;

drawNineCell(canvas);

}

private void drawNineCell(Canvas canvas) {

int nums =9;

RectF rectF =new RectF();

for (int i =0; i < nums; i++) {

int startX =innerEachGap + (i %3) * (eachWidth +innerEachGap);

int startY =innerEachGap + (i /3) * (eachHeight +innerEachGap);

rectF.set(startX, startY, startX +eachWidth, startY +eachHeight);

if (i == nums /2) {

cellPaint.setColor(0xFFFFE535);

bgPaint.setColor(0xFFFF356B);

rectF.set(rectF.left + rectF.left * (1 -scale) *0.08f, rectF.top + rectF.top * (1 -scale) *0.08f, rectF.right - rectF.right * (1 -scale) *0.08f, rectF.bottom - rectF.bottom * (1 -scale) *0.08f);

canvas.drawRoundRect(rectF, radiusBg, radiusBg, cellPaint);

mCenterButtonRectF =new RectF(rectF);

rectF.set(rectF.left +dip2px(10), rectF.top +dip2px(10), rectF.right -dip2px(10), rectF.bottom -dip2px(10));

canvas.drawRoundRect(rectF, radiusBg, radiusBg, bgPaint);

cellTextPaint.setColor(Color.WHITE);

canvas.drawText(start, rectF.centerX() -cellTextPaint.measureText(start) /2, rectF.centerY() + getTextDiffY(cellTextPaint), cellTextPaint);

}else {

if (positions[currentPosition] == i) {

cellPaint.setColor(0xFFFBC01B);

cellTextPaint.setColor(Color.WHITE);

}else {

cellPaint.setColor(Color.WHITE);

cellTextPaint.setColor(0xFFFF5A00);

}

canvas.drawRoundRect(rectF, radiusBg, radiusBg, cellPaint);

canvas.drawText(rewardTexts[i], rectF.centerX() -cellTextPaint.measureText(rewardTexts[i]) /2, rectF.centerY() + getTextDiffY(cellTextPaint), cellTextPaint);

}

}

}

private float getTextDiffY(Paint paint) {

Paint.FontMetrics fontMetrics = paint.getFontMetrics();

return Math.abs(fontMetrics.descent - fontMetrics.ascent) /2 - fontMetrics.descent;

}

private void startLoop() {

currentLoopCount =0;

Random random =new Random();

stopPosition = random.nextInt(7);

currentPosition =0;

new Thread(action).start();

}

private Runnableaction =new Runnable() {

@Override

public void run() {

while (true) {

isRuning =true;

if (currentLoopCount >=LOOP_COUNT) {

isRuning =false;

postDelayed(new Runnable() {

@Override

public void run() {

Toast.makeText(getContext(), "恭喜你抽中了position=" +stopPosition +"(" +rewardTexts[positions[stopPosition]] +")", Toast.LENGTH_LONG).show();

}

}, 500);

break;

}

currentPosition++;

if (currentPosition >7) {

currentLoopCount++;

currentPosition =0;

}

post(new Runnable() {

@Override

public void run() {

invalidate();

}

});

if (currentLoopCount ==LOOP_COUNT -1) {

if (currentPosition %7 ==stopPosition) {

if (currentPosition ==stopPosition) {

currentLoopCount =LOOP_COUNT;

}

}

SystemClock.sleep(100 * (currentPosition +1));

}else {

SystemClock.sleep(100);

}

}

}

};

private void startPressScaleAnim() {

ValueAnimator valueAnimator = ValueAnimator.ofFloat(0.8f, 1.0f);

valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

scale = ((float) animation.getAnimatedValue());

invalidate();

}

});

valueAnimator.setDuration(300);

valueAnimator.start();

}

public static int dip2px(float dipValue) {

final float scale = Resources.getSystem().getDisplayMetrics().density;

return (int) (dipValue * scale +0.5f);

}

/**

* 将sp值转换为px值,保证文字大小不变

*/

public static int sp2px(Context context, float spValue) {

final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;

return (int) (spValue * fontScale +0.5f);

}

}

不足之处:

1.绘制了两层外框,中间的红色区域绘制了两次,导致过渡绘制了。起初的想法是用bgPaint.setStyle(Paint.Style.Stroke);绘制边框,然而绘制出的是内部是圆角,外角还是直角。

2.暂只支持文本的显示,未设置图片的显示。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值