android 模擬航拍遙控
由於最近做一個航拍項目,手機端模擬遙控,控制無人機,之前在網上這方面的知識比較少,所有就貼出來demo供大家參考,廢話少說,先貼圖
左右兩個點,在圈內活動,一個是控制油門,一個是控制方向,放手後會返回中心點,這些在遊戲場景中經常看到,比如射擊類的遊戲,這裏自定義view,繼承ImageView,難點就在手指控制這部分,以下是源碼。
package com.remotecontrol;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.widget.ImageView;
/**
* Created by qingyuan on 2016/5/20.
* 自定義view 模擬遙控器
*/
public class RemoteControl extends ImageView {
public final String TAG="RemoteControl";
public RemoteControl(Context context) {
super(context);
InitData( context);
}
public RemoteControl(Context context, AttributeSet attrs) {
super(context, attrs);
InitData( context);
}
public RemoteControl(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
InitData( context);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public RemoteControl(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
InitData( context);
}
DisplayMetrics dm = new DisplayMetrics();
WindowManager manager;
//屏幕的寬跟高
int mDisplayWidth;
int mDisplayWidth_2;//屏幕寬的1/2
int mDisplayWidth_4;//屏幕寬的1/4
int mDisplayWidth_43;//屏幕寬的3/4
int mDisplayHeight;
int mDisplayHeight_2;//屏幕高的1/2
int mDisplayHeight_4;//屏幕高的1/4
int mDisplayHeight_43;//屏幕高的3/4
Point leftCenter;//左邊中點的座標
Point rightCenter;//右邊中點的座標
/********************右邊的底圖**************************/
Bitmap btm_bg;
Rect rectSrc_bg;
Rect rectDst_bg;
/********************左邊的底圖**************************/
Bitmap btm_bar;
Rect rectSrc_bar;
Rect rectDst_bar;
/********************左邊的圓點**************************/
Bitmap btm_circle_left;
MyPoint leftPoint;
double leftDistance;//距離中心點的距離
double leftAngle;//atan2的角度值
int leftCircle_2;//圓的一半
/********************右邊的圓點**************************/
Bitmap btm_circle_right;
MyPoint rightPoint;
double rightDistance;//距離中心點的距離
double rightAngle;//atan2的角度值
int rightCircle_2;//圓的一半
int stopRadius;//圓的半徑,左右的半徑是一樣的
Matrix matrix;//矩陣,同過改變矩陣來改變bitmap的位置
final double degToRad = Math.PI/180.0;
final double radToDeg = 180.0/Math.PI;
public void InitData(Context context)
{
manager = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
manager.getDefaultDisplay().getMetrics(dm);
mDisplayWidth = dm.widthPixels;
mDisplayHeight = dm.heightPixels;
mDisplayWidth_2=mDisplayWidth/2;//屏幕寬的1/2
mDisplayWidth_4=mDisplayWidth/4;//屏幕寬的1/4
mDisplayWidth_43=mDisplayWidth*3/4;//屏幕寬的3/4
mDisplayHeight_2=mDisplayHeight/2;//屏幕高的1/2
mDisplayHeight_4=mDisplayHeight/4;//屏幕高的1/4
mDisplayHeight_43=mDisplayHeight*3/4;//屏幕高的3/4
btm_bg= BitmapFactory.decodeResource(context.getResources(),R.drawable.control_bg);
btm_circle_left= BitmapFactory.decodeResource(context.getResources(),R.drawable.records);
btm_circle_right= BitmapFactory.decodeResource(context.getResources(),R.drawable.help);
btm_bar= BitmapFactory.decodeResource(context.getResources(),R.drawable.shift_bar_bg);
matrix = new Matrix();
float scaleSize= (float) (mDisplayHeight*1.0/btm_bg.getWidth()*0.65f);//縮放爲屏幕的0.65
matrix.postScale(scaleSize,scaleSize);
btm_bg = Bitmap.createBitmap(btm_bg,0,0,btm_bg.getWidth(),btm_bg.getHeight(),matrix,true);
btm_bar = Bitmap.createBitmap(btm_bar,0,0,btm_bar.getWidth(),btm_bar.getHeight(),matrix,true);
btm_circle_left = Bitmap.createBitmap(btm_circle_left,0,0,btm_circle_left.getWidth(),btm_circle_left.getHeight(),matrix,true);
btm_circle_right = Bitmap.createBitmap(btm_circle_right,0,0,btm_circle_right.getWidth(),btm_circle_right.getHeight(),matrix,true);
leftCircle_2=btm_circle_left.getWidth()/2;
rightCircle_2=btm_circle_right.getWidth()/2;
//左邊中心點的位置設爲
leftCenter=new Point();
leftCenter.set(mDisplayWidth_4,mDisplayHeight_2);
//右邊中心點的位置設爲
rightCenter=new Point();
rightCenter.set(mDisplayWidth_43,mDisplayHeight_2);
/***********************左邊的地圖位置***************************/
rectSrc_bar=new Rect(0,0,btm_bar.getWidth(),btm_bar.getHeight());
rectDst_bar=new Rect(
leftCenter.x-btm_bg.getWidth()/2,
leftCenter.y-btm_bg.getHeight()/2,
leftCenter.x+btm_bg.getWidth()/2,
leftCenter.y+btm_bg.getHeight()/2
);
/***********************左邊的圓點位置***************************/
leftPoint=new MyPoint(leftCenter.x, leftCenter.y);
/***********************右邊的圓底圖位置***************************/
rectSrc_bg=new Rect(0,0,btm_bg.getWidth(),btm_bg.getHeight());
rectDst_bg=new Rect(
rightCenter.x-btm_bar.getWidth()/2,
rightCenter.y-btm_bar.getHeight()/2,
rightCenter.x+btm_bar.getWidth()/2,
rightCenter.y+btm_bar.getHeight()/2
);
/***********************右邊邊的圓點位置***************************/
rightPoint=new MyPoint(rightCenter.x,rightCenter.y);
//半徑爲底圖的一半,這裏決定了可移動圓的大小
stopRadius=btm_bg.getWidth()/2;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(btm_bg,rectSrc_bg,rectDst_bg,null);
canvas.drawBitmap(btm_bar,rectSrc_bar,rectDst_bar,null);
//更新的左中心點的位置
matrix.reset();
matrix.postTranslate(leftPoint.x-leftCircle_2,leftPoint.y-leftCircle_2);
canvas.drawBitmap(btm_circle_left,matrix,null);
//更新的右中心點的位置
matrix.reset();
matrix.postTranslate(rightPoint.x-rightCircle_2,rightPoint.y-rightCircle_2);
canvas.drawBitmap(btm_circle_right,matrix,null);
}
@TargetApi(Build.VERSION_CODES.ECLAIR)
@Override
public boolean onTouchEvent(MotionEvent event) {
float x;
float y;
double distance;//圓點離中心點的距離
double angle;//圓點tan2的角度
float x2;
float y2;
double angle2;
double distance2;
switch (MotionEvent.ACTION_MASK & event.getAction())
{
case MotionEvent.ACTION_DOWN:
//判斷第一個手指按下的位置所在的區域
if(Math.abs(event.getX()-rightPoint.x)<btm_circle_right.getWidth()
&& Math.abs(event.getY()-rightPoint.y)<btm_circle_right.getHeight())
{
rightPoint.setPointerIndex(0);
rightPoint.setCanMove(true);
}
else if(Math.abs(event.getX()-leftPoint.x)<btm_circle_left.getWidth()
&& Math.abs(event.getY()-leftPoint.y)<btm_circle_left.getHeight())
{
leftPoint.setPointerIndex(0);
leftPoint.setCanMove(true);
}
break;
case MotionEvent.ACTION_POINTER_UP:
//獲取離開手指的id
int pointerId = (event.getAction()&MotionEvent.ACTION_POINTER_ID_MASK)>>> MotionEvent.ACTION_POINTER_ID_SHIFT;
//根據手指離開的id判斷是開了的是哪個移動的點
if(rightPoint.isCanMove() && rightPoint.getPointerIndex()==pointerId){
rightPoint.setCanMove(false);
//手指離開讓圓點返回中心點
rightPoint.x=rightCenter.x;
rightPoint.y=rightCenter.y;
rightPoint.setPointerIndex(-1);
}
if(leftPoint.isCanMove() &&leftPoint.getPointerIndex()==pointerId)
{
leftPoint.setCanMove(false);
leftPoint.x=leftCenter.x;
leftPoint.y=rightCenter.y;
leftPoint.setPointerIndex(-1);
}
break;
case MotionEvent.ACTION_UP:
//全部手指離開之後
rightPoint.setCanMove(false);
rightPoint.x=rightCenter.x;
rightPoint.y=rightCenter.y;
rightPoint.setPointerIndex(0);
leftPoint.setCanMove(false);
leftPoint.x=leftCenter.x;
leftPoint.y=leftCenter.y;
leftPoint.setPointerIndex(0);
break;
case MotionEvent.ACTION_POINTER_DOWN:
//獲取第二根手指的id
pointerId = (event.getAction()&MotionEvent.ACTION_POINTER_ID_MASK)>>> MotionEvent.ACTION_POINTER_ID_SHIFT;
//第二根手指按下
if(!rightPoint.isCanMove() && Math.abs(event.getX(pointerId)-rightPoint.x)<btm_circle_right.getWidth()
&& Math.abs(event.getY(pointerId)-rightPoint.y)<btm_circle_right.getHeight())
{
rightPoint.setPointerIndex(pointerId);
rightPoint.setCanMove(true);
}else if(!leftPoint.isCanMove() && Math.abs(event.getX(pointerId)-leftPoint.x)<btm_circle_left.getWidth()
&& Math.abs(event.getY(pointerId)-leftPoint.y)<btm_circle_left.getHeight())
{
leftPoint.setPointerIndex(pointerId);
leftPoint.setCanMove(true);
}
break;
case MotionEvent.ACTION_MOVE:
//如果是單指移動
if(event.getPointerCount()==1) {
if (rightPoint.isCanMove()) {
x=event.getX(0)-rightCenter.x;
y=event.getY(0)-rightCenter.y;
//計算角度,atan2的用法自己百度咯
angle = (Math.atan2( x, y )*radToDeg)-90;
distance = Math.sqrt((x*x)+(y*y));
//判斷是不是超出了規定的半徑長度
if( distance >= stopRadius ) {
distance = stopRadius;
double radAngle = angle*degToRad;
rightPoint.x = (int) (distance*Math.cos(radAngle))+rightCenter.x;
rightPoint.y = (int) (-distance*Math.sin(radAngle))+rightCenter.y;
}
else
{
rightPoint.x= (int) event.getX(0);
rightPoint.y = (int)event.getY(0);
}
rightDistance=distance;
rightAngle=angle;
}
else if(leftPoint.isCanMove())
{
x=event.getX(0)-leftCenter.x;
y=event.getY(0)-leftCenter.y;
angle = (Math.atan2( x, y )*radToDeg)-90;
distance = Math.sqrt((x*x)+(y*y));
if( distance >= stopRadius ) {
distance = stopRadius;
double radAngle = angle*degToRad;
leftPoint.x = (int) (distance*Math.cos(radAngle))+leftCenter.x;
leftPoint.y = (int) (-distance*Math.sin(radAngle))+leftCenter.y;
}
else
{
leftPoint.x = (int) event.getX(0);
leftPoint.y = (int) event.getY(0);
}
}
}
else
{ //雙指移動
if (rightPoint.isCanMove() && rightPoint.getPointerIndex()!=-1 && rightPoint.getPointerIndex()<event.getPointerCount() ) {
x=event.getX(rightPoint.getPointerIndex())-rightCenter.x;
y=event.getY(rightPoint.getPointerIndex())-rightCenter.y;
angle = (Math.atan2( x, y )*radToDeg)-90;
distance = Math.sqrt((x*x)+(y*y));
if( distance >= stopRadius ) {
distance = stopRadius;
double radAngle = angle*degToRad;
rightPoint.x = (int) (distance*Math.cos(radAngle))+rightCenter.x;
rightPoint.y = (int) (-distance*Math.sin(radAngle))+rightCenter.y;
}
else
{
rightPoint.x = (int) event.getX(rightPoint.getPointerIndex());
rightPoint.y = (int) event.getY(rightPoint.getPointerIndex());
}
rightDistance=distance;
rightAngle=angle;
}
if(leftPoint.isCanMove() && leftPoint.getPointerIndex()!=-1 && leftPoint.getPointerIndex()<event.getPointerCount())
{
x2=event.getX(leftPoint.getPointerIndex())-leftCenter.x;
y2=event.getY(leftPoint.getPointerIndex())-leftCenter.y;
angle2 = (Math.atan2( x2, y2 )*radToDeg)-90;
distance2 = Math.sqrt((x2*x2)+(y2*y2));
if( distance2 >= stopRadius ) {
distance2 = stopRadius;
double radAngle = angle2*degToRad;
leftPoint.x = (int) (distance2*Math.cos(radAngle))+leftCenter.x;
leftPoint.y = (int) (-distance2*Math.sin(radAngle))+leftCenter.y;
}
else
{
leftPoint.x = (int) event.getX(leftPoint.getPointerIndex());
leftPoint.y = (int) event.getY(leftPoint.getPointerIndex());
}
leftDistance=distance2;
leftAngle=angle2;
}
}
break;
}
invalidate();
return true;
}
public double getRightDistance() {
return rightDistance;
}
public double getRightAngle() {
return rightAngle;
}
public double getLeftAngle() {
return leftAngle;
}
public double getLeftDistance() {
return leftDistance;
}
/**
* 自定義座標點,添加了兩個屬性
*/
public class MyPoint extends Point{
//手指的index
private int pointerIndex=-1;
//是否能移動
private boolean isCanMove;
public boolean isCanMove() {
return isCanMove;
}
public void setCanMove(boolean canMove) {
isCanMove = canMove;
}
public MyPoint(int x, int y) {
super(x, y);
}
public int getPointerIndex() {
return pointerIndex;
}
public void setPointerIndex(int pointerIndex) {
this.pointerIndex = pointerIndex;
}
}
}
在我的真正項目中對內存性能要求比較高,所有我並沒有直接用繼承ImageView,而是使用了SurfaceView,雙緩衝,單獨線程刷新畫面,還有局部刷新,基本上跟上面的差不多,只是將onDraw()裏面的刷新代碼放到SurfaceView中,網上也有很多SurfaceView使用的例子,稍微借鑑一下就能轉過來。各位有更好的寫法,可以告知,謝謝,轉發請標明出自http://blog.csdn.net/tpyangqingyuan/article/details/51658180