今天要來實作這兩個功能的結合,那麼首先先加入需要的(手把)依賴至gradle中:
implementation 'io.github.controlwear:virtualjoystick:1.10.1'
那麼就先附上今天UI(FrameLayout用來放SurfaceView、並加入手把JoyStickView):
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:custom="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity">
<FrameLayout
android:id="@+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</FrameLayout>
<io.github.controlwear.virtual.joystick.android.JoystickView
android:id="@+id/joyStick"
android:layout_width="280dp"
android:layout_height="160dp"
android:layout_centerHorizontal="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0"
custom:JV_backgroundColor="#11FFFF"
custom:JV_borderWidth="1dp"
custom:JV_fixedCenter="true" />
</androidx.constraintlayout.widget.ConstraintLayout>
首先前置同前幾天一樣,先繼承SurfaceView並實作SurfaceHolder.Callback並繼承裡面的方法跟constructor,
然後加入Canvas需要的畫筆(paint)並設置。
public class SurfaceActivity extends SurfaceView implements SurfaceHolder.Callback{
private SurfaceHolder mSurfaceHolder;
private Paint paint;
public SurfaceActivity(Context context) {
super(context);
init();
}
private void init(){
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
//加入畫筆
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
paint.setStrokeWidth(10);
paint.setTextSize(32);
}
//當Surface創建會呼叫,通常會開啟繪圖的線程(非主線程)
@Override
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
}
//當Surface尺寸發生變化則呼叫
@Override
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
//當Surface被銷毀會呼叫
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
}
}
接著要進行繪圖時,Canvas有兩個關鍵的方法:
//返回鎖定Canvas畫布
canvas = mSurfaceHolder.lockCanvas();
//...繪圖Function執行
//結束鎖定,提交顯示改變
mSurfaceHolder.unlockCanvasAndPost(canvas);
然後通常會加入delay去跑繪圖指令(這邊是用Handler+postDelayed延遲):
Handler mHandler = new Handler();
//每50ms進行一次繪圖
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
//返回鎖定Canvas畫布
canvas = mSurfaceHolder.lockCanvas();
//...繪圖Function執行
//結束鎖定,提交顯示改變
mSurfaceHolder.unlockCanvasAndPost(canvas);
mHandler.postDelayed(this, 50);
}
}, 50);
接著就設計需要的程式碼:
public class SurfaceActivity extends SurfaceView implements SurfaceHolder.Callback{
private SurfaceHolder mSurfaceHolder;
private Paint paint;
private Canvas canvas;
float screenWidth, screenHeight;
int pos_x,pos_y,move_x=0,move_y=0;
public SurfaceActivity(Context context) {
super(context);
init();
}
private void init(){
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
//畫筆
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
paint.setStrokeWidth(10);
paint.setTextSize(32);
}
//繪圖Function,畫30Radius圓圈
public void drawView(int x , int y){
Log.d("TAGG","DrawView"+pos_x+"\n"+pos_y);
canvas.drawCircle(pos_x = pos_x+x,pos_y = pos_y+y,30,paint);
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
Log.d("TAGG","surfaceCreated");
//取得螢幕大小
screenWidth=getWidth();
screenHeight=getHeight();
//設置初始位置
pos_x= (int) (screenWidth*1/4);
pos_y= (int) (screenHeight*1/4);
//返回鎖定Canvas畫布
canvas = mSurfaceHolder.lockCanvas();
//繪圖Function
drawView(pos_x,pos_y);
//改變顯示
mSurfaceHolder.unlockCanvasAndPost(canvas);
Handler mHandler = new Handler();
//每50ms進行一次繪圖
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
//返回鎖定Canvas畫布
canvas = mSurfaceHolder.lockCanvas();
//繪圖Function
drawView(move_x, move_y);
//清零移動量
move_x = 0;
move_y = 0;
//改變顯示
mSurfaceHolder.unlockCanvasAndPost(canvas);
mHandler.postDelayed(this, 50);
}
}, 50);
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {
Log.d("TAGG","surfaceChanged");
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
Log.d("TAGG","surfaceDestroyed");
}
//回傳canvas對象
public Canvas getCanvas(){
return canvas;
}
//從activity設定需要的移動量
public void setMove_x(int move_x) {
this.move_x = move_x;
}
public void setMove_y(int move_y) {
this.move_y = move_y;
}
}
//onCreate...{
surfaceActivity = new SurfaceActivity(this);
fl = findViewById(R.id.frameLayout);
//將surfaceView加入至FrameLayout上
fl.addView(surfaceActivity);
//
joystickView=findViewById(R.id.joyStick);
//搖桿監聽器
joystickView.setOnMoveListener(new JoystickView.OnMoveListener() {
@Override
public void onMove(int angle, int strength) {
}
});
接著設計需要的MainActivity程式碼。
public class MainActivity extends AppCompatActivity {
SurfaceActivity surfaceActivity;
FrameLayout fl;
Canvas canvas;
//搖桿
JoystickView joystickView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
surfaceActivity = new SurfaceActivity(this);
fl = findViewById(R.id.frameLayout);
//將surfaceView加入至FrameLayout上
fl.addView(surfaceActivity);
joystickView=findViewById(R.id.joyStick);
//搖桿監聽器
joystickView.setOnMoveListener(new JoystickView.OnMoveListener() {
@Override
public void onMove(int angle, int strength) {
//取得surfaceView的Canvas
canvas = surfaceActivity.getCanvas();
//搖桿angle為
// 90度
//180 0
// 270
if(angle>0&&angle<=90){
//計算x y移動量
int x = (int) (10-(angle/9));
int y = (int) -(angle/30);
//設定x y移動量
surfaceActivity.setMove_x(x);
surfaceActivity.setMove_y(y);
}
else if(angle>90&&angle<=180){
//計算x y移動量
int x = (int) ((180-angle)/9)-10;
int y = (int) -((180-angle)/30);
//設定x y移動量
surfaceActivity.setMove_x(x);
surfaceActivity.setMove_y(y);
}
else if(angle>180&&angle<=270){
//計算x y移動量
int x = (int) -((270-angle)/9);
int y = (int) ((angle-180)/30);
//設定x y移動量
surfaceActivity.setMove_x(x);
surfaceActivity.setMove_y(y);
}
else{
//計算x y移動量
int x = (int) ((angle-270)/9);
int y = (int) ((360-angle)/30);
//設定x y移動量
surfaceActivity.setMove_x(x);
surfaceActivity.setMove_y(y);
}
}
});
}
}