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。

0 0
原创粉丝点击