Andriod小程式——簡單製作遊戲中控制任務移動的輪盤
阿新 • • 發佈:2018-12-16
說明
近期有在做一個專案,專案的其中一個要求就是,做一個控制裝置上下左右的輪盤。網上找了好多都是猜獎轉盤,本菜鳥表示心情十分複雜。於是在花費了19個小時之後,本菜雞才簡單製作了一個 成型,話不多說,先上圖
上面出現的兩個數,是移動的引數,一會兒我會說明。
這個效果實現起來相當簡單 文末含有整個類 ,只要有一點android基礎,一看就懂,下面一步步解釋。
自定義自己的view繼承於View類
一般都是繼承於View類,看自己的需求。
當然也可以繼承於SurfaceView,但是這裡,不會一直重新整理,我認為沒必要。
public class DiscView extends View {
//有人可能不懂三個構造方法的意思,我解釋一下
//在主程式中new的時候呼叫這個
//DiscView disc = new DiscView(this);
public DiscView(Context context) {
this(context,null);
}
//在xml檔案中建立控制元件是呼叫這個
public DiscView(Context context,AttributeSet attrs) {
this(context, attrs, 0 );
}
//在xml檔案中建立控制元件,指定style屬性的時候會使用這個方法,否則預設第二種
public DiscView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
manager = LocalBroadcastManager.getInstance(context);
}
}
重寫onDraw()方法
這裡是 重點,整個控制元件的核心 要一步步來。
當我們看到這個控制元件的時候那個樣子,如圖
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.TRANSPARENT);
//設定背景圖的半徑
radiusBack = getWidth() / 2;
//設定小圓點的半徑
radiusPre = getWidth() / 4;
//圓心
circleX = getWidth() / 2;
circleY = getHeight() / 2;
if (paint == null) {
paint = new Paint();
}
//設定畫筆的基本屬性
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(0x7f111111);
//畫出內圓
canvas.drawCircle(circleX,circleY,(float) radiusBack,paint);
//畫出外圓,內外圓顏色不一樣,所以要用不同的顏色
paint.setColor(0xFF744041);
canvas.drawCircle(btnX,btnY,(float) radiusPre,paint);
canvas.save();
}
但是這個肯定不是我們需要的效果。
所以說肯定是要動態重新整理的。我們整理一下思路。
接下來實現這個過程
完善onDraw()方法
因為要捕獲手指的位置,因此重寫onTouch()方法是不可少的
重寫OnTouch()方法
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_MOVE:
//手指在螢幕上移動會呼叫這個方法,(btnX,btnY)是內部圓的圓心
//我們要隨時根據自己的手指位置來帶動內部圓移動,因此在手指移動過程中要記錄圓心。
btnX = (int) event.getX();
btnY = (int) event.getY();
//記錄圓心,進行判斷,我們不希望我們的內部圓完全跑到外部之外,所以要進行判斷,當內部圓的圓心在離開外圓,就控制他不離開外圓
//這裡用到了勾股定理
if ((btnY - circleY)*(btnY - circleY) + (btnX - circleX)*(btnX - circleX) >= radiusBack * radiusBack){
//改變圓心位置
changeBtnLocation();
}
//每次都要提交併重新整理螢幕,invalidate會呼叫OnDraw()方法
this.invalidate();
break;
case MotionEvent.ACTION_UP:
//當手指離開的時候 把btnX和btnY都置為零,onMesure中進行判斷,當兩個都為零時
btnX = 0;
btnY = 0;
//同樣重新整理
this.invalidate();
break;
}
//返回true時螢幕才會對你的手勢攔截並消費。
return true;
}
關於changeBtnLocation()方法,這裡會用到數學知識。
這裡我畫了一個圖,簡單易懂
/**
* 這裡會用到數學知識,相似三角形
*/
private void changeBtnLocation() {
//相似比
double similarity = Math.sqrt((radiusBack * radiusBack) / ((btnY - circleY)*(btnY - circleY) + (btnX - circleX)*(btnX - circleX)));
//改變(btnX,btnY)的位置
btnX = (int) ((btnX - circleX)*similarity + circleX);
btnY = (int) ((btnY - circleY)*similarity + circleY);
}
之前的onDraw()方法只有之前的版本,這裡,我們要改一下,讓他根據生成的btnX btnY的值進行不同的重新整理
更新onDraw()方法
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.TRANSPARENT);
//設定背景圖的半徑
radiusBack = getWidth() / 2;
//設定小圓點的半徑
radiusPre = getWidth() / 4;
//圓心
circleX = getWidth() / 2;
circleY = getHeight() / 2;
if (paint == null) {
paint = new Paint();
}
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(0x7f111111);
canvas.drawCircle(circleX,circleY,(float) radiusBack,paint);
//如果手指離開螢幕
if (btnX <= 0 && btnY <= 0){
paint.setColor(0xFFF10159);
canvas.drawCircle(circleX,circleY,(float) radiusPre,paint);
canvas.save();
return;
}
//手指點選螢幕,之所以顏色引數不一樣,是為了讓點選有反饋
paint.setColor(0xFF744041);
canvas.drawCircle(btnX,btnY,(float) radiusPre,paint);
canvas.save();
}
寫到這裡,會有一個問題差不多就可以跑起來了。但是還有幾個問題
- 控制元件的layout_width 和 layout_height要一樣。
- 內圓移動到邊緣會被切割掉,很醜。
- 控制元件寫完了,但是我們的主程式怎麼用啊。總不能為了好看而搞個控制元件出來。
關於第一個問題網上很多解決方法 ,我懶得寫了 ,我是用的時候直接設定一樣的,如有需要,重寫 onMeasure() 方法
解決遺留問題
解決切割問題
很簡單,讓內圓和外圓的直徑加起來小於控制元件的寬度,即getWidth()即可
//設定背景圖的半徑
radiusBack = getWidth() / 3;
//設定小圓點的半徑
radiusPre = getWidth() / 8;
// 2 / 3 + 2 / 8 = 11 / 12 < 1
解決主程式的呼叫問題
這個也很簡單;在移動或者手指離開的時候 根據需求 發個廣播就行了
在主程式中接收廣播就行了
private void sendAction(int x,int y) {
Intent intent = new Intent(DISC_BROADCAST);
intent.putExtra("move",(x - circleX) + " " + (y - circleY));
manager.sendBroadcast(intent);
}
最後放上程式的完整程式碼
重寫的控制元件
package com.example.administrator.myapplication;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.v4.content.LocalBroadcastManager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
public class DiscView extends View {
private Paint paint;
private int btnX;
private int btnY;
//內外圓半徑
private int radiusBack;
private int radiusPre;
//外圓圓心
private float circleX;
private float circleY;
//廣播action
public static final String DISC_BROADCAST = "com.scsdesign.DISCVIEW";
private LocalBroadcastManager manager;
public DiscView(Context context) {
this(context,null);
}
public DiscView(Context context,AttributeSet attrs) {
this(context, attrs, 0 );
}
public DiscView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
manager = LocalBroadcastManager.getInstance(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.TRANSPARENT);
//設定背景圖的半徑
radiusBack = getWidth() / 3;
//設定小圓點的半徑
radiusPre = getWidth() / 8;
//圓心
circleX = getWidth() / 2;
circleY = getHeight() / 2;
if (paint == null) {
paint = new Paint();
}
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(0x7f111111);
canvas.drawCircle(circleX,circleY,(float) radiusBack,paint);
if (btnX <= 0 && btnY <= 0){
paint.setColor(0xFFF10159);
canvas.drawCircle(circleX,circleY,(float) radiusPre,paint);
canvas.save();
return;
}
paint.setColor(0xFF744041);
canvas.drawCircle(btnX,btnY,(float) radiusPre,paint);
canvas.save();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_MOVE:
System.out.println(event.getX() + " " + event.getY());
btnX = (int) event.getX();
btnY = (int) event.getY();
if ((btnY - circleY)*(btnY - circleY) + (btnX - circleX)*(btnX - circleX) >= radiusBack * radiusBack){
changeBtnLocation();
}
this.invalidate();
break;
case MotionEvent.ACTION_UP:
System.out.println(event.getX() + " " + event.getY() + "TAG");
//發廣播
sendAction(btnX,btnY);
btnX = 0;
btnY = 0;
this.invalidate();
break;
}
return true;
}
private void sendAction(int x,int y) {
Intent intent = new Intent(DISC_BROADCAST);
intent.putExtra("move",(x - circleX) + " " + (y - circleY));
manager.sendBroadcast(intent);
}
/**
* 這裡會用到數學知識
*/
private void changeBtnLocation() {
//相似比
double similarity = Math.sqrt((radiusBack * radiusBack) / ((btnY - circleY)*(btnY - circleY) + (btnX - circleX)*(btnX - circleX)));
btnX = (int) ((btnX - circleX)*similarity + circleX);
btnY = (int) ((btnY - circleY)*similarity + circleY);
}
}
主程式中呼叫
package com.example.administrator.myapplication;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.SurfaceView;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private TextView textView;
private String message;
private MyBroadCastReceiver receiver;
private LocalBroadcastManager manager;
private DiscView view;
private boolean is = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
view = findViewById(R.id.view);
textView = findViewById(R.id.textView2);
/**
* 註冊本地廣播接收器就,接收廣播
**/
manager = LocalBroadcastManager.getInstance(this);
IntentFilter filter = new IntentFilter();
filter.addAction(view.DISC_BROADCAST);
receiver = new MyBroadCastReceiver();
manager.registerReceiver(receiver,filter);
}
class MyBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String string = intent.getStringExtra("move");
textView.setText(string);
}
}
}