【android 遥杆控件 自定义控件】

android 遥杆控件 自定义控件

遥杆控件 android 自定义控件


在这里插入图片描述

package com.sun.demo1;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import androidx.core.content.ContextCompat;



/**
 * Created by jaelyn on 16-06-29.
 * 摇杆view
 */
public class RockerView extends View {
    public static final int BACK_MODE_LINE_Y = 1;
    public static final int BACK_MODE_CENTER = 0;
    public static final int CONTROL_BY_TOUCH = 0;
    public static final int CONTROL_BY_ORIENTATION = 1;
    private static final int TOUCH_DOWN = 0;
    private static final int TOUCH_MOVE = 1;
    private static final int TOUCH_UP = 2;
    private static final int TOUCH_CENTER = 3;
    private static final float PagerMarginsScale = 0.0f;
    private Drawable mDrawBg;
    private Drawable mDrawRocker;
    private int mPagerMargins = 0;
    private int mBgWight;
    private int mBgR;
    private int mRockerWidth, mRockerR;
    private int mRockerX, mRockerY;
    private Rect mRockerRect = new Rect();
    private int mRockerGravide = 0;
    private int mRockeBackMode = BACK_MODE_LINE_Y;
    private int mTouchMode = TOUCH_CENTER;

    private Paint paint = new Paint();

    private OnLocaListener onLocaListener;

    private byte trimX = (byte) 128, trimY = (byte) 128;

    private SensorUtil sensorUtil;

    private int mControlMode = CONTROL_BY_TOUCH;

    private float mRockerXPer, mRockerYPer;
    private float angle;

    public String name;


    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public String avenue;
    public String spareAvenue;

    public String getAvenue() {
        return avenue;
    }

    public void setAvenue(String avenue) {
        this.avenue = avenue;
    }

    public String getSpareAvenue() {
        return spareAvenue;
    }

    public void setSpareAvenue(String spareAvenue) {
        this.spareAvenue = spareAvenue;
    }

    public OnMyMoveListener myMoveListener;
    public void setOnMyMoveListener(OnMyMoveListener listener){
        this.myMoveListener=listener;
    }


    RotationListener rotationListener;
    public interface RotationListener {
        // 角度
        void AngleChange(float angleNum);
        // 是否显示方向标
        void isShow(boolean isShow);
        // 方向值
        void PositionChange(int type, int x,int y);
        // 按下时的坐标
        void onDown(float x,float y);
    }
    public void setRotationListener(RotationListener rotationListener){
        this.rotationListener = rotationListener;
    }

    private Runnable moveBackRunnable = new Runnable() {
        @Override
        public void run() {
            moveBack();
        }
    };

    public RockerView(Context context) {
        super(context);
        init(null, 0);
    }

    public RockerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }

    public RockerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs, defStyleAttr);
    }

    public void init(AttributeSet attrs, int defStyleAttr) {
        paint.setColor(0xaaff0000);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
        int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
        parentHeight = parentWidth = Math.min(parentWidth, parentHeight);
        mBgWight = parentWidth;
        mPagerMargins = (int) (parentWidth * PagerMarginsScale);
        mBgR = parentWidth / 2 - mPagerMargins - mRockerR;
        mRockerWidth = mDrawRocker.getIntrinsicWidth();
        mRockerWidth =(( mBgWight / 2) / 2) +10;
        mRockerR = mRockerWidth / 2;
        setMeasuredDimension(parentWidth, parentHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制背景
        mDrawBg.setBounds(0, 0, mBgWight, mBgWight);
        mDrawBg.draw(canvas);
        //计算小球的位置
        mDrawRocker.setBounds(getRockerRect());
        //绘制小球
        mDrawRocker.draw(canvas);
    }

    /**
     * 以摇杆当前的坐标为中心点,计算Rocker绘画的范围
     * @return
     */
    private Rect getRockerRect() {
        int rockerX = (int) (mBgR * mRockerXPer + mBgWight / 2);
        int rockerY = (int) (-mBgR * mRockerYPer + mBgWight / 2);
        mRockerRect.set(rockerX - mRockerR, rockerY - mRockerR, rockerX + mRockerR,
                rockerY + mRockerR);
        return mRockerRect;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mControlMode == CONTROL_BY_ORIENTATION) {
            return super.onTouchEvent(event);
        }
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            if (isTouchRocker((int) event.getX(), (int) event.getY())) {
                mTouchMode = TOUCH_DOWN;
                if(rotationListener != null){
                    rotationListener.isShow(true);
                }
                return true;
            }
            break;
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:
            if (mTouchMode == TOUCH_MOVE) {
                moveBack();
                mTouchMode = TOUCH_UP;
            }
            if(rotationListener != null){
                rotationListener.isShow(false);
            }
            break;
        }
        return super.onTouchEvent(event);
    }

    public boolean isTouchRocker(int x, int y) {
//        return mRockerRect.contains(x, y);
        return true;
    }

    private void moveRocker(float x, float y) {
        if (!isRange(x, y)) {
            setCircleXY(x, y);
        } else {
            mRockerX = (int) x;
            mRockerY = (int) y;
        }
        setLinstenerData();
        postInvalidate();
    }

    private boolean isRange(float x, float y) {
        float dx = mBgWight / 2 - x;
        float dy = mBgWight / 2 - y;
        return mBgR >= Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2));
    }

    private void setLinstenerData() {
        if (mBgR == 0) {
            return;
        }
        mRockerXPer = (mRockerX - mBgWight / 2f) / (float) mBgR;
        mRockerYPer = (mBgWight / 2f - mRockerY) / (float) mBgR;
        if(this.myMoveListener!=null){
            this.myMoveListener.onSignal(avenue, name, transformFB(mBgR, mRockerX - mBgWight / 2), 1);
            this.myMoveListener.onSignal(spareAvenue, name, transformFB(mBgR, mRockerY - mBgWight / 2), 2);
        }
    }

    public int transformFB(int radius, float num){
        int direction = 0;
        if(num > 0){
            direction = (int) ((Math.abs(num) / radius) * 90);
            direction = 90 + direction;
        }else if(num < 0){
            direction = (int) ((Math.abs(num) / radius) * 90);
            direction = 90 - direction;
        }else{
            direction = 90;
        }

        //超值锁
        if(direction > 180){
            direction = 180;
        }
        if(direction < 0){
            direction = 0;
        }
        return direction;
    }


    public interface OnLocaListener {
        void getLocation(int x, int y);
    }

    public void setOnLocaListener(OnLocaListener onLocaListener) {
        this.onLocaListener = onLocaListener;
    }

    private class AnimToBack implements Runnable {

        private long startTime;
        private float duration;
        private int startX, startY;
        private int dx, dy;

        public void start(int startX, int startY, int endX, int endY, int duration) {
            this.duration = duration;
            dx = endX - startX;
            dy = endY - startY;
            this.startX = startX;
            this.startY = startY;
            startTime = SystemClock.uptimeMillis();
            post(this);
        }

        @Override
        public void run() {
            float progress = (SystemClock.uptimeMillis() - startTime) / duration;
            if (progress >= 1) {
                progress = 1;
            } else {
                post(this);
            }
            moveRocker(dx * progress + startX, dy * progress + startY);
        }
    }

    private void setBackMode(int baceMode, boolean moveToBack) {
        this.mRockeBackMode = baceMode;
        if (moveToBack) {
            moveBack();
        }
    }

    public void moveBack() {
        int backX = mBgWight / 2;
        int backY = mBgWight / 2;
        if (mRockeBackMode == BACK_MODE_CENTER) {
            backY = backX = mBgWight / 2;
        } else if (mRockeBackMode == BACK_MODE_LINE_Y) {
            backX = mBgWight / 2;
            backY = mBgWight;
        }
        new AnimToBack().start(mRockerX, mRockerY, backX, backY, 150);
    }

    public void moveToPoint(float percentX, float percentY) {
        float x = percentX * mBgR + mBgWight / 2;
        float y = percentY * mBgR + mBgWight / 2;
        new AnimToBack().start((int) mRockerX, (int) mRockerY, (int) x, (int) y, 10);
    }

    public void moveToPointDura(float percentX, float percentY, int duration) {
        float x = percentX * mBgR + mBgWight / 2;
        float y = percentY * mBgR + mBgWight / 2;
        new AnimToBack().start((int) mRockerX, (int) mRockerY, (int) x, (int) y, 10);
        Handler handler = new Handler();
        handler.removeCallbacks(moveBackRunnable);
        handler.postDelayed(moveBackRunnable, duration);
    }

    public void setControlMode(int type) {
        final double stickyCenter = 0.09;
        if (type == CONTROL_BY_TOUCH) {
            mControlMode = CONTROL_BY_TOUCH;
            if (sensorUtil != null) {
                sensorUtil.unRegisterListener();
                sensorUtil = null;
            }
        } else if (type == CONTROL_BY_ORIENTATION) {
            mControlMode = CONTROL_BY_ORIENTATION;
            sensorUtil = new SensorUtil(getContext(), new SensorUtil.OnChangeListener() {
                @Override
                public void setXY(float dx, float dy) {
                    Log.d("sensor", dx + "--" + dy);
                    float x = dx * mBgR + (float) mBgWight / 2;
                    float y = dy * mBgR + (float) mBgWight / 2;
                    moveRocker(x, y);
                }
            });
            sensorUtil.registerListener();
        }
    }


    public float getAngle(float xx,float yy){
        double angle,k;
        if (mBgWight / 2==yy)//斜率不存在时
            if (mBgWight / 2 > xx)//判断x1指向x2的方向
                angle= -Math.PI/2;
            else
                angle= Math.PI/2;
        else{
            k=(mBgWight / 2-xx)/(mBgWight / 2-yy); //两点的坐标求斜率,至于为什么是(x1-x2)/(y1-y2)不是(y1-y2)/(x1-x2)待会我们再做解释
            if (mBgWight / 2 > yy) {//判断x1y1指向x2y2的方向
                // 用反tan求角度 这个高中好像没学过 既然Math类已经帮我们封装好了就直接拿来用吧
                angle = Math.atan(k) + Math.PI;
            } else {
                angle = Math.atan(k);
            }
        }
        return (float) angle;
    }

    /**
     * 角度值换算
     * @param num
     * @return
     */
    public  float transformAngle(float num){
        float  roundLength = (float) (Math.abs(num + 1.5) );
        num = (float) (roundLength / 0.0172);
        if(num > 360){
            num = 360;
        }else{
            num = 360 - num;
        }
        return num;
    }

    public void registerListener() {
        if (sensorUtil != null) {
            sensorUtil.registerListener();
        }
    }

    public void unregisterListener() {
        if (sensorUtil != null) {
            sensorUtil.unRegisterListener();
        }
    }

    public float getmRockerXPer() {
        return mRockerXPer;
    }

    public float getmRockerYPer() {
        return mRockerYPer;
    }

    public byte getTrimX() {
        return trimX;
    }

    public void setTrimX(byte trimX) {
        this.trimX = trimX;
    }

    public byte getTrimY() {
        return trimY;
    }

    public void setTrimY(byte trimY) {
        this.trimY = trimY;
    }
}

附 DEMO地址 https://download.csdn.net/download/qq_37630270/87971815

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
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 中的内容。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

百思不得姐1

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

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

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

打赏作者

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

抵扣说明:

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

余额充值