这里的转盘主要实现的重点是绘制每块答案区域的文本,并绘制出来,而转盘和背景只是张图片
1、绘制文本的位置区域
2、获取旋转动画
3、提供点击事件
4、提供接口
1、初始化变量
private List mRollGameTextList;
private int mRollGameTextCount = 0;
private Paint mTextPaint;
private int mTextSize = 14;
private RectF mRange; //View的区域
private int mRadius; //直径
private float mStartAngle = 22.5f; //开始的角度
private int mWidth;
private boolean isRolling = false;
private float fromDegress = 0;
private float toDegress = 0;
//拼手气的正确答案位置
private int mAnswerPosition = 0;
//拼手气转盘转一圈的时间
private int mTimeForPer = 800;
//拼手气转盘的圈数
private int mRollCount = 3;
//拼手气转盘第一行文本的个数
private int mFirstLineTextCount = 4;
//当前转盘的启动位置,只能在创建转盘完成之后调用
private int mCurrentPosition = 0;
//点击事件的文本区域
private HashMap mRollGameTextPath;
//点击事件的全局区域
private Region mGlobalRegion;
2、测量大小
public RollGameTextViewList(Context context) {
this(context, null);
}
public RollGameTextViewList(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public RollGameTextViewList(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mTextPaint = new Paint();
mTextPaint.setColor(Color.parseColor("#611E14"));
mTextPaint.setTextSize(dip2px(context, mTextSize));
mRollGameTextPath = new HashMap<>();
}
/**
* 设置为正方形
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = Math.min(getMeasuredWidth(), getMeasuredHeight());
mRadius = mWidth - getPaddingLeft() - getPaddingRight();
setMeasuredDimension(mWidth, mWidth);
if (mRange == null) {
mRange = new RectF(getPaddingLeft(), getPaddingTop(), mRadius
+ getPaddingLeft(), mRadius + getPaddingTop());
} else {
mRange.set(getPaddingLeft(), getPaddingTop(), mRadius
+ getPaddingLeft(), mRadius + getPaddingTop());
}
if (mGlobalRegion == null) {
mGlobalRegion = new Region(-mWidth, -mWidth, mWidth, mWidth);
} else {
mGlobalRegion.set(-mWidth, -mWidth, mWidth, mWidth);
}
}
3、绘制文本
@Override
protected void onDraw(Canvas canvas) {
if (checkStartCondition()) {
//设置起始位置
mStartAngle = -112.5f - (mCurrentPosition * 45);
canvas.save();
float tmpAngle = mStartAngle;
float sweepAngle = (float) (360 / mRollGameTextCount);
for (int i = 0; i < mRollGameTextList.size(); i++) {
drawText(canvas, tmpAngle, sweepAngle, mRollGameTextList.get(i), i);
tmpAngle += sweepAngle;
}
canvas.restore();
}
}
/**
* 绘制字体
*
* @param canvas
* @param startAngle
* @param sweepAngle
* @param text
*/
private void drawText(Canvas canvas, float startAngle, float sweepAngle, String text, int position) {
if (text.length() >= mFirstLineTextCount) {
drawLineTwo(canvas, startAngle, sweepAngle, text, position);
} else {
drawLineOne(canvas, startAngle, sweepAngle, text, position);
}
}
/**
* 兼容两行文本
*
* @param canvas
* @param startAngle
* @param sweepAngle
* @param text
*/
private void drawLineTwo(Canvas canvas, float startAngle, float sweepAngle, String text, int position) {
String textForWard = text.substring(0, mFirstLineTextCount);
String textForBack = text.substring(mFirstLineTextCount, text.length());
int textWidthForWard = measureTextView(textForWard).width();
int textHeightForWard = measureTextView(textForWard).height();
int textWidthForBack = measureTextView(textForBack).width();
int textHeightForBack = measureTextView(textForBack).height();
float h1_Offset = (float) (mRadius * Math.PI / mRollGameTextCount / 2 - textWidthForWard / 2);// 水平偏移:圆长/个数/2 - 文本宽度/2
float v1_Offset = textHeightForWard;// 垂直偏移
float h2_Offset = (float) (mRadius * Math.PI / mRollGameTextCount / 2 - textWidthForBack / 2);// 水平偏移:圆长/个数/2 - 文本宽度/2
float v2_Offset = textHeightForWard + textHeightForBack;// 垂直偏移
Path path = new Path();
path.addArc(mRange, startAngle, sweepAngle);
canvas.drawTextOnPath(textForWard, path, h1_Offset, v1_Offset, mTextPaint);
canvas.drawTextOnPath(textForBack, path, h2_Offset, v2_Offset, mTextPaint);
path.lineTo(mWidth / 2, mWidth / 2);
path.close();
mRollGameTextPath.put(position, path);
}
/**
* 兼容一行文本
*
* @param canvas
* @param startAngle
* @param sweepAngle
* @param text
*/
private void drawLineOne(Canvas canvas, float startAngle, float sweepAngle, String text, int position) {
Path path = new Path();
path.addArc(mRange, startAngle, sweepAngle);
int textWidth = measureTextView(text).width();
int textHeight = measureTextView(text).height();
float hOffset = (float) (mRadius * Math.PI / mRollGameTextCount / 2 - textWidth / 2);// 水平偏移:圆长/个数/2 - 文本宽度/2
float vOffset = textHeight;// 垂直偏移
canvas.drawTextOnPath(text, path, hOffset, vOffset, mTextPaint);
path.lineTo(mWidth / 2, mWidth / 2);
path.close();
mRollGameTextPath.put(position, path);
}
/**
* 测量文本
*
* @param text
* @return
*/
private Rect measureTextView(String text) {
Rect bounds = new Rect();
mTextPaint.getTextBounds(text, 0, text.length(), bounds);
return bounds;
}
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
4、旋转动画
/**
* 开启拼手气
*/
private void startRollGame() {
if (checkStartCondition()) {
if (!isRolling) {
isRolling = true;
toDegress = (mRollGameTextCount - mAnswerPosition) * 45 + mRollCount * 360;
startRollGameRotateAnimation(fromDegress, toDegress);
fromDegress = toDegress % 360;
Log.e("TAG", "fromDegress:" + fromDegress + "-toDegress:" + toDegress);
}
}
}
/**
* 判断开启条件
*
* @return
*/
private boolean checkStartCondition() {
if (null == mRollGameTextList || mRollGameTextList.size() == 0 || mRollCount == 0) {
return false;
}
return true;
}
/**
* 开启旋转动画
*
* @param fromDegress
* @param toDegress
*/
private void startRollGameRotateAnimation(float fromDegress, float toDegress) {
RotateAnimation rollGameRotateAnimation = getRollGameRotateAnimation(fromDegress, toDegress);
startAnimation(rollGameRotateAnimation);
}
/**
* 获取旋转动画
*
* @param fromDegress
* @param toDegress
* @return
*/
private RotateAnimation getRollGameRotateAnimation(float fromDegress, float toDegress) {
RotateAnimation rotateAnimation = new RotateAnimation(fromDegress, toDegress, mWidth / 2, mWidth / 2);
rotateAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
rotateAnimation.setDuration((long) ((toDegress - fromDegress) / 360 * mTimeForPer));
rotateAnimation.setAnimationListener(rollGameAnimListener);
rotateAnimation.setFillAfter(true);
return rotateAnimation;
}
/**
* 动画监听
*/
private Animation.AnimationListener rollGameAnimListener = new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
isRolling = false;
}
@Override
public void onAnimationRepeat(Animation animation) {
}
};
5、提供API
public void setAnswerPosition(int mAnswerPosition) {
this.mAnswerPosition = mAnswerPosition;
}
public int getAnswerPosition() {
return mAnswerPosition;
}
public void setTimeForPer(int mTimeForPer) {
this.mTimeForPer = mTimeForPer;
}
public void setRollCount(int mRollCount) {
this.mRollCount = mRollCount;
}
public void setCurrentPosition(int mCurrentPosition) {
this.mCurrentPosition = mCurrentPosition;
invalidate();
}
public void setFirstLineTextCount(int mFirstLineTextCount) {
this.mFirstLineTextCount = mFirstLineTextCount;
}
public void setRollGameTextList(List mRollGameTextList) {
if (null != mRollGameTextList && mRollGameTextList.size() > 0) {
this.mRollGameTextList = mRollGameTextList;
this.mRollGameTextCount = mRollGameTextList.size();
invalidate();
}
}
/**
* 开启动画
*/
public void start() {
startRollGame();
}
6、提供点击事件
public interface OnRegionClickListener {
public void onRegionClick(int position);
}
private OnRegionClickListener mRegionClickListener;
public void setOnRegionClickListener(OnRegionClickListener mClickListener) {
this.mRegionClickListener = mClickListener;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (checkStartCondition()) {
int x = (int) event.getX();
int y = (int) event.getY();
for (int i = 0; i < mRollGameTextPath.size(); i++) {
Path path = mRollGameTextPath.get(i);
Region region = new Region();
//将path和region的两个区域取交集
region.setPath(path, mGlobalRegion);
if (region.contains(x, y)) {
if (null != mRegionClickListener) {
mRegionClickListener.onRegionClick(i);
}
break;
}
}
}
break;
}
return super.onTouchEvent(event);
}
7、转盘的使用
public class MainActivity extends AppCompatActivity implements RollGameTextViewList.OnRegionClickListener {
private RollGameTextViewList iv_rollgame_context;
private static List text = new ArrayList<>();
static {
text.add("送");
text.add("送礼");
text.add("送礼物");
text.add("送礼物吧");
text.add("送礼物微信");
text.add("送礼物我爱你");
text.add("送礼物宝贵砖石");
text.add("送礼物我爱你么么");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv_rollgame_context = findViewById(R.id.iv_rollgame_context);
iv_rollgame_context.setRollCount(10);
iv_rollgame_context.setTimeForPer(800);
iv_rollgame_context.setAnswerPosition(2);
iv_rollgame_context.setCurrentPosition(0);
iv_rollgame_context.setFirstLineTextCount(4);
iv_rollgame_context.setRollGameTextList(text);
iv_rollgame_context.setOnRegionClickListener(this);
}
public void startRoll(View view) {
iv_rollgame_context.start();
Toast.makeText(this, "answerIs:" + text.get(iv_rollgame_context.getAnswerPosition()), Toast.LENGTH_SHORT).show();
}
@Override
public void onRegionClick(int position) {
Toast.makeText(this, "click position:" + position, Toast.LENGTH_SHORT).show();
}
}
其布局的实现
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
tools:context="com.yy.a.MainActivity">
<ImageView
android:layout_width="284dp"
android:layout_height="284dp"
android:layout_centerInParent="true"
android:background="@drawable/rg_zp_bg" />
<com.yy.a.RollGameTextViewList
android:id="@+id/iv_rollgame_context"
android:layout_width="235dp"
android:layout_height="235dp"
android:layout_centerInParent="true"
android:background="@drawable/rg_zp_context"
android:padding="10dp" />
<Button
android:layout_width="75dp"
android:layout_height="75dp"
android:layout_centerInParent="true"
android:background="@drawable/rg_start_off_btn"
android:onClick="startRoll"
android:text="启动" />
<RelativeLayout
android:layout_width="177dp"
android:layout_height="66dp"
android:layout_above="@id/iv_rollgame_context"
android:layout_centerHorizontal="true">
<ImageView
android:layout_width="177dp"
android:layout_height="66dp"
android:background="@drawable/rg_zp_logo_tips" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:text="转盘结果"
android:textColor="#ffffff" />
RelativeLayout>
RelativeLayout>
8、源码下载
源码下载