Android自定义View——拼手气转盘

效果图

这里写图片描述

原理分析

这里的转盘主要实现的重点是绘制每块答案区域的文本,并绘制出来,而转盘和背景只是张图片

1、绘制文本的位置区域
2、获取旋转动画
3、提供接口

实现步骤

1、初始化变量

//文本相关
private List<String> mRollGameTextList;
private int mRollGameTextCount = 0;
private Paint mTextPaint;
private int mTextSize = 14;

private RectF mRange; //View的区域
private int mRadius; //View的直径
private int mWidth; //View的宽度
private float mStartAngle = 22.5f; //开始的角度

//旋转动画相关
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;

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));
}

/**
 * 设置为正方形
 *
 * @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);
    mRange = new RectF(getPaddingLeft(), getPaddingTop(), mRadius
            + getPaddingLeft(), mRadius + getPaddingTop());
}

3、绘制文本

@Override
protected void onDraw(Canvas canvas) {
    if (checkStartCondition()) {
        canvas.save();
        canvas.rotate(-90, mWidth / 2, mWidth / 2);

        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();
    }
}

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);
}

/**
 * 兼容一行文本
 *
 * @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);
}

/**
 * 测量文本
 *
 * @param text
 * @return
 */
private Rect measureTextView(String text) {
    Rect bounds = new Rect();
    mTextPaint.getTextBounds(text, 0, text.length(), bounds);
    return bounds;
}

4、旋转动画

 /**
 * 开启拼手气
 */
private void startRollGame() {
    if (checkStartCondition()) {
        if (!isRolling) {
            isRolling = true;
            toDegress = mAnswerPosition * 45 + mRollCount * 360;
            startRollGameRotateAnimation(fromDegress, toDegress);
            fromDegress = fromDegress + (toDegress - fromDegress) % 360;

            Log.e("TAG", "fromDegress:" + fromDegress + "-toDegress:" + toDegress + "-" + mAnswerPosition);
        }
    }
}

/**
 * 判断开启条件
 *
 * @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 void setTimeForPer(int mTimeForPer) {
    this.mTimeForPer = mTimeForPer;
}

public void setRollCount(int mRollCount) {
    this.mRollCount = mRollCount;
}

public void setFirstLineTextCount(int mFirstLineTextCount) {
    this.mFirstLineTextCount = mFirstLineTextCount;
}

public void setRollGameTextList(List<String> mRollGameTextList) {
    if (null != mRollGameTextList && mRollGameTextList.size() > 0) {
        this.mRollGameTextList = mRollGameTextList;
        this.mRollGameTextCount = mRollGameTextList.size();
        Collections.reverse(mRollGameTextList);
        invalidate();
    }
}

/**
 * 开启动画
 */
public void start() {
    startRollGame();
}

6、转盘的使用

public class MainActivity extends AppCompatActivity{

    private RollGameTextViewList iv_rollgame_context;
    private static List<String> 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.setFirstLineTextCount(4);
        iv_rollgame_context.setRollGameTextList(text);
    }

    public void startRoll(View view) {
        iv_rollgame_context.start();
    }
}

其布局的实现

<?xml version="1.0" encoding="utf-8"?>
<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>

7、源码下载

源码下载

8、点击事件

转盘的每块区域增加点击事件,具体的代码不便放在源码,其主要步骤如下

  1. 获取每块盘块的区域Path
  2. 创建Region集合,添加盘块的Path
  3. 复写onTouchEvent方法
  4. 通过遍历Region跟点击的x,y比较,判断是否在当前区域
  5. 提供接口,进行回调

猜你喜欢

转载自blog.csdn.net/qq_30379689/article/details/79252899