android 虚拟摇杆绘制

原创 2015年11月19日 11:39:24

首先附上效果图


1、自定义RockerView

package com.example.rocker;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnPreDrawListener;

public class RockerView extends View {

	//固定摇杆背景圆形的X,Y坐标以及半径
	private float mRockerBg_X;
	private float mRockerBg_Y;
	private float mRockerBg_R;
	//摇杆的X,Y坐标以及摇杆的半径
	private float mRockerBtn_X;
	private float mRockerBtn_Y;
	private float mRockerBtn_R;
	private Bitmap mBmpRockerBg;
	private Bitmap mBmpRockerBtn;
	
	private PointF mCenterPoint;
	
	public RockerView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
		// 获取bitmap
		mBmpRockerBg = BitmapFactory.decodeResource(context.getResources(), R.drawable.rocker_bg);
		mBmpRockerBtn = BitmapFactory.decodeResource(context.getResources(), R.drawable.rocker_btn);
		
		getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
			
			// 调用该方法时可以获取view实际的宽getWidth()和高getHeight()
			@Override
			public boolean onPreDraw() {
				// TODO Auto-generated method stub
				getViewTreeObserver().removeOnPreDrawListener(this); 
				
				Log.e("RockerView", getWidth() + "/" +  getHeight());
				mCenterPoint = new PointF(getWidth() / 2, getHeight() / 2);
				mRockerBg_X = mCenterPoint.x;
				mRockerBg_Y = mCenterPoint.y;
				
				mRockerBtn_X = mCenterPoint.x;
				mRockerBtn_Y = mCenterPoint.y;
				
				float tmp_f = mBmpRockerBg.getWidth() / (float)(mBmpRockerBg.getWidth() + mBmpRockerBtn.getWidth());
				mRockerBg_R = tmp_f * getWidth() / 2;
				mRockerBtn_R = (1.0f - tmp_f)* getWidth() / 2;
				
				return true;
			}
		});
	
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					
					//系统调用onDraw方法刷新画面
					RockerView.this.postInvalidate();
					
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}).start();
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		// TODO Auto-generated method stub
		super.onDraw(canvas);
		canvas.drawBitmap(mBmpRockerBg, null, 
				new Rect((int)(mRockerBg_X - mRockerBg_R), 
						(int)(mRockerBg_Y - mRockerBg_R), 
						(int)(mRockerBg_X + mRockerBg_R), 
						(int)(mRockerBg_Y + mRockerBg_R)), 
				null);
		canvas.drawBitmap(mBmpRockerBtn, null, 
				new Rect((int)(mRockerBtn_X - mRockerBtn_R), 
						(int)(mRockerBtn_Y - mRockerBtn_R), 
						(int)(mRockerBtn_X + mRockerBtn_R), 
						(int)(mRockerBtn_Y + mRockerBtn_R)), 
				null);
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		// TODO Auto-generated method stub
		if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) {
			// 当触屏区域不在活动范围内
			if (Math.sqrt(Math.pow((mRockerBg_X - (int) event.getX()), 2) + Math.pow((mRockerBg_Y - (int) event.getY()), 2)) >= mRockerBg_R) {
				//得到摇杆与触屏点所形成的角度
				double tempRad = getRad(mRockerBg_X, mRockerBg_Y, event.getX(), event.getY());
				//保证内部小圆运动的长度限制
				getXY(mRockerBg_X, mRockerBg_Y, mRockerBg_R, tempRad);
			} else {//如果小球中心点小于活动区域则随着用户触屏点移动即可
				mRockerBtn_X = (int) event.getX();
				mRockerBtn_Y = (int) event.getY();
			}
			if(mRockerChangeListener != null) {
				mRockerChangeListener.report(mRockerBtn_X - mCenterPoint.x, mRockerBtn_Y - mCenterPoint.y);
			}
		} else if (event.getAction() == MotionEvent.ACTION_UP) {
			//当释放按键时摇杆要恢复摇杆的位置为初始位置
			mRockerBtn_X = mCenterPoint.x;
			mRockerBtn_Y = mCenterPoint.y;
			if(mRockerChangeListener != null) {
				mRockerChangeListener.report(0, 0);
			}
		}
		return true;
	}
	
	/***
	 * 得到两点之间的弧度
	 */
	public double getRad(float px1, float py1, float px2, float py2) {
		//得到两点X的距离
		float x = px2 - px1;
		//得到两点Y的距离
		float y = py1 - py2;
		//算出斜边长
		float xie = (float) Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
		//得到这个角度的余弦值(通过三角函数中的定理 :邻边/斜边=角度余弦值)
		float cosAngle = x / xie;
		//通过反余弦定理获取到其角度的弧度
		float rad = (float) Math.acos(cosAngle);
		//注意:当触屏的位置Y坐标<摇杆的Y坐标我们要取反值-0~-180
		if (py2 < py1) {
			rad = -rad;
		}
		return rad;
	}
	
	/**
	 * 
	 * @param R  圆周运动的旋转点
	 * @param centerX 旋转点X
	 * @param centerY 旋转点Y
	 * @param rad 旋转的弧度
	 */
	public void getXY(float centerX, float centerY, float R, double rad) {
		//获取圆周运动的X坐标 
		mRockerBtn_X = (float) (R * Math.cos(rad)) + centerX;
		//获取圆周运动的Y坐标
		mRockerBtn_Y = (float) (R * Math.sin(rad)) + centerY;
	}
	
	RockerChangeListener mRockerChangeListener = null;
	public void setRockerChangeListener(RockerChangeListener rockerChangeListener) {
		mRockerChangeListener = rockerChangeListener;
	}
	public interface RockerChangeListener {
		public void report(float x, float y);
	}
}
2、布局文件中添加RockerView

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ff4f4f4f"
    tools:context=".MainActivity" >

    <com.example.rocker.RockerView 
        android:id="@+id/rockerView1"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:layout_alignParentBottom="true"
        android:layout_marginLeft="20dp"
        android:layout_marginBottom="20dp"/>
    
	<com.example.rocker.RockerView 
        android:id="@+id/rockerView2"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_marginRight="20dp"
        android:layout_marginBottom="20dp"/>

</RelativeLayout>


3、MainActiviy中使用RockerView

package com.example.rocker;

import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.widget.RelativeLayout;

public class MainActivity extends Activity {

	private static final String TAG = "MainActivity";

	void doLog(String log) {
		Log.e(TAG, log);
	}

	private RockerView rockerView1;
	private RockerView rockerView2;
	int screenWidth;
	int screenHeight;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().setFlags(
				WindowManager.LayoutParams.FLAG_FULLSCREEN
						| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
				WindowManager.LayoutParams.FLAG_FULLSCREEN
						| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);// 设置全屏
																			// ,
																			// 屏幕长亮
		setContentView(R.layout.activity_main);

		DisplayMetrics dm = getResources().getDisplayMetrics();
		screenWidth = dm.widthPixels;
		screenHeight = dm.heightPixels;

		rockerView1 = (RockerView) findViewById(R.id.rockerView1);
		rockerView2 = (RockerView) findViewById(R.id.rockerView2);

		rockerView1.setRockerChangeListener(new RockerView.RockerChangeListener() {

					@Override
					public void report(float x, float y) {
						// TODO Auto-generated method stub
						// doLog(x + "/" + y);
						setLayout(rockerView2, (int)x, (int)y);
					}
				});

		rockerView2.setRockerChangeListener(new RockerView.RockerChangeListener() {

					@Override
					public void report(float x, float y) {
						// TODO Auto-generated method stub
						// doLog(x + "/" + y);
						setLayout(rockerView1, (int)x, (int)y);
					}
				});
	}

	public void setLayout(View v, int dx, int dy) {
		int left = v.getLeft() + dx;
		int top = v.getTop() + dy;
		int right = v.getRight() + dx;
		int bottom = v.getBottom() + dy;
		if (left < 0) {
			left = 0;
			right = left + v.getWidth();
		}
		if (right > screenWidth) {
			right = screenWidth;
			left = right - v.getWidth();
		}
		if (top < 0) {
			top = 0;
			bottom = top + v.getHeight();
		}
		if (bottom > screenHeight) {
			bottom = screenHeight;
			top = bottom - v.getHeight();
		}
		v.layout(left, top, right, bottom);
	}
}

如下是代码下载地址

http://download.csdn.net/detail/qwjun/9282127



教你一步步实现一个虚拟摇杆

各位朋友,大家好,我是秦元培,欢迎大家关注我的博客,我的博客地址是http://qinyuanpei.com。最近因为项目需要决定尝试自己来实现一个虚拟摇杆,所以在今天的文章中我们的目标是使用uGUI...
  • qinyuanpei
  • qinyuanpei
  • 2015年10月30日 15:14
  • 10916

Android 虚拟摇杆,多种模式回调,返回距离级别,方向,角度。

Android 虚拟摇杆,多种模式回调,返回距离级别,方向,角度。
  • u014608640
  • u014608640
  • 2016年10月27日 18:16
  • 637

android 虚拟摇杆绘制

首先附上效果图 1、自定义RockerView package com.example.rocker; import android.content.Context; import andro...
  • qwjun
  • qwjun
  • 2015年11月19日 11:39
  • 3055

虚拟摇杆的surfaceView实现

  • 2017年09月06日 09:14
  • 4KB
  • 下载

安卓虚拟摇杆

Rocker安卓虚拟摇杆:由于需要制作一个控制小车移动的应用,使用按键控制不太舒服,故制作了一个虚拟摇杆。该摇杆原理十分简单,就是继承一个surfaceView,然后根据用户操作不断重绘界面,同时返回...
  • u013831257
  • u013831257
  • 2015年09月15日 19:43
  • 5071

android 开发之虚拟摇杆

  • 2015年11月19日 11:51
  • 2.42MB
  • 下载

Android自定义View系列(二)——打造一个仿2K游戏摇杆

写作原因:Android进阶过程中有一个绕不开的话题——自定义View。这一块是安卓程序员更好地实现功能自主化必须迈出的一步。下面这个系列博主将通过实现几个例子来认识安卓自定义View的方法。从自定义...
  • u011506583
  • u011506583
  • 2016年07月22日 00:22
  • 817

android游戏引擎andengine学习系列三:绘制游戏虚拟摇杆

如何高效的学习,这才是我们最值得去学习的。     andengine中绘制虚拟游戏摇杆非常简单,只需要实现AnalogOnScreenControl模拟摇杆类,在设置一些属性即可。先看效果图:...
  • duancanmeng
  • duancanmeng
  • 2012年02月14日 14:52
  • 9349

虚拟摇杆安卓源代码

  • 2013年08月24日 23:46
  • 44KB
  • 下载

Android自定义摇杆

转载请说明出处! 作者:kqw攻城狮 出处:个人站 | CSDN效果图源码KqwRockerDemo喜欢就给个star,谢谢!功能 支持自适应大小 支持2个方向、4个方向、8个方向的摇动监听 支持...
  • q4878802
  • q4878802
  • 2016年09月01日 18:47
  • 11697
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:android 虚拟摇杆绘制
举报原因:
原因补充:

(最多只允许输入30个字)