GameRocker: 360°游戏摇杆
来源:互联网 发布:淘宝店铺上架宝贝软件 编辑:程序博客网 时间:2024/05/01 08:26
GameRocker: 360°游戏摇杆
GameRocker是一个自定义View,类似于游戏操作中的摇杆,可以实现360°方向切换。
最近因为项目需求,需要在Android平板上使用类似游戏手柄上的360°摇杆来实现机器人运动的控制。
首先,看看实现的效果图:
其实现原理比较简单,设置小圆的触摸事件,并跟随手指的滑动而移动,但不能超过大圆的范围。但是手指移动的范围是可以大于大圆的范围的,所以需要先判断手指移动的范围是否超过大圆范围,超过了就需要进行坐标折算。因为是同心圆,所以,判断的是否超限的依据可以简化为,手指移动的x,y坐标的平方和是否大于最初的圆环的宽度。其原理示意图如下:
对于超限的移动,需要折算坐标,其公式如下:
double distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY); distanceX = (float) (originLeft * distanceX / distance); distanceY = (float) (originTop * distanceY / distance);
其中distanceX 、distanceY 为手指移动的距离;originLeft 、originTop 为最开始小圆的marginLfet和marginTop,也就是圆环的宽度。
其实现步骤可以分为以下几步:
1、使用shape.xml文件画出大、小两个圆,并同心放置在布局文件里
circle_background.xml:
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:height="150dp" android:width="150dp"/> <solid android:color="@color/background" /></shape>
circle_button.xml:
<?xml version="1.0" encoding="utf-8"?><shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:height="80dp" android:width="80dp"/> <solid android:color="@color/light_blue" /></shape>
布局文件:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="200dp" android:layout_height="200dp"> <ImageView android:layout_width="200dp" android:layout_height="200dp" android:src="@drawable/circle_background" android:layout_centerInParent="true" android:id="@+id/background"/> <ImageView android:layout_width="80dp" android:layout_height="80dp" android:src="@drawable/circle_button" android:layout_marginTop="60dp" android:layout_marginLeft="60dp" android:id="@+id/rocker" android:clickable="true"/></RelativeLayout>
2、新建一个GameRocker继承RelativeLayout,代码如下:
package com.james_jiang.gamerocker.game_rocker;import android.annotation.TargetApi;import android.content.Context;import android.os.Build;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.widget.ImageView;import android.widget.RelativeLayout;import com.james_jiang.gamerocker.R;public class GameRocker extends RelativeLayout implements View.OnTouchListener{ private final static String TAG = "GameRocker"; private ImageView rocker; private ImageView background; private boolean loadOnce; private GameRockerListener listener; //rocker移动监听,传递坐标数据 private float xDown; //按下时x坐标 private float yDown; //按下时y坐标 private int originLeft; //也是最大移动距离 private int originTop; private float xDistance; //上一次移动的x距离 private float yDistance; private RelativeLayout.LayoutParams params; private final static int DIVIDE = 15; //松开时,反弹传递的距离等分数 public GameRocker(Context context) { super(context); initFiled(); initView(context); } public GameRocker(Context context, AttributeSet attrs) { super(context, attrs); initFiled(); initView(context); } public GameRocker(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initFiled(); initView(context); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public GameRocker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initFiled(); initView(context); } private void initFiled(){ loadOnce = false; listener = null; xDown = 0.0f; yDown = 0.0f; xDistance = 0.0f; yDistance = 0.0f; } private void initView(Context context){ View gameRocker = LayoutInflater.from(context).inflate(R.layout.game_rocker, null, true); rocker = (ImageView) gameRocker.findViewById(R.id.rocker); background = (ImageView) gameRocker.findViewById(R.id.background); rocker.setOnTouchListener(this); addView(gameRocker, 0); //需要添加视图 } @Override protected void onLayout(boolean change, int l, int t, int r, int b){ super.onLayout(change, l, t, r, b); if(change && !loadOnce){ rocker.setImageResource(R.drawable.circle_button); background.setImageResource(R.drawable.circle_background); params = (RelativeLayout.LayoutParams) rocker.getLayoutParams(); loadOnce = true; originTop = rocker.getTop(); originLeft = rocker.getLeft(); } } //注册监听器 public void setOnGameRockerListener(GameRockerListener listener){ this.listener = listener; } @Override public boolean onTouch(View view, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: xDown = event.getRawX(); yDown = event.getRawY(); break; case MotionEvent.ACTION_MOVE: float distanceX = event.getRawX() - xDown; float distanceY = event.getRawY() - yDown; //先判断是否越界 if(!isInnerCircle(distanceX, distanceY)){ //坐标折算 double distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY); distanceX = (float) (originLeft * distanceX / distance); distanceY = (float) (originTop * distanceY / distance); } //再执行响应 if(distanceX != xDistance || distanceY != yDistance) { xDistance = distanceX; yDistance = distanceY; params.setMargins((int) (distanceX + originLeft), (int) (distanceY + originTop), 0, 0); rocker.setLayoutParams(params); if(listener != null){ listener.onDirection(distanceX, distanceY); } } break; case MotionEvent.ACTION_UP: float divideX = xDistance / DIVIDE; float divideY = yDistance / DIVIDE; for (int i = 0; i < DIVIDE; i++) { xDistance = xDistance - divideX; yDistance = yDistance - divideY; if(i == DIVIDE - 1) { xDistance = 0.0f; yDistance = 0.0f; } params.setMargins((int) xDistance + originLeft, (int) yDistance + originTop, 0, 0); rocker.setLayoutParams(params); if (listener != null) { listener.onDirection(xDistance, yDistance); } } break; } return false; } //判断是否在外圈内,因为是同心圆,所以任何方向上都只能平移圆环的宽度 private boolean isInnerCircle(float xMove, float yMove){ if(Math.sqrt(xMove * xMove + yMove *yMove) > originLeft) return false; return true; }}
3、摇杆移动监听器,在使用摇杆的地方注册监听器,获取移动的x、y坐标。
package com.james_jiang.gamerocker.game_rocker;public interface GameRockerListener { //入参是移动的x和y的距离 void onDirection(float x, float y); }
4、在主Activity布局内加入该控件,并注册监听器。
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.james_jiang.gamerocker.MainActivity"> <com.james_jiang.gamerocker.game_rocker.GameRocker android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/game_rocker" android:layout_centerInParent="true"/></RelativeLayout>
MainActivity.java:
package com.james_jiang.gamerocker;import android.app.Activity;import android.os.Bundle;import android.util.Log;import com.james_jiang.gamerocker.game_rocker.GameRocker;import com.james_jiang.gamerocker.game_rocker.GameRockerListener;public class MainActivity extends Activity implements GameRockerListener{ private final static String TAG = "MainActivity"; private GameRocker gameRocker; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView(){ gameRocker = (GameRocker) findViewById(R.id.game_rocker); setOnListener(); } private void setOnListener(){ gameRocker.setOnGameRockerListener(this); } @Override public void onDirection(float x, float y) { Log.i(TAG, "x = " + x + ", y = " + y); }}
到这里,整个项目就完成了。附上源码地址:
源码下载:
第一次写blog,写的不好,大家见谅,有bug望不吝赐教,最后希望找到小伙伴能在Android学习上相互帮助,QQ:853397420。
- GameRocker: 360°游戏摇杆
- 360°平滑游戏摇杆 Rocker
- 360度游戏摇杆
- 360°平滑游戏摇杆(触屏方向导航)
- Android游戏开发之360°半透明摇杆。。。。
- 【Android游戏开发二十四】360°平滑游戏摇杆(触屏方向导航)
- (转)【Android游戏开发二十四】360°平滑游戏摇杆(触屏方向导航)
- 【Android游戏开发二十四】360°平滑游戏摇杆(触屏方向导航)
- 【Android游戏开发二十四】360°平滑游戏摇杆(触屏方向导航)
- 【ANDROID游戏开发二十四】360°平滑游戏摇杆(触屏方向导航)
- 24—360°平滑游戏摇杆(触屏方向导航)
- Android 360°平滑游戏摇杆(触屏方向导航)
- (libgdx小结)游戏摇杆
- libgdx 游戏摇杆
- liggdx 游戏摇杆类
- Android手机游戏摇杆
- 手机游戏的摇杆
- AS3.0做游戏的360度摇杆
- 二维裁剪
- Android获取屏幕大小的三种方法
- StringBuffer与StringBuilder的区别
- java中对数字型数据的一些操作方法
- ios对输入框长度的限制,并且首位不能为空
- GameRocker: 360°游戏摇杆
- C语言(函数)
- MongoDB索引与固定集合
- windows下不需要插件使用MyEclipse运行hadoop2.6.0 MapReduce程序。
- 运行时错误检查(/RTC)编译选项及实现原理
- 剑指offer面试题24:二叉搜索树的后序遍历序列
- 关于Java字符串中提取自己想要的数据进行计算
- [从头学数学] 第261节 Python实现数据结构:红黑树(RB Tree)
- 用python自定义实现db2的连接池