GamepadView、JostickView仿創客工場中的遙感的自定義View
阿新 • • 發佈:2019-02-14
一、遊戲操縱桿的展示:
小時候必定玩過小霸王類的手柄遊戲,很懷念小時候。無意間看見創客工場裡控制機器人的View中有一個類似操縱桿的控制元件。
所以很感興趣的仿造做了一個玩玩,效果如下:
二、實現的思想步驟:
1、首先自定義View的三大步驟 onMeasure()、onDraw()、onLayout(),在此控制元件中放置對應的圖片即可實現。onLayout的放置似乎沒有什麼必要,最主要的是onDraw繪製出控制元件。
2、繪製出控制元件接下來就是onTouchEvent()對手勢的事件進行處理,包含中心遙感隨著手勢的點選、拖動實時位置的變化,手勢釋放時中心遙感回到原始位置。
3、以上處理完之後就是邏輯的處理了,根據手勢的事件在View所表現出來的座標計算出對應的方向並與方向箭頭掛上關係。
三、程式碼展示:
package com.wangyongyao1989.joystickview.views; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import com.wangyongyao1989.joystickview.R; /** * @author wangyao * @package com.wangyongyao1989.joystickview.views * @describe TODO * @date 2018/8/3 */ public class GameJoystickView extends View { public final static long DEFAULT_LOOP_INTERVAL = 200; // 200 ms private final double RAD = 57.2957795; //1弧度=57.2957795° private final String TAG = GameJoystickView.class.getName(); private Bitmap mJoystickBackground; private Bitmap mJoystickCenter; private Bitmap mJoystickCenterUP; private Paint mainCircle; private int mCenterX; private int mCenterY; private int mJoystickRadius; private Bitmap mJoystickCenterDown; private int mCenterPositionX = 0; private int mCenterPositionY = 0; private int mCenterRadius; private int mCenterNorH; private int mCenterNorW; private int mTouchPositionX; private int mTouchPositionY; private OnJoystickMoveListener onJoystickMoveListener; // Listener private long loopInterval = DEFAULT_LOOP_INTERVAL; public final static int VIEW_CENTER_CIRCLE = 0; public final static int UP_DIRECTION = -1; //上 public final static int DOWN_DIRECTION = -2; //下 public final static int LEFT_DIRECTION = -3; //左 public final static int RIGHT_DIRECTION = -4; //右 private int lastPower = 0; private int lastAngle = 0; private int[] angleArray = new int[] {0, 15, 35, 55, 75, 105, 125, 145, 165, 195, 215, 235, 255, 285, 305, 325, 345, 360}; private Bitmap mArrowLeftNor, mArrowLeftPre, mArrowRightNor, mArrowRightPre, mArrowUpNor, mArrowUpPre,mArrowDownNor, mArrowDownPre, mArrowLeft, mArrowRight, mArrowUp, mArrowDown; private int mArrowH; private int mArrowW; public GameJoystickView(Context context) { super(context); initGameJoystickView(context); } public GameJoystickView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); initGameJoystickView(context); } public GameJoystickView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initGameJoystickView(context); } /** * 遙感移動方向的監聽 */ public interface OnJoystickMoveListener { void onValueChanged(int angle, int power, int direction); } public void setOnJoystickMoveListener(OnJoystickMoveListener listener, long repeatInterval) { this.onJoystickMoveListener = listener; this.loopInterval = repeatInterval; } private void initGameJoystickView(Context context) { //獲取每種狀態下的bitmap mJoystickBackground = BitmapFactory.decodeResource(context.getResources(), R.drawable.widget_bg_joystick); mJoystickCenter = BitmapFactory.decodeResource(context.getResources(), R.drawable.widget_image_joystick_center_nor); mJoystickCenterDown = BitmapFactory.decodeResource(context.getResources(), R.drawable.widget_image_joystick_center_pre); mJoystickCenterUP = BitmapFactory.decodeResource(context.getResources(), R.drawable.widget_image_joystick_center_nor); mArrowLeft = BitmapFactory.decodeResource(context.getResources(), R.drawable.widget_image_joystick_left_nor); mArrowRight = BitmapFactory.decodeResource(context.getResources(), R.drawable.widget_image_joystick_right_nor); mArrowUp= BitmapFactory.decodeResource(context.getResources(), R.drawable.widget_image_joystick_up_nor); mArrowDown= BitmapFactory.decodeResource(context.getResources(), R.drawable.widget_image_joystick_down_nor); mArrowLeftNor = BitmapFactory.decodeResource(context.getResources(), R.drawable.widget_image_joystick_left_nor); mArrowLeftPre = BitmapFactory.decodeResource(context.getResources(), R.drawable.widget_image_joystick_left_pre); mArrowRightNor = BitmapFactory.decodeResource(context.getResources(), R.drawable.widget_image_joystick_right_nor); mArrowRightPre = BitmapFactory.decodeResource(context.getResources(), R.drawable.widget_image_joystick_right_pre); mArrowUpNor= BitmapFactory.decodeResource(context.getResources(), R.drawable.widget_image_joystick_up_nor); mArrowUpPre = BitmapFactory.decodeResource(context.getResources(), R.drawable.widget_image_joystick_up_pre); mArrowDownNor = BitmapFactory.decodeResource(context.getResources(), R.drawable.widget_image_joystick_down_nor); mArrowDownPre = BitmapFactory.decodeResource(context.getResources(), R.drawable.widget_image_joystick_down_pre); mArrowH = mArrowUpNor.getHeight(); mArrowW = mArrowUpNor.getWidth(); mCenterNorH = mJoystickCenter.getHeight(); mCenterNorW = mJoystickCenter.getWidth(); mCenterRadius = Math.max(mCenterNorH, mCenterNorW) / 5; mainCircle = new Paint(Paint.ANTI_ALIAS_FLAG); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); int d = Math.min(w, h); //控制元件中心位置座標 mCenterPositionX = (int) getWidth() / 2; mCenterPositionY = (int) getWidth() / 2; mTouchPositionX = mCenterPositionX; mTouchPositionY = mCenterPositionY; //獲取整個控制元件矩形的邊長的0.75倍 mJoystickRadius = (int)( d/2 *0.75) ; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int d = Math.min(measure(widthMeasureSpec), measure(heightMeasureSpec)); setMeasuredDimension(d, d); } @Override protected void onDraw(Canvas canvas) { // super.onDraw(canvas); //獲取view的中心點 mCenterX = (getWidth()) / 2; mCenterY = (getHeight()) / 2; //canvas出背景圖片 canvas.drawBitmap(mJoystickBackground,null,new Rect( (mCenterX - mJoystickRadius), (mCenterY - mJoystickRadius), (mCenterX + mJoystickRadius), (mCenterY + mJoystickRadius) ),mainCircle); //朝上箭頭 canvas.drawBitmap(mArrowUp,null, new Rect( (mCenterX - mArrowW /4), (int) ((mCenterY - mJoystickRadius * 0.8) - mArrowH/4), (mCenterX + mArrowW/4), (int) ((mCenterY - mJoystickRadius * 0.8) + mArrowH/4) ),mainCircle); canvas.drawBitmap(mArrowDown,null, new Rect( (mCenterX - mArrowW /4), (int) ((mCenterY + mJoystickRadius * 0.8) - mArrowH/4), (mCenterX + mArrowW/4), (int) ((mCenterY + mJoystickRadius * 0.8) + mArrowH/4) ),mainCircle); canvas.drawBitmap(mArrowLeft,null, new Rect( (int) ((mCenterX - mJoystickRadius *0.8) - mArrowW /4), (mCenterY - mArrowH/4), (int) ((mCenterX - mJoystickRadius *0.8) + mArrowW /4), (mCenterY + mArrowH/4) ),mainCircle); canvas.drawBitmap(mArrowRight,null, new Rect( (int) ((mCenterX + mJoystickRadius *0.8) - mArrowW /4), (mCenterY - mArrowH/4), (int) ((mCenterX + mJoystickRadius *0.8) + mArrowW /4), (mCenterY + mArrowH/4) ),mainCircle); mCenterPositionX = mTouchPositionX; mCenterPositionY = mTouchPositionY; //canvas出中心遙感的圖片 canvas.drawBitmap(mJoystickCenter,null,new Rect( (mCenterPositionX - mCenterNorW/5), (mCenterPositionY - mCenterNorH/5), (mCenterPositionX + mCenterNorW/5), (mCenterPositionY + mCenterNorH/5) ),mainCircle); } @Override public boolean onTouchEvent(MotionEvent event) { mTouchPositionX = (int) event.getX(); mTouchPositionY = (int) event.getY(); double sqrt = Math.sqrt((mTouchPositionX - mCenterX) * (mTouchPositionX - mCenterX) + (mTouchPositionY - mCenterY) * (mTouchPositionY - mCenterY)); // Log.e(TAG,"sqrt;"+sqrt); if (sqrt > (mJoystickRadius - mCenterRadius) ) { mTouchPositionX = (int) ((mTouchPositionX - mCenterX )* (mJoystickRadius - mCenterRadius) / sqrt + mCenterX); mTouchPositionY = (int) ((mTouchPositionY - mCenterY) * (mJoystickRadius - mCenterRadius) / sqrt + mCenterY); } changeArrowState(); invalidate(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN : { if (sqrt > mJoystickRadius) { mTouchPositionX = mCenterX; mTouchPositionY = mCenterY; return false; }else { mJoystickCenter = mJoystickCenterDown; invalidate(); } } if (onJoystickMoveListener != null) onJoystickMoveListener.onValueChanged(getAngle(), getPower(), getFourDirection()); break; case MotionEvent.ACTION_MOVE : { if (onJoystickMoveListener != null) onJoystickMoveListener.onValueChanged(getAngle(), getPower(), getFourDirection()); } break; case MotionEvent.ACTION_UP : { mTouchPositionX = (int) mCenterX; mTouchPositionY = (int) mCenterY; mJoystickCenter = mJoystickCenterUP; mArrowUp = mArrowUpNor; mArrowDown = mArrowDownNor; mArrowLeft = mArrowLeftNor; mArrowRight = mArrowRightNor; invalidate(); if (onJoystickMoveListener != null) onJoystickMoveListener.onValueChanged(getAngle(), getPower(), getFourDirection()); } break; } return true; } /** * 改變箭頭顯示狀態 */ private void changeArrowState() { if (getFourDirection() == UP_DIRECTION ) { mArrowUp = mArrowUpPre; mArrowDown = mArrowDownNor; mArrowLeft = mArrowLeftNor; mArrowRight = mArrowRightNor; }else if (getFourDirection() == DOWN_DIRECTION) { mArrowDown = mArrowDownPre; mArrowUp = mArrowUpNor; mArrowLeft = mArrowLeftNor; mArrowRight = mArrowRightNor; }else if(getFourDirection() == LEFT_DIRECTION) { mArrowLeft = mArrowLeftPre; mArrowUp = mArrowUpNor; mArrowDown = mArrowDownNor; mArrowRight = mArrowRightNor; }else if (getFourDirection() == RIGHT_DIRECTION) { mArrowRight = mArrowRightPre; mArrowUp = mArrowUpNor; mArrowDown = mArrowDownNor; mArrowLeft = mArrowLeftNor; } } /** * 返回四個方向的值 * @return */ private int getFourDirection() { int direction = VIEW_CENTER_CIRCLE; lastPower = getPower(); if (lastPower <= 33 ) { return direction; } int a = 0; if (lastAngle <= 0) { a = (lastAngle * -1) + 90; } else if (lastAngle > 0) { if (lastAngle <= 90) { a = 90 - lastAngle; } else { a = 360 - (lastAngle - 90); } } int arraylength = angleArray.length; for (int i = 1; i < arraylength; i++) { if (a >= angleArray[i-1] && a < angleArray[i]) { direction = i % (arraylength - 1); if (direction == 0) { direction++; } break; } } Log.e(TAG,"direction:"+direction); if (direction > 3 && direction <= 5) { return UP_DIRECTION; }else if (direction > 5 && direction <=10) { return LEFT_DIRECTION; }else if (direction > 10 && direction <= 15) { return DOWN_DIRECTION; }else if ((direction > 0 && direction <= 3) || ((direction > 15 && direction <= 16))) { return RIGHT_DIRECTION; }else { return 0; } } /** * 手勢移動的半徑值(相對於View中心點)與背景圖案半徑的比值 * @return */ private int getPower() { return (int) (100 * Math.sqrt((mTouchPositionX - mCenterX) * (mTouchPositionX - mCenterX) + (mTouchPositionY - mCenterY) * (mTouchPositionY - mCenterY)) / mJoystickRadius); } /** * 通過座標獲取角度值(反正切值 * 弧度 = 角度) * @return */ private int getAngle() { if (mTouchPositionX > mCenterX) { if (mTouchPositionY < mCenterY) { return lastAngle = (int) (Math.atan((mTouchPositionY - mCenterY) / (mTouchPositionX - mCenterX)) * RAD + 90); } else if (mTouchPositionY > mCenterY) { return lastAngle = (int) (Math.atan((mTouchPositionY - mCenterY) / (mTouchPositionX - mCenterX)) * RAD) + 90; } else { return lastAngle = 90; } } else if (mTouchPositionX < mCenterX) { if (mTouchPositionY < mCenterY) { return lastAngle = (int) (Math.atan((mTouchPositionY - mCenterY) / (mTouchPositionX - mCenterX)) * RAD - 90); } else if (mTouchPositionY > mCenterY) { return lastAngle = (int) (Math.atan((mTouchPositionY - mCenterY) / (mTouchPositionX - mCenterX)) * RAD) - 90; } else { return lastAngle = -90; } } else { if (mTouchPositionY <= mCenterY) { return lastAngle = 0; } else { if (lastAngle < 0) { return lastAngle = -180; } else { return lastAngle = 180; } } } } /** * 控制元件測量的設定 * @param measureSpec * @return */ private int measure(int measureSpec) { int result = 0; // Decode the measurement specifications. int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.UNSPECIFIED) { // Return a default size of 200 if no bounds are specified. result = 200; } else { // As you want to fill the available space // always return the full available bounds. result = specSize; } return result; } }