1. 程式人生 > >Andriod小程式——簡單製作遊戲中控制任務移動的輪盤

Andriod小程式——簡單製作遊戲中控制任務移動的輪盤

說明

近期有在做一個專案,專案的其中一個要求就是,做一個控制裝置上下左右的輪盤。網上找了好多都是猜獎轉盤,本菜鳥表示心情十分複雜。於是在花費了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);
        }
    }

}