Android | 自定义摇杆控件

控件已开源,gitee项目地址:https://gitee.com/y141111/RockerView
先看实现的功能,如不能满足需求,不必浪费宝贵的时间继续阅读

功能

在这里插入图片描述
xml:

<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".ControlEp">
    <TextView
        android:id="@+id/msg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/show"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <com.shy.rockerview.MyRockerView
        android:id="@+id/rocker_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:rockerSpeedLevel="10"
        app:rockerCallBackMode="CALL_BACK_MODE_STATE_CHANGE"
        app:rockerScale="0.5"/>

    <Button
        android:id="@+id/startReceiver"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/stopReceiver"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

java:

class MainActivity : AppCompatActivity() {
    companion object {
        const val TAG = "MainActivity"
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val mRockerViewXY = findViewById<MyRockerView>(R.id.rockerXY_View)  // 1
        val mRockerViewZ = findViewById<MyRockerView>(R.id.rockerZ_View)    // 2
        val directionXY_Text = findViewById<TextView>(R.id.directionXY_Text)    // 1当前方向
        val angleXY_Text = findViewById<TextView>(R.id.angleXY_Text)    // 1当前角度
        val levelXY_Text = findViewById<TextView>(R.id.levelXY_Text)    // 1当前偏移级别
        val directionZ_Text = findViewById<TextView>(R.id.directionZ_Text) // 2当前方向
        val angleZ_Text = findViewById<TextView>(R.id.angleZ_Text) // 2当前角度
        val levelZ_Text = findViewById<TextView>(R.id.levelZ_Text) // 2当前偏移级别
        
        // 1
        mRockerViewXY.setOnShakeListener(MyRockerView.DirectionMode.DIRECTION_8, object : MyRockerView.OnShakeListener {
            override fun onStart() {}
            override fun direction(direction: MyRockerView.Direction) {
                var directionXY = ""
                when (direction) {
                    MyRockerView.Direction.DIRECTION_CENTER -> {
                        directionXY = "当前方向:中心"
                        angleXY_Text.text = "当前方向:"
                        levelXY_Text.text = "当前偏移级别:"
                    }
                    MyRockerView.Direction.DIRECTION_DOWN -> {
                        directionXY = "当前方向:下"
                    }
                    MyRockerView.Direction.DIRECTION_LEFT -> {
                        directionXY = "当前方向:左"
                    }
                    MyRockerView.Direction.DIRECTION_UP -> {
                        directionXY = "当前方向:上"
                    }
                    MyRockerView.Direction.DIRECTION_RIGHT -> {
                        directionXY = "当前方向:右"
                    }
                    MyRockerView.Direction.DIRECTION_DOWN_LEFT -> {
                        directionXY = "当前方向:左下"
                    }
                    MyRockerView.Direction.DIRECTION_DOWN_RIGHT -> {
                        directionXY = "当前方向:右下"
                    }
                    MyRockerView.Direction.DIRECTION_UP_LEFT -> {
                        directionXY = "当前方向:左上"
                    }
                    MyRockerView.Direction.DIRECTION_UP_RIGHT -> {
                        directionXY = "当前方向:右上"
                    }
                }
                Log.e(TAG, "XY轴$directionXY")
                Log.e(TAG, "-----------------------------------------------")
                directionXY_Text.text = directionXY
            }
            override fun onFinish() {}
        })
        // 1
        mRockerViewXY.setOnAngleChangeListener(object : MyRockerView.OnAngleChangeListener {
            override fun onStart() {}
            override fun angle(angle: Double) {
                var angleXY = ""
                angleXY = "当前角度:$angle"
                Log.e(TAG, "XY轴$angleXY")
                angleXY_Text.setText(angleXY)
            }

            override fun onFinish() {}
        })
        // 1
        mRockerViewXY.setOnDistanceLevelListener { level ->
            var levelXY = ""
            levelXY = "当前距离级别:$level"
            Log.e(TAG, "XY轴$levelXY")
            levelXY_Text.setText(levelXY)
        }


        // 2
        mRockerViewZ.setOnShakeListener(MyRockerView.DirectionMode.DIRECTION_4_ROTATE_45, object : MyRockerView.OnShakeListener {
            override fun onStart() {}
            override fun direction(direction: MyRockerView.Direction) {
                var directionXY = ""
                when (direction) {
                    MyRockerView.Direction.DIRECTION_CENTER -> {
                        directionXY = "当前方向:中心"
                        angleZ_Text.text = "当前方向:"
                        levelZ_Text.text = "当前偏移级别:"
                    }
                    MyRockerView.Direction.DIRECTION_DOWN -> {
                        directionXY = "当前方向:下"
                    }
                    MyRockerView.Direction.DIRECTION_LEFT -> {
                        directionXY = "当前方向:左"
                    }
                    MyRockerView.Direction.DIRECTION_UP -> {
                        directionXY = "当前方向:上"
                    }
                    MyRockerView.Direction.DIRECTION_RIGHT -> {
                        directionXY = "当前方向:右"
                    }
                    else -> {}
                }
                Log.e(TAG, "XY轴$directionXY")
                Log.e(TAG, "-----------------------------------------------")
                directionZ_Text.text = directionXY
            }
            override fun onFinish() {}
        })

        // 2
        mRockerViewZ.setOnAngleChangeListener(object : MyRockerView.OnAngleChangeListener {
            override fun onStart() {}
            override fun angle(angle: Double) {
                var angleXY = ""
                angleXY = "当前角度:$angle"
                Log.e(TAG, "XY轴$angleXY")
                angleZ_Text.setText(angleXY)
            }

            override fun onFinish() {}
        })
        // 2
        mRockerViewZ.setOnDistanceLevelListener { level ->
            var levelXY = ""
            levelXY = "当前距离级别:$level"
            Log.e(TAG, "XY轴$levelXY")
            levelZ_Text.setText(levelXY)
        }
    }
}

实现的监听功能

1.摇杆方向监听
2.摇杆摇动角度监听
3.摇杆摇动距离监听

方向模式

实现了摇杆的五中方向模式
DIRECTION_2_HORIZONTAL,// 横向 左右两个方向
DIRECTION_2_VERTICAL, // 纵向 上下两个方向
DIRECTION_4_ROTATE_0, // 四个方向
DIRECTION_4_ROTATE_45, // 四个方向 旋转45度
DIRECTION_8 // 八个方向




用法

准备工作

1.在project的build.gradle中添加

	allprojects {
		repositories {
			...
			maven { url 'https://jitpack.io' }
		}
	}

2.在module的build.gradle中添加

	dependencies {
	        implementation 'com.gitee.y141111:RockerView:1.0.0'
	}

3.xml中添加

    <com.shy.rockerview.MyRockerView
        android:id="@+id/rocker_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:areaBackground="@color/white"
        app:rockerBackground="@color/black"
        app:rockerSpeedLevel="10"
        app:rockerCallBackMode="CALL_BACK_MODE_STATE_CHANGE"
        app:rockerScale="0.5"/>

areaBackground 设置区域背景
rockerBackground 设置摇杆样式
rockerScale 设置摇杆的相对于背景的比例
rockerSpeedLevel 设置当前位置相对于中心点的距离的比例
rockerCallBackMode 有变化就回调,或者是方向改变才会回调

4.在Activity中添加

八方向示例

1).添加方向监听(方向发生改变触发)
        MyRockerView rockerView = binding.rockerView;
        
        rockerView.setOnShakeListener(MyRockerView.DirectionMode.DIRECTION_8, new MyRockerView.OnShakeListener() {
            @Override
            public void onStart() {
                
            }

            @Override
            public void direction(MyRockerView.Direction direction) {
                switch (direction){
                    case DIRECTION_CENTER:
                        // 中心方向触发事件,一般归零
                        break;
                    case DIRECTION_UP:
                        // 上 触发事件
                        break;
                    case DIRECTION_UP_RIGHT:
                        // 上右 触发事件
                        break;
                    case DIRECTION_RIGHT:
                        break;
                    case DIRECTION_DOWN_RIGHT:
                        break;
                    case DIRECTION_DOWN:
                        break;
                    case DIRECTION_DOWN_LEFT:
                        break;
                    case DIRECTION_LEFT:
                        break;
                    case DIRECTION_UP_LEFT:
                        break;
                }
            }

            @Override
            public void onFinish() {

            }
        });
2).添加角度监听(角度发生改变触发)
        rockerView.setOnAngleChangeListener(new MyRockerView.OnAngleChangeListener() {
            @Override
            public void onStart() {
                
            }

            @Override
            public void angle(double angle) {
                // 角度发生改变 触发事件 当前角度 angle
            }

            @Override
            public void onFinish() {

            }
        });
3).添加距离监听(距离发生改变触发)
        rockerView.setOnDistanceLevelListener(new MyRockerView.OnDistanceLevelListener() {
            @Override
            public void onDistanceLevel(int level) {
                // 距中心点距离发生改变 触发事件
            }
        });

四方向示例

旋转45度才是正常情况下使用的四方向

1).添加方向监听(方向发生改变触发)

        MyRockerView rockerView = binding.rockerView;

        rockerView.setOnShakeListener(MyRockerView.DirectionMode.DIRECTION_4_ROTATE_45, new MyRockerView.OnShakeListener() {
            @Override
            public void onStart() {

            }

            @Override
            public void direction(MyRockerView.Direction direction) {
                switch (direction){
                    case DIRECTION_CENTER:
                        // 中心方向触发事件,一般归零
                        break;
                    case DIRECTION_UP:
                        // 上 触发事件
                        break;
                    case DIRECTION_RIGHT:
                        // 右 触发事件
                        break;
                    case DIRECTION_DOWN:
                        // 下 触发事件
                        break;
                    case DIRECTION_LEFT:
                        // 左 触发事件
                        break;
                }
            }

            @Override
            public void onFinish() {

            }
        });
2).添加角度监听,角度发生改变触发
        rockerView.setOnAngleChangeListener(new MyRockerView.OnAngleChangeListener() {
            @Override
            public void onStart() {
                
            }

            @Override
            public void angle(double angle) {
                // 角度发生改变 触发事件 当前角度 angle
            }

            @Override
            public void onFinish() {

            }
        });
3).添加距离监听(距离发生改变触发)
        rockerView.setOnDistanceLevelListener(new MyRockerView.OnDistanceLevelListener() {
            @Override
            public void onDistanceLevel(int level) {
                // 距中心点距离发生改变 触发事件
            }
        });

角度和距离发生改变触发的是相同的监听事件


源代码

控件已开源,gitee项目地址:https://gitee.com/y141111/RockerView

  • 10
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 19
    评论
Android 自定义摇杆可以通过继承 View 或者 ViewGroup 来实现。以下是一个简单的实现步骤: 1. 创建一个自定义 View 或者 ViewGroup,命名为 JoystickView。 2. 在 JoystickView 中定义一些必要的属性,例如大小、颜色、形状等。 3. 在 JoystickView 中重写 onMeasure() 方法,根据用户设置的大小来测量 View 的宽高。 4. 在 JoystickView 中重写 onDraw() 方法,绘制摇杆和底盘。 5. 在 JoystickView 中重写 onTouchEvent() 方法,处理用户触摸事件,计算出摇杆的位置并通知监听器。 6. 创建一个 JoystickListener 接口,并在 JoystickView 中定义一个 setJoystickListener() 方法,用于设置监听器。 7. 在 JoystickView 中实现回调接口,当摇杆位置发生变化时,通知监听器。 下面是一个简单的代码示例: ```java public class JoystickView extends View { private Paint paint; private int centerX; private int centerY; private int baseRadius; private int hatRadius; private JoystickListener joystickListener; public JoystickView(Context context) { super(context); init(); } public JoystickView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public JoystickView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { paint = new Paint(); paint.setAntiAlias(true); paint.setStyle(Paint.Style.FILL); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); int size = Math.min(width, height); setMeasuredDimension(size, size); centerX = size / 2; centerY = size / 2; baseRadius = size / 3; hatRadius = size / 6; } @Override protected void onDraw(Canvas canvas) { // 绘制底盘 paint.setColor(Color.GRAY); canvas.drawCircle(centerX, centerY, baseRadius, paint); // 绘制摇杆 paint.setColor(Color.RED); canvas.drawCircle(centerX, centerY, hatRadius, paint); } @Override public boolean onTouchEvent(MotionEvent event) { float distance = (float) Math.sqrt(Math.pow(event.getX() - centerX, 2) + Math.pow(event.getY() - centerY, 2)); if (distance > baseRadius) { return true; } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: float x = event.getX(); float y = event.getY(); invalidate(); if (joystickListener != null) { joystickListener.onJoystickMoved((x - centerX) / baseRadius, (y - centerY) / baseRadius); } break; case MotionEvent.ACTION_UP: invalidate(); if (joystickListener != null) { joystickListener.onJoystickReleased(); } break; } return true; } public void setJoystickListener(JoystickListener listener) { joystickListener = listener; } public interface JoystickListener { void onJoystickMoved(float xPercent, float yPercent); void onJoystickReleased(); } } ``` 使用示例: ```java public class MainActivity extends AppCompatActivity implements JoystickView.JoystickListener { private TextView directionTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); directionTextView = findViewById(R.id.direction_text_view); JoystickView joystickView = findViewById(R.id.joystick_view); joystickView.setJoystickListener(this); } @Override public void onJoystickMoved(float xPercent, float yPercent) { String direction = ""; if (xPercent < -0.5) { direction += "LEFT "; } else if (xPercent > 0.5) { direction += "RIGHT "; } if (yPercent < -0.5) { direction += "UP"; } else if (yPercent > 0.5) { direction += "DOWN"; } directionTextView.setText(direction); } @Override public void onJoystickReleased() { directionTextView.setText(""); } } ``` 以上示例中,我们在 MainActivity 中设置了一个 JoystickListener 监听器,当摇杆位置发生变化时,就会调用 onJoystickMoved() 方法。在 onJoystickMoved() 方法中,我们计算出摇杆的位置百分比,并根据百分比来判断摇杆的方向。最后将方向显示在 TextView 中。当用户松开摇杆时,我们会调用 onJoystickReleased() 方法,清空 TextView 中的内容。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

指针不南

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

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

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

打赏作者

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

抵扣说明:

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

余额充值