在一些手機遊戲中,玩家可以通過虛擬控制盤來控制遊戲角色的行動。 無人機和玩具操控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");
}
});
複製程式碼
至此,我們實現了一個簡單的控制盤控制元件。在一些控制類應用中可以使用這個控制元件。
若想要做出更優美,更吸引人的控制元件,需要我們有好的審美水平。