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、欢迎留言共同交流学习提高,谢谢!