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

效果图

这里写图片描述

原理分析

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

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

实现步骤

1、初始化变量

private List<String> 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<Integer, Path> 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<String> 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<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.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();
    }
}

其布局的实现

<?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
        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、源码下载

源码下载

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

许英俊潇洒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值