如何使用Android属性动画实现Roulette效果?
like this:
效果图
一、问题拆解
该效果可以拆分成三个小问题。
1、如何用数字绘制一个任意大小的圆形?
2、如何让指针旋转并停在指定数字?
3、如何实现先加速后减速的旋转效果?
二、具体实现
1、如何用数字绘制一个任意大小的圆形?
这个问题可转换成:如何将字符按照任意曲线的路径进行绘制?
a、绘制曲线:可以用Path类来实现,Path提供了多种曲线的绘制,圆、椭圆、矩形、二阶三阶贝塞尔曲线等等。
b、按曲线路径绘制字符:Canvas类提够了一个方法drawTextOnPath,可以根据曲线路径来绘制字符。
//绘制圆
mPath.addCircle(0,0,mRadius,Path.Direction.CW);
canvas.drawPath(mPath,mPaint);
//按曲线绘制字符
//mPath:曲线路径,hOffset:水平偏移值(字符之间的距离),vOffset:垂直偏移值(字符和曲线的距离)
canvas.drawTextOnPath("按照曲线绘制",mPath,hOffset,vOffset,mPaint);
2、如何让指针旋转并停在指定数字?
a、指针旋转:其实就是一张图片选择,Matrix提够了图片的旋转、缩放、平移、错切变换等方法。所以这里使用Matrix可以解决图片旋转的问题。
//设置旋转角度,90度
mMatrix.postRotate(90,dx ,dy);
//mBitmap:旋转图片
canvas.drawBitmap(mBitmap,mMatrix,mPaint);
b、指针停在指定数字:
要让指针停在指定位置,就要获取当前位置的角度,该角度可以通过计算获得。
假如,LP可投注数值为10个(1—10),那么数字之前的夹角就是 360/10 = 36。
那么假如,我们要获取n的角度,n_degrees = n*36
//每个号码的平均弧度值
float average = 360/10;
//计算winNumber(开奖号码)所在的弧度值
float winDegrees = (average * winNumber + average/2 );
3、如何实现先加速后减速的旋转效果?
这里android提供的插值器 AccelerateDecelerateInterpolator,就是先加速再减速插值器
//属性动画
ValueAnimator animator = ValueAnimator.ofFloat(0,360);
//动画执行5秒
animator.setDuration(1000*5);
animator.setRepeatCount(0);
//先加速再减速插值器
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
invalidate();
}
});
三、Roulette的完整代码
package com.metre.roulette_demo;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.Typeface;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
/**
* Created by mi on 2019/6/10
*/
public class RouletteView extends View {
public RouletteView(Context context) {
this(context,null);
}
public RouletteView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public RouletteView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr,0);
}
public RouletteView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
//画笔
private Paint mPaint;
//曲线
private Path mPath;
//Matrix(图片旋转缩放平移错切)
private Matrix mMatrix;
//指针图片
private Bitmap mBitmap;
private boolean onRunning;
//屏幕中心点坐标
private float mCentX;
private float mCentY;
//开奖号码
private int winNumber;
//圆的半径
private float mRadius = 400;
//当前角度
private float mCurrentRotateRadius = -90;
//默认开奖指针开始位置
private float defaultPointer = 360 - 90;
//圆周长
private int mCircleRadius = 360;
//指针旋转次数(一次动画内)
private int mRotateCount = 10;
//指针旋转次数
private int mBetCount = 27;
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.mCentX = w/2;
this.mCentY = h/2;
}
private void init(){
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(4);
mPaint.setColor(Color.GRAY);
mPaint.setAntiAlias(true);
mPath = new Path();
mMatrix = new Matrix();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;//缩小图片
mBitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.arrow,options);
}
//开始开奖
public void startOpenWin(){
if(onRunning)return;
winNumber = (int) (Math.random()*mBetCount+1);
startAnimator();
}
//启动动画
private void startAnimator(){
//每个号码的平均弧度值
float average = mCircleRadius/mBetCount;
//计算开奖号码所在的弧度值
float winDegrees = (average * (winNumber-1)) - 90 + ( winNumber >= 10 ? average : average/2 );
//当弧度值小于0
if(winDegrees < 0){
winDegrees = mCircleRadius + winDegrees;
}
//最大变化值: (360 x 一次动画指针旋转的次数)+ 开奖号码所在的弧度
float maxChangeValue = mCircleRadius * mRotateCount + winDegrees;
Log.d("RouletteView","startAnimator | winDegrees-------->"+winDegrees);
Log.d("RouletteView","startAnimator | maxChangeValue---->"+maxChangeValue);
//属性动画
ValueAnimator animator = ValueAnimator.ofFloat(defaultPointer,maxChangeValue);
//动画执行5秒
animator.setDuration(1000*5);
animator.setRepeatCount(0);
//先加速再减速插值器
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
//转换成360度弧值
mCurrentRotateRadius = convertRadius(value);
Log.d("RouletteView","AnimatorUpdateListener | mCurrentRotateRadius--->"+mCurrentRotateRadius);
invalidate();
}
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
onRunning = false;
//记录上一次动画的指针
defaultPointer = mCurrentRotateRadius;
}
});
animator.start();
onRunning = true;
}
//转换成0-360度弧值
private float convertRadius(float value){
Log.d("RouletteView","convertRadius | value--->"+value);
if( value < mCircleRadius )return value;
float tmp = value - mCircleRadius;
return convertRadius(tmp);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//画布移到中心
canvas.translate(mCentX,mCentY);
mPaint.setStyle(Paint.Style.STROKE);
mPath.reset();
//绘制圆
mPath.addCircle(0,0,mRadius,Path.Direction.CW);
canvas.drawPath(mPath,mPaint);
//绘制开奖号码
mPaint.setStyle(Paint.Style.FILL);
canvas.drawText(""+winNumber,-4,-500,mPaint);
//画布逆时针旋转90
canvas.rotate(-90);
@SuppressLint("DrawAllocation")
PathMeasure pathMeasure = new PathMeasure(mPath,false);
//圆周长
float distance = pathMeasure.getLength();
mPaint.setTextSize(60);
mPaint.setTypeface(Typeface.DEFAULT_BOLD);
//水平偏移值(数字之间的距离)
float hOffset = distance/mBetCount;
//垂直偏移值(和圆的距离)
float vOffset = -6;
//绘制27个值
for(int j = 0 ; j < mBetCount;j++){
canvas.drawTextOnPath(""+(j+1),mPath,j*hOffset,vOffset,mPaint);
}
drawPointer(canvas);
}
//绘制指针
private void drawPointer(Canvas canvas){
float dx = mBitmap.getWidth()/2;
float dy = mBitmap.getHeight()/10;
mMatrix.reset();
mMatrix.postRotate(mCurrentRotateRadius,dx ,dy);
mMatrix.postTranslate(-dx,-dy);
canvas.drawBitmap(mBitmap,mMatrix,mPaint);
}
}













网友评论