美文网首页
蓝牙游戏手柄监听操作app

蓝牙游戏手柄监听操作app

作者: 大胡子的机器人 | 来源:发表于2021-08-24 19:17 被阅读0次

    首先蓝牙手柄连接又android官方协议,我们只需要在activity或者view中监听即可。
    手柄一般可分为模拟按键、安卓和PC等模式,我们这里简单介绍下模拟按键和安卓的2种模式

    1、模拟按键

    顾名思义,手柄操作相当于在屏幕上点击,可以用以下代码进行监听

    package com.**.activity.widget;
    
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.PixelFormat;
    import android.graphics.PorterDuff;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.InputDevice;
    import android.view.KeyEvent;
    import android.view.MotionEvent;
    import android.view.SurfaceHolder;
    import android.view.SurfaceView;
    import android.view.View;
    import android.widget.ImageView;
    import android.widget.ProgressBar;
    import android.widget.TextView;
    
    import java.util.ArrayList;
    
    /**
     * Debug测试手柄按键的SurfaceView
     */
    public class HandTouchView extends SurfaceView implements SurfaceHolder.Callback {
        private static String TAG = "HandTouchView";
        public boolean isTest = false;
        private static final int MAX_TOUCHPOINTS = 10;
        public static final int LEFT_JS_CODE = -1;//
        public static final int RIGHT_JS_CODE = -2;//
    
        public static final double LEFT_JS_MAX_X = 139.27744-58.87142;
        public static final double LEFT_JS_MAX_Y = 430.57452-224.23979;
        private Paint paint;
        private ArrayList<BtnBean> btnList = new ArrayList<>();
        private ArrayList<BtnBean> tempList = new ArrayList<>();
    
        private int width, height;//SurfaceView的宽和高
        private float scale = 1.0f;
        private Canvas canvas;
        private int pointerCount = 0;//同时onTouch事件的 个数
        //按钮bean start
        private BtnBean XBean;
        private BtnBean YBean;
        private BtnBean LeftJSBean;
        private BtnBean RightJSBean;
        //按钮bean end
        //控件对象start
        public ImageView ivBtnX;
        public ImageView ivBtnY;
        public ProgressBar progressbar_l2;
        public ProgressBar progressbar_r2;
        public TextView tvProgressL2;
        public TextView tvProgressR2;
        //控件对象end
    
        public HandTouchView(Context context) {
            super(context);
            init();
        }
    
        public HandTouchView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        private void init() {
            setZOrderOnTop(true);
            SurfaceHolder holder = getHolder();
            holder.addCallback(this);
            holder.setFormat(PixelFormat.TRANSLUCENT);
            setFocusable(true); // 确保我们的View能获得输入焦点
            setFocusableInTouchMode(true); // 确保能接收到触屏事件
    
            paint = new Paint();
            paint.setColor(Color.BLUE);
            //先用2个 测试 以后换数据库
            //按键的bean start
            XBean = new BtnBean(KeyEvent.KEYCODE_BUTTON_X,
                                0, 0);
            YBean = new BtnBean(KeyEvent.KEYCODE_BUTTON_Y,
                                0, 0);
            LeftJSBean = new BtnBean(LEFT_JS_CODE, 0,
                                     0);//左摇杆
            RightJSBean = new BtnBean(RIGHT_JS_CODE, 0, 0);//右摇杆
            //按键的bean end
        }
    
        /*
         * 处理触屏事件
         */
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            describeEvent(this, event);
            handleLeftStick(this,event);
            // 获得屏幕触点数量
            pointerCount = event.getPointerCount();
            if (pointerCount > MAX_TOUCHPOINTS) {
                pointerCount = MAX_TOUCHPOINTS;
            }
            // 锁定Canvas,开始进行相应的界面处理
            canvas = getHolder().lockCanvas();
            if (canvas != null) {
                canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                if (event.getAction() == MotionEvent.ACTION_UP) {
                    // 当手离开屏幕时,清屏
                } else {
                    // 先在屏幕上画一个十字,然后画一个圆
                    for (int i = 0; i < pointerCount; i++) {//重绘:相当于把以前的点带着一起画
                        // 获取一个触点的坐标,然后开始绘制
                        int id = event.getPointerId(i);
                        float x = event.getX(i);
                        float y = event.getY(i);
                        drawCrosshairsAndText(x, y, paint, canvas);
                    }
                    for (int i = 0; i < pointerCount; i++) {
                        int id = event.getPointerId(i);
                        float x = event.getX(i);
                        float y = event.getY(i);
                        drawCircle(x, y, paint, canvas);
                    }
                }
                // 画完后,unlock
                getHolder().unlockCanvasAndPost(canvas);
            }
            return true;
        }
        //将左侧手柄按钮的移动转化为坐标,传递到上层
        private void handleLeftStick(View view,MotionEvent event){
            float x = event.getRawX() - 221.68193f;
            float y = event.getRawY() - 82.87142f;
            if(mStickListener != null){
                mStickListener.onLeftMove(x,y,event.getAction());
            }
        }
    
        /**
         * 画十字及坐标信息
         *
         * @param x
         * @param y
         * @param paint
         * @param c
         */
        private void drawCrosshairsAndText(float x, float y, Paint paint, Canvas c) {
            c.drawLine(0, y, width, y, paint);//横线
            c.drawLine(x, 0, x, height, paint);//竖线
        }
    
        /**
         * 画圆
         *
         * @param x
         * @param y
         * @param paint
         * @param c
         */
        private void drawCircle(float x, float y, Paint paint, Canvas c) {
            c.drawCircle(x, y, 15 * scale, paint);
        }
    
        /*
         * 进入程序时背景画成黑色,然后把"START_TEXT"写到屏幕
         */
        public void surfaceChanged(SurfaceHolder holder, int format, int width,
                                   int height) {
            this.width = width;
            this.height = height;
            if (width > height) {
                this.scale = width / 480f;
            } else {
                this.scale = height / 480f;
            }
        }
    
        public void surfaceCreated(SurfaceHolder holder) {
        }
    
        public void surfaceDestroyed(SurfaceHolder holder) {
        }
    
    
        //画点和十字
        private void drawBtnCanvas() {
            canvas = getHolder().lockCanvas();
            if (canvas != null) {
                canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                // 先在屏幕上画一个十字,然后画一个圆
                for (int i = 0; i < btnList.size(); i++) {
                    drawCrosshairsAndText(btnList.get(i).x, btnList.get(i).y, paint, canvas);
                    drawCircle(btnList.get(i).x, btnList.get(i).y, paint, canvas);
                }
                // 画完后,unlock
                getHolder().unlockCanvasAndPost(canvas);
            }
        }
    
        //清除画布
        private void clearBtnCanvas() {
            canvas = getHolder().lockCanvas();
            if (canvas != null) {
                canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                // 画完后,unlock
                getHolder().unlockCanvasAndPost(canvas);
            }
        }
    
        //btnList添加按键
        private void addBtnBean(int keyCode, BtnBean bean) {
            if (keyCode == bean.keyCode) {
                if (!btnList.contains(bean))
                    btnList.add(bean);
            }
        }
        private void removeBtnBean(int keyCode, BtnBean bean) {
            if (keyCode == bean.keyCode) {
                if (btnList.contains(bean))
                    btnList.remove(bean);
            }
        }
    
        //打印log start
        private void describeEvent(View view, MotionEvent event) {
            StringBuilder sb = new StringBuilder(300);
    
            sb.append("Action: ").append(event.getAction()).append("\n");// 获取触控动作比如ACTION_DOWN
            sb.append("相对坐标: ").append(event.getX()).append("  *  ").append(event.getY()).append("   ");
            sb.append("绝对坐标: ").append(event.getRawX()).append("  *  ").append(event.getRawY()).append("\n");
    
            if (event.getX() < 0 || event.getX() > view.getWidth() || event.getY() < 0 || event.getY() > view.getHeight()) {
                sb.append("未点击在View范围内");
            }
            sb.append("Edge flags: ").append(event.getEdgeFlags()).append("  ");// 边缘标记,但是看设备情况,很可能始终返回0
            sb.append("Pressure: ").append(event.getPressure()).append("  ");// 压力值,0-1之间,看情况,很可能始终返回1
            sb.append("Size: ").append(event.getSize()).append("\n");// 指压范围
            sb.append("Down time: ").append(event.getDownTime()).append("ms   ");
            sb.append("Event time: ").append(event.getEventTime()).append("ms   ");
            sb.append("Elapsed: ").append(event.getEventTime() - event.getDownTime()).append("ms\n");
    
            Log.e(TAG,"describeEvent:" + sb.toString());
            //sb.toString();
        }
        //打印log end
    
        //手柄event start    keyCode==4时候 是back
        //KeyEvent.KEYCODE_BUTTON_X   99;
        //KEYCODE_BUTTON_Y = 100
        //KEYCODE_BUTTON_A = 96;
        //KEYCODE_BUTTON_B = 97;
        //KEYCODE_BUTTON_START = 108;
        //KEYCODE_UNKNOWN = 0 -- i键
        //KEYCODE_BACK = 4   (Y , B , 手机back键也会触发)
        //KEYCODE_BUTTON_L1 = 102
        //KEYCODE_BUTTON_R1 = 103
        //KEYCODE_BUTTON_L2 = 104 -- 有onGenericMotionEvent属性 0.003921628不变
        //KEYCODE_BUTTON_R2 = 105 -- 有onGenericMotionEvent属性 0.003921628不变
        //KEYCODE_DPAD_DOWN = 20;
        //KEYCODE_DPAD_LEFT = 21;
        //KEYCODE_DPAD_RIGHT = 22;
        //KEYCODE_DPAD_UP = 19;
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            Log.e(TAG,"keyDown keyCode = " + keyCode + ", scanCode = " + event.getScanCode());
            InputDevice device = event.getDevice();
    //        if (device != null && (device.getSources() + "").length() == 8 && keyCode == KeyEvent.KEYCODE_BACK) {
    ////小米手柄 16779025  手机 4355//三星手柄 16778513  手机 257//moto    16779025  手机 769
    //            return true;
    //        }
            //添加Btn对象
            addBtnBean(keyCode, XBean);
    
            addBtnBean(keyCode, YBean);
            // 锁定Canvas,开始进行相应的界面处理
            drawBtnCanvas();
            return super.onKeyDown(keyCode, event);
        }
    
    
        @Override
        public boolean onKeyUp(int keyCode, KeyEvent event) {
            Log.e(TAG, "keyCode="+keyCode);
            InputDevice device = event.getDevice();
            if (device != null && (device.getSources() + "").length() == 8 && keyCode == KeyEvent.KEYCODE_BACK) {
                //小米手柄 16779025  手机 4355//三星手柄 16778513  手机 257//moto    16779025  手机 769
                return true;
            }
            removeBtnBean(keyCode, XBean);
            removeBtnBean(keyCode, YBean);
            drawBtnCanvas();
            if (btnList.size() == 0)
                clearBtnCanvas();
            return super.onKeyUp(keyCode, event);
        }
    
        //摇杆官方示例start
        @Override
        public boolean onGenericMotionEvent(MotionEvent event) {
            Log.e(TAG,"AXIS_LTRIGGER"+event.getAxisValue(MotionEvent.AXIS_LTRIGGER));
    
            // Check that the event came from a game controller
            if ((event.getSource() & InputDevice.SOURCE_JOYSTICK) ==
                    InputDevice.SOURCE_JOYSTICK &&
                    event.getAction() == MotionEvent.ACTION_MOVE) {
                // Process all historical movement samples in the batch
    //            final int historySize = event.getHistorySize();
    //            //处理 历史事件
    //            for (int i = 0; i < historySize; i++) {
    //                // Process the event at historical position i
    //                processJoystickInput(event, i);
    //            }
    
                // Process the current movement sample in the batch (position -1)
                processJoystickInput(event, -1);
    
                return true;
            }
            return super.onGenericMotionEvent(event);
        }
    
        private static float getCenteredAxis(MotionEvent event,
                                             InputDevice device, int axis, int historyPos) {
            final InputDevice.MotionRange range =
                    device.getMotionRange(axis, event.getSource());
    
            // A joystick at rest does not always report an absolute position of
            // (0,0). Use the getFlat() method to determine the range of values
            // bounding the joystick axis center.
            if (range != null) {
                final float flat = range.getFlat();
                final float value =
                        historyPos < 0 ? event.getAxisValue(axis) :
                                event.getHistoricalAxisValue(axis, historyPos);
    
                // Ignore axis values that are within the 'flat' region of the
                // joystick axis center.
                if (Math.abs(value) > flat) {
                    return value;
                }
            }
            return 0;
        }
    
        private void processJoystickInput(MotionEvent event,
                                          int historyPos) {
    
            InputDevice mInputDevice = event.getDevice();
            //左摇杆
            float leftx = getCenteredAxis(event, mInputDevice,
                                          MotionEvent.AXIS_X, historyPos);
            float lefty = getCenteredAxis(event, mInputDevice,
                                          MotionEvent.AXIS_Y, historyPos);
            //右摇杆
            float rightx = getCenteredAxis(event, mInputDevice,
                                           MotionEvent.AXIS_Z, historyPos);
            float righty = getCenteredAxis(event, mInputDevice,
                                           MotionEvent.AXIS_RZ, historyPos);
            //L2(0.0--0.7)
            float l2 = getCenteredAxis(event, mInputDevice,
                                       MotionEvent.AXIS_BRAKE, historyPos);
            //R2(0.0--1.0)
            float r2 = getCenteredAxis(event, mInputDevice,
                                       MotionEvent.AXIS_GAS, historyPos);
            //在这里处理 Axis 事件
    
            doLeftJoystick(leftx, lefty);//左摇杆
            doRightJoystick(rightx, righty);//右摇杆
            doL2(l2);//L2(0.0--0.7)
            doR2(r2);//R2(0.0--1.0)
    
    
            // Update the ship object based on the new x and y values
        }
    
    
        private void doLeftJoystick(float x, float y) {
            if (Math.abs(x) > 0 || Math.abs(y) > 0) {
                boolean isLeftAdd = false;
                for (int i = 0; i < btnList.size(); i++) {
                    if (btnList.get(i).keyCode == LEFT_JS_CODE) {
                        btnList.get(i).x = 0 ;
                        btnList.get(i).y = 0 ;
                        isLeftAdd = true;
                        break;
                    }
                }
                if (!isLeftAdd) {
                    LeftJSBean = new BtnBean(LEFT_JS_CODE, 0,
                                             0);//左摇杆
                    addBtnBean(LEFT_JS_CODE, LeftJSBean);
                }
    
            }
    
            if (x == 0 && y == 0) {
                for (int i = 0; i < btnList.size(); i++) {
                    if (btnList.get(i).keyCode != LEFT_JS_CODE)
                        tempList.add(btnList.get(i));
                }
                btnList.clear();
                btnList.addAll(tempList);
                tempList.clear();
            }
            drawBtnCanvas();
            if (btnList.size() == 0)
                clearBtnCanvas();
        }
    
        private void doRightJoystick(float x, float y) {
            if (Math.abs(x) > 0 || Math.abs(y) > 0) {
                boolean isRightAdd = false;
                for (int i = 0; i < btnList.size(); i++) {
                    if (btnList.get(i).keyCode == RIGHT_JS_CODE) {
                        btnList.get(i).x = 0;
                        btnList.get(i).y = 0;
                        isRightAdd = true;
                        break;
                    }
                }
                if (!isRightAdd) {
                    RightJSBean = new BtnBean(RIGHT_JS_CODE, 0, 0);//右摇杆
                    addBtnBean(RIGHT_JS_CODE, RightJSBean);
                }
    
            }
    
            if (x == 0 && y == 0) {
                for (int i = 0; i < btnList.size(); i++) {
                    if (btnList.get(i).keyCode != RIGHT_JS_CODE)
                        tempList.add(btnList.get(i));
                }
                btnList.clear();
                btnList.addAll(tempList);
                tempList.clear();
            }
            drawBtnCanvas();
            if (btnList.size() == 0)
                clearBtnCanvas();
        }
    
        private void doL2(float l2) {
            int a = (int) (l2 * 100);
            if (progressbar_l2 != null)
                progressbar_l2.setProgress(a);
            if (tvProgressL2 != null)
                tvProgressL2.setText(a+"");
        }
    
    
        private void doR2(float r2) {
            int a = (int) (r2 * 100);
            if (progressbar_r2 != null)
                progressbar_r2.setProgress(a);
            if (tvProgressR2 != null)
                tvProgressR2.setText(a+"");
        }
    
    
        //摇杆官方示例end
        //手柄event end
    
        public class BtnBean{
            public int keyCode;
            public float x;
            public float y;
    
            public BtnBean(int keyCode, float x, float y) {
                this.keyCode = keyCode;
                this.x = x;
                this.y = y;
            }
        }
        private OnStickMoveListener mStickListener;
        public void setStickMoveListener(OnStickMoveListener listener){
            mStickListener = listener;
        }
        public interface OnStickMoveListener{
            void onLeftMove(float x,float y,int action);
        }
    }
    
    
    

    2、安卓模式监听

    我这里只监听了左摇杆的按键和位移,用来操作机器人的前前后左右,核心代码如下:

        override fun onGenericMotionEvent(event: MotionEvent?): Boolean {
            var x = event?.getAxisValue(MotionEvent.AXIS_X)
            var y = event?.getAxisValue(MotionEvent.AXIS_Y)
            var msg = Message.obtain(mHandler)
            var lineSpeed = if (Math.abs(y!!) > 0.01) -(y/2) else 0f
            var angSpeed = if (Math.abs(x!!) > 0.01) -x/2 else 0f
            if (lineSpeed == 0f && angSpeed == 0f) {
                mHandler?.removeMessages(WHAT_MOVE)
                mHandler?.sendEmptyMessage(WHAT_STOP) //停止移动
            } else {
                msg.what = WHAT_MOVE
                msg.obj = Speed(lineSpeed.toDouble(), angSpeed.toDouble())
                mHandler?.removeMessages(WHAT_MOVE)
                mHandler?.sendMessage(msg)  //开始移动
            }
            return true
        }
    
    image.png

    相关文章

      网友评论

          本文标题:蓝牙游戏手柄监听操作app

          本文链接:https://www.haomeiwen.com/subject/kefjiltx.html