Android自定义游戏摇杆、方向轮盘控件

效果展示

在这里插入图片描述

过程阐述

1、自定义控件,继承View,实现OnTouchListener事件监听接口
CtrlRoundView extends View implements View.OnTouchListener
2、自定义属性小圆大小、控件背景颜色,小圆大小

<declare-styleable name="CtrlRoundView">
        <attr name="fingerColor" format="color"/>
        <attr name="borderColor" format="color"/>
        <attr name="fingerSize" format="dimension"/>       
    </declare-styleable>

3、在 CtrlRoundView构造方法中获取并初始化属性
4、在onMeasure方法中设置控件宽高、大圆的半径,初始化中心位置参数
5、在onDraw方法中绘制大圆、小圆
6、在onTouch方法中实现移动效果
7、设置小圆位置改变的回调方法
8、在Activity中添加自定义控件,并设置属性

所有代码展示

自定义属性文件(values/attrs.xml)

<?xml version="1.0" encoding="utf-8"?>
<resources>
   
    <!--自定义控制圆盘的自定义属性-->
    <declare-styleable name="CtrlRoundView">
        <attr name="fingerColor" format="color"/>
        <attr name="borderColor" format="color"/>
        <attr name="fingerSize" format="dimension"/>
        <attr name="getDirection" format="string"/>
        <attr name="getDistance" format="float"/>
        <attr name="getAngle" format="float"/>
    </declare-styleable>

</resources>

自定义控件java代码(CtrlRoundView.class)

package com.example.myapplication;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class CtrlRoundView extends View implements View.OnTouchListener {
    private Paint borderPaint = new Paint();//大圆画笔
    private Paint fingerPaint = new Paint();//小圆画笔
    private float borderRadius = 160;//默认大圆半径
    private float fingerRadius = 30;//默认小圆半径
    private float centerX = borderRadius;//大圆中心点位置x
    private float centerY = borderRadius;//大圆中心点位置y
    private float fingerX = centerX;//小圆中心点X
    private float fingerY = centerY;//小圆中心点Y
    private float lastX = fingerX, lastY = fingerY;//默认中心点位置
    private float maxLong; //小圆最大移动距离,大圆减去小圆的半径 = borderRadius - fingerRadius
    private ValueAnimator autoCenterAnimator;//自动回中心补间动画
    private MoveListener moveListener;//移动回调接口



    public CtrlRoundView(Context context) {
        this(context, null, 0);
    }

    public CtrlRoundView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CtrlRoundView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //初始化
        if (attrs != null) {
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CtrlRoundView);
            //获取大圆、小圆的颜色,给定默认颜色
            int fingerColor = typedArray.getColor(R.styleable.CtrlRoundView_fingerColor, Color.parseColor("#eeeeee"));
            int borderColor = typedArray.getColor(R.styleable.CtrlRoundView_borderColor, Color.GRAY);
            fingerRadius = typedArray.getDimension(R.styleable.CtrlRoundView_fingerSize, fingerRadius);//获取小圆半径
            borderPaint.setColor(borderColor);//设置大圆颜色
            fingerPaint.setColor(fingerColor);//设置小圆颜色
            typedArray.recycle();//资源回收
            //自动回原点补间动画
            setOnTouchListener(this);
            autoCenterAnimator = ValueAnimator.ofFloat(1);
            autoCenterAnimator.addUpdateListener(animation -> {
                Float aFloat = (Float) animation.getAnimatedValue();
                changeFingerPosition(lastX + (centerX - lastX) * aFloat, lastY + (centerY - lastY) * aFloat);
            });

        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //调用者在布局文件中可能wrap_content  ,宽度高度不一致
        //获取模式AT_MOST  40dp
        //宽度高度不一致,取最小值,确保是个正方形,并且圆的半径不能超过容器的大小,否则大圆的半径设置为宽度的一半
        Log.d("wksView", "onMeasure");
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        int tmpMin = (width > height ? height : width);//长宽返回一个最小的
        borderRadius = tmpMin / 2;
//        maxLong = borderRadius - fingerRadius;//大圆减去小圆的半径
        centerX = centerY = fingerX = fingerY = lastX = lastY = borderRadius;//初始化中心位置参数
        setMeasuredDimension(tmpMin, tmpMin);//设置控件大小
    }

    @Override
    protected void onDraw(@NonNull Canvas canvas) {
        super.onDraw(canvas);
        //绘制UI
        canvas.drawCircle(centerX, centerY, borderRadius, borderPaint);//绘制大圆
        canvas.drawCircle(fingerX, fingerY, fingerRadius, fingerPaint);//绘制小圆

    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        float moveX = event.getX(), moveY = event.getY();//移动的绝对坐标
        float positionX = moveX - centerX, positionY = moveY - centerY;//计算相对于中心的坐标
        maxLong = borderRadius-fingerRadius;//最大可移动距离,大圆减小圆
        float eventLong =(float) Math.sqrt(positionX*positionX+positionY*positionY);
        float courrLong = 0f;

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //按下事件,从圆外开始按下无效,通过勾股定理计算是否超出圆外,这个算法不对,
                Log.e("wksACTION_DOWN","moveX:"+moveX+"  moveY:"+moveY+"/n positionX:"+positionX+"  positionY:"+positionY);
                if (positionX * positionX + positionY * positionY > borderRadius * borderRadius) {
                    break;
                }
            case MotionEvent.ACTION_MOVE:
                //可移动最大距离
                //测算当前坐标距离
                courrLong=(eventLong<maxLong?maxLong:eventLong);
                positionX=(maxLong/courrLong)*positionX+centerX;
                positionY=(maxLong/courrLong)*positionY+centerY;

                Log.e("wksSction_move","eventLong:"+eventLong+"currLong:"+courrLong+"positionX:"+positionX+"positionY:"+positionY);
                changeFingerPosition(positionX,positionY);

                break;
            case MotionEvent.ACTION_UP://抬起手指,自动回原点
                autoCenterAnimator.setDuration(1000);//1秒
                autoCenterAnimator.start();//开始动画
                break;
        }
        return true;
    }

    //改变小圆位置的回调方法
    private void changeFingerPosition(float fingerX, float fingerY) {
        this.fingerX = fingerX;
        this.fingerY = fingerY;
        if (moveListener != null){
            float r = borderRadius - fingerRadius;
            if (r==0){
                invalidate();
                return;
            }
            moveListener.move((fingerX-centerX)/r,(fingerY-centerY)/r);
        }
        invalidate();
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        autoCenterAnimator.removeAllListeners();
    }

    public void setMoveListener(MoveListener moveListener) {
        this.moveListener = moveListener;
    }

    //回调事件接口
    public interface MoveListener {
        void move(float dx, float dy);
    }
}

在activity中添加控件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal"
    tools:context=".CtrlRoundMainActivity">
   
    <com.example.myapplication.CtrlRoundView
        android:id="@+id/lpView"
        android:layout_width="300dp"
        android:layout_height="match_parent"
        android:layout_marginTop="0dp"

        app:fingerSize="30dp"
        app:fingerColor="#333333"
        app:borderColor="#C8C8C8"/>

</LinearLayout>

不足之处与改善意见请留言修改,供大家学习参考

1、如何将移动的方向、角度、距离传递给textview?
2、如何将背景和小圆设置为图片,增加控件的魅力值?
3、欢迎留言共同交流学习提高,谢谢!

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值