Android自定义view摇杆,Android自定义View实现游戏摇杆键盘的方法示例

前言

本文主要给大家介绍的是关于Android自定义View实现游戏摇杆键盘的相关内容,为什么会有这篇文章呢?因为在之前的一个项目,操作方向的方式为上下左右,左上需要同时按住左键和右键的方式进行操作。

如下图:

665b2c8dea2a58ca78039b2f428372e8.png

近来需要升级项目,操作方式改为类似王者荣耀的摇杆操作。

如下图:

e97688b270474c9b5661150678f74856.png

好了,下面话不多说了,跟着小编来一起看看是如何实现的吧。

绘制背景

实现遥感按钮,需要绘制背景,绘制中心的遥感按钮。绘制遥感背景,需要创建一个RemoteViewBg类,存储背景图,减少重复创建bitmap。

RemoteViewBg类代码如下:

public class RemoteViewBg {

private Bitmap bitmapBg;

public RemoteViewBg(Bitmap bitmap) {

bitmapBg = bitmap;

}

//背景的绘图函数

public void draw(Canvas canvas, Paint paint, Rect src0 ,Rect dst0 ) {

canvas.drawBitmap(bitmapBg, src0, dst0, paint);

}

}

点击触摸事件

重写系统的触摸时间,判断触摸点在背景范围内还是背景范围外

@Override

public boolean onTouchEvent(MotionEvent event) {

if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) {

// // 在范围外触摸

if (Math.sqrt(Math.pow((bigCircleX - (int) event.getX()), 2) + Math.pow((bigCircleY - (int) event.getY()), 2)) >= bigCircleR) {

double tempRad = getRad(bigCircleX, bigCircleY, event.getX(), event.getY());

getXY(bigCircleX, bigCircleY, bigCircleR, tempRad);

} else {//范围内触摸

smallCircleX = (int) event.getX();

smallCircleY = (int) event.getY();

}

} else if (event.getAction() == MotionEvent.ACTION_UP) {

smallCircleX = bigCircleX;

smallCircleY = bigCircleY;

}

return true;

}

弧度计算

通过 event.getX(), event.getY()获得当前的触摸点,与圆点进行计算,获取弧度

/***

* 得到两点之间的弧度

*/

public float getRad(float px1, float py1, float px2, float py2) {

float x = px2 - px1;

float y = py1 - py2;

//斜边的长

float z = (float) Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));

float cosAngle = x / z;

float rad = (float) Math.acos(cosAngle);

if (py2 < py1) {

rad = -rad;

}

return rad;

}

图形绘制

通过 canvas.drawCircle()和 canvas.drawBitmap()分别进行遥感按钮和遥感背景的绘制,注意对遥感背景的保存,如果在绘制的时候每次BitmapFactory.decodeResource()会增加耗时,因此只需在surfaceCreated()中进行bitmap的生成即可。

public void draw() {

try {

canvas = sfh.lockCanvas();

canvas.drawColor(getResources().getColor(R.color.ghostwhite));

// 指定图片绘制区域(左上角的四分之一)

Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());

// 指定图片在屏幕上显示的区域

Rect dst = new Rect(bigCircleX - bigCircleR, bigCircleY - bigCircleR, bigCircleX + bigCircleR, bigCircleY + bigCircleR);

// 绘制图片

remoteViewBg.draw(canvas, paint, src, dst);

paint.setColor(0x70ff0000);

//绘制摇杆

canvas.drawCircle(smallCircleX, smallCircleY, smallCircleR, paint);

} catch (Exception e) {

// TODO: handle exception

} finally {

try {

if (canvas != null)

sfh.unlockCanvasAndPost(canvas);

} catch (Exception e2) {

e2.printStackTrace();

}

}

}

使用

在activity中动态添加

RelativeLayout relativeLayout = (RelativeLayout) findViewById(R.id.dance_relative_layout);

remoteSurfaceView = new RemoteSurfaceView(this);

params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,

RelativeLayout.LayoutParams.MATCH_PARENT);

remoteSurfaceView.setLayoutParams(params);

relativeLayout.addView(remoteSurfaceView);

全部代码

public class RemoteSurfaceView extends SurfaceView implements Callback, Runnable {

private float scale = this.getResources().getDisplayMetrics().density;

private Thread th;

private SurfaceHolder sfh;

private Canvas canvas;

private Paint paint;

private boolean flag;

private int bigCircleX = 0;

private int bigCircleY =0;

private int bigCircleR = 0;

//摇杆的X,Y坐标以及摇杆的半径

private float smallCircleX = 0;

private float smallCircleY = 0;

private float smallCircleR = 0;

private Bitmap bitmap;

private RemoteViewBg remoteViewBg;

public RemoteSurfaceView(Context context) {

super(context);

sfh = this.getHolder();

sfh.addCallback(this);

paint = new Paint();

paint.setAntiAlias(true);

setFocusable(true);

setFocusableInTouchMode(true);

setZOrderOnTop(true);

getHolder().setFormat(PixelFormat.TRANSPARENT);

}

public void surfaceCreated(SurfaceHolder holder) {

int width = getWidth();

int height = getHeight();

bigCircleX = width / 2;

bigCircleY = height / 2;

bigCircleR = width / 4;

smallCircleX = width / 2;

smallCircleY = height / 2;

smallCircleR = width / 8;

bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.fangxiang);

remoteViewBg = new RemoteViewBg(bitmap);

th = new Thread(this);

flag = true;

th.start();

}

/***

* 得到两点之间的弧度

*/

public float getRad(float px1, float py1, float px2, float py2) {

float x = px2 - px1;

float y = py1 - py2;

//斜边的长

float z = (float) Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));

float cosAngle = x / z;

float rad = (float) Math.acos(cosAngle);

if (py2 < py1) {

rad = -rad;

}

return rad;

}

@Override

public boolean onTouchEvent(MotionEvent event) {

if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) {

// 在范围外触摸

if (Math.sqrt(Math.pow((bigCircleX - (int) event.getX()), 2) + Math.pow((bigCircleY - (int) event.getY()), 2)) >= bigCircleR) {

double tempRad = getRad(bigCircleX, bigCircleY, event.getX(), event.getY());

getXY(bigCircleX, bigCircleY, bigCircleR, tempRad);

} else {//范围内触摸

smallCircleX = (int) event.getX();

smallCircleY = (int) event.getY();

}

} else if (event.getAction() == MotionEvent.ACTION_UP) {

smallCircleX = bigCircleX;

smallCircleY = bigCircleY;

}

return true;

}

public void getXY(float x, float y, float R, double rad) {

//获取圆周运动的X坐标

smallCircleX = (float) (R * Math.cos(rad)) + x;

//获取圆周运动的Y坐标

smallCircleY = (float) (R * Math.sin(rad)) + y;

}

public void draw() {

try {

canvas = sfh.lockCanvas();

canvas.drawColor(getResources().getColor(R.color.ghostwhite));

// 指定图片绘制区域(左上角的四分之一)

Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());

// 指定图片在屏幕上显示的区域

Rect dst = new Rect(bigCircleX - bigCircleR, bigCircleY - bigCircleR, bigCircleX + bigCircleR, bigCircleY + bigCircleR);

// 绘制图片

remoteViewBg.draw(canvas, paint, src, dst);

paint.setColor(0x70ff0000);

//绘制摇杆

canvas.drawCircle(smallCircleX, smallCircleY, smallCircleR, paint);

} catch (Exception e) {

// TODO: handle exception

} finally {

try {

if (canvas != null)

sfh.unlockCanvasAndPost(canvas);

} catch (Exception e2) {

e2.printStackTrace();

}

}

}

public void run() {

while (flag) {

draw();

try {

Thread.sleep(50);

} catch (Exception ex) {

ex.printStackTrace();

}

}

}

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

public void surfaceDestroyed(SurfaceHolder holder) {

flag = false;

}

}

总结

以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

在一些手机游戏中,玩家可以通过虚拟控制盘来控制游戏角色的行动。 无人机和玩具操控App中也有这一类控制盘的应用。用自定义View的方式来实现类似手柄的控件。相关代码请见 github.com/RustFisher/…JoystickView特性目前JoystickView特性如下2种风格固定控制盘;浮动跟随模式;控制盘会移动到手指第一次点击的地方可以在背景上添加“箭头”,即添加效果图片自定义的“触摸球”图片和背景图片手指移出了控制盘范围,仍然能够保持追随能获取到移动位置的百分比参数实现思路用自定义View的方式实现这个控制盘。创建TouchView。控制盘的基本要求是跟随手指做出反应。为了获取到手指触屏的坐标,会用到View的onTouchEvent方法。控件中的“触摸球”和背景由图片得来。在自定义view中先获取相应的bitmap,缩放成指定的尺寸。onTouchEvent中获取到相应的坐标,计算出图片应该出现的位置;onDraw中根据坐标进行绘制。 计算出手指位置与控制盘中心的距离等信息,通过listener传递出去。代码示例样式设定有固定和浮动这两种风格,未来可能还会添加public enum PadStyle {     FLOATING /* 随用户手指重新定位 */,     FIXED    /* 固定位置 */ }控制盘配置我们可以不直接操作TouchView,创建TouchViewModel存放相关的配置。    private int bgResId;           // 背景图片资源ID     private int touchBmpResId;     // 触摸图资源ID - 例如一个圆球     private int directionPicResId; // 指示当前触摸点与圆心相对方向的图片ID     private float mWholeViewWid;    // 整个View的宽     private float mWholeViewHeight; // 整个View的高     private float mWholePadWid;    // 盘的宽度,包括箭头;并不是View的总宽度     private float mWholePadHeight; // 盘的高度,包括箭头;并不是View的总宽度     private int mRoundBgRadius;    // 背景圆的半径 背景圆位置可以变化     private int mTouchBallRadius = 100; // 触摸球的半径     private int mRoundBgPadding;   // 背景圆到Pad边界的px  一般是留给方向箭头的位置     private boolean showDirectionPic = false;    // 是否显示指示图片     private PadStyle mPadStyle = PadStyle.FIXED; // 默认为固定位置的     private PadLocationType mPadLocationType = PadLocationType.LEFT_BOT;     // .........控制盘管理器控制盘的配置项比较多,抽象出一个DefaultController来管理控制盘。这个控制器不是必要的。 管理器需要控制盘所在的父View,这里用的是RelativeLayout。创建一个“左控制盘”。将各个尺寸配置传入。最后添加到containerView中。    private void createLeftControlTouchView() {         TouchViewModel model = new TouchViewModel(                 R.drawable.ui_pic_joystick_left_pad,                 R.drawable.ui_pic_joystick_control_ball);         model.setWholeViewSize(ctx.getResources().getDimensionPixelSize(R.dimen.ui_joystick_whole_field_wid),                 ctx.getResources().getDimensionPixelSize(R.dimen.ui_joystick_whole_field_height));         model.setPadSize(ctx.getResources().getDimensionPixelSize(R.dimen.ui_joystick_pad_size),                 ctx.getResources().getDimensionPixelSize(R.dimen.ui_joystick_pad_size));         int roundBgRadius = ctx.getResources().getDimensionPixelSize(R.dimen.ui_joystick_round_bg_radius);         model.setContentSize(roundBgRadius, (int) (roundBgRadius / 3.5));         model.setStyle(padStyle, PadLocationType.LEFT_BOT);         model.setRoundBgPadding(ctx.getResources().getDimensionPixelSize(R.dimen.ui_joystick_circle_bg_padding));         leftControlTouchView = new TouchView(ctx);         leftControlTouchView.init(model);         // View的总大小         RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(                 ctx.getResources().getDimensionPixelSize(R.dimen.ui_joystick_whole_field_wid),                 ctx.getResources().getDimensionPixelSize(R.dimen.ui_joystick_whole_field_height)         );         params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);         params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);         leftControlTouchView.setLayoutParams(params);     }     // ............     createLeftControlTouchView();     containerView.addView(leftControlTouchView);管理器初始化时需要一个ViewGroup来承载控制盘。    public DefaultController(Context context, RelativeLayout containerView, PadStyle padStyle) {         this.ctx = context;         this.containerView = containerView;         this.padStyle = padStyle;     }Fragment中使用初始化管理器初始化管理器,创建控制盘    mDefaultController =             new DefaultController(getContext(),                     (RelativeLayout) root.findViewById(R.id.joystick_container));     mDefaultController.createViews();     mDefaultController.showViews(false);设置监听器,获取用户的操作信息通过控制器来设置监听器        mDefaultController.setLeftTouchViewListener(new JoystickTouchViewListener() {             @Override             public void onTouch(float horizontalPercent, float verticalPercent) {                 Log.d(TAG, "onTouch left: "   horizontalPercent   ", "   verticalPercent);             }             @Override             public void onReset() {                 Log.d(TAG, "onReset: left");             }             @Override             public void onActionDown() {                 Log.d(TAG, "onActionDown: left");             }             @Override             public void onActionUp() {                 Log.d(TAG, "onActionUp: left");             }         });至此,我们实现了一个简单的控制盘控件。在一些控制类应用中可以使用这个控件。若想要做出更优美,更吸引人的控件,需要我们有好的审美水平。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值