Android - JoystickView 虚拟手柄,控制盘,自定义UI

在一些手机游戏中,玩家可以通过虚拟控制盘来控制游戏角色的行动。 无人机和玩具操控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");
            }
        });
复制代码

至此,我们实现了一个简单的控制盘控件。在一些控制类应用中可以使用这个控件。

若想要做出更优美,更吸引人的控件,需要我们有好的审美水平。

你可能感兴趣的:(Android - JoystickView 虚拟手柄,控制盘,自定义UI)