以前接手的一个蓝牙项目 不说多,先上控制界面图
本文重点说的是图片左下方的控制摇杆原点的实现
先写摇杆控件
/**
* 摇杆控件
*/
public class RockerView extends View {
private static final String TAG = "RockerView";
private static final int DEFAULT_SIZE = 400;
private static final int DEFAULT_ROCKER_RADIUS = DEFAULT_SIZE / 8;
private Paint mAreaBackgroundPaint;
private Paint mRockerPaint;
private Point mRockerPosition;
private Point mCenterPoint;
private int mAreaRadius;
private int mRockerRadius;
private CallBackMode mCallBackMode = CallBackMode.CALL_BACK_MODE_MOVE;
private OnAngleChangeListener mOnAngleChangeListener;
private OnShakeListener mOnShakeListener;
private DirectionMode mDirectionMode;
private Direction tempDirection = Direction.DIRECTION_CENTER;
// 角度
private static final double ANGLE_0 = 0;
private static final double ANGLE_360 = 360;
// 360°水平方向平分2份的边缘角度
private static final double ANGLE_HORIZONTAL_2D_OF_0P = 90;
private static final double ANGLE_HORIZONTAL_2D_OF_1P = 270;
// 360°垂直方向平分2份的边缘角度
private static final double ANGLE_VERTICAL_2D_OF_0P = 0;
private static final double ANGLE_VERTICAL_2D_OF_1P = 180;
// 360°平分4份的边缘角度
private static final double ANGLE_4D_OF_0P = 0;
private static final double ANGLE_4D_OF_1P = 90;
private static final double ANGLE_4D_OF_2P = 180;
private static final double ANGLE_4D_OF_3P = 270;
// 360°平分4份的边缘角度(旋转45度)
private static final double ANGLE_ROTATE45_4D_OF_0P = 45;
private static final double ANGLE_ROTATE45_4D_OF_1P = 135;
private static final double ANGLE_ROTATE45_4D_OF_2P = 225;
private static final double ANGLE_ROTATE45_4D_OF_3P = 315;
// 360°平分8份的边缘角度
private static final double ANGLE_8D_OF_0P = 22.5;
private static final double ANGLE_8D_OF_1P = 67.5;
private static final double ANGLE_8D_OF_2P = 112.5;
private static final double ANGLE_8D_OF_3P = 157.5;
private static final double ANGLE_8D_OF_4P = 202.5;
private static final double ANGLE_8D_OF_5P = 247.5;
private static final double ANGLE_8D_OF_6P = 292.5;
private static final double ANGLE_8D_OF_7P = 337.5;
// 摇杆可移动区域背景
private static final int AREA_BACKGROUND_MODE_PIC = 0;
private static final int AREA_BACKGROUND_MODE_COLOR = 1;
private static final int AREA_BACKGROUND_MODE_XML = 2;
private static final int AREA_BACKGROUND_MODE_DEFAULT = 3;
private int mAreaBackgroundMode = AREA_BACKGROUND_MODE_DEFAULT;
private Bitmap mAreaBitmap;
private int mAreaColor;
// 摇杆背景
private static final int ROCKER_BACKGROUND_MODE_PIC = 4;
private static final int ROCKER_BACKGROUND_MODE_COLOR = 5;
private static final int ROCKER_BACKGROUND_MODE_XML = 6;
private static final int ROCKER_BACKGROUND_MODE_DEFAULT = 7;
private int mRockerBackgroundMode = ROCKER_BACKGROUND_MODE_DEFAULT;
private Bitmap mRockerBitmap;
private int mRockerColor;
public RockerView(Context context, AttributeSet attrs) {
super(context, attrs);
// 获取自定义属性
initAttribute(context, attrs);
if (isInEditMode()) {
Logger.i(TAG, "RockerView: isInEditMode");
}
// 移动区域画笔
mAreaBackgroundPaint = new Paint();
mAreaBackgroundPaint.setAntiAlias(true);
// 摇杆画笔
mRockerPaint = new Paint();
mRockerPaint.setAntiAlias(true);
// 中心点
mCenterPoint = new Point();
// 摇杆位置
mRockerPosition = new Point();
}
/**
* 获取属性
*
* @param context context
* @param attrs attrs
*/
private void initAttribute(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RockerView);
// 可移动区域背景
Drawable areaBackground = typedArray.getDrawable(R.styleable.RockerView_areaBackground);
if (null != areaBackground) {
// 设置了背景
if (areaBackground instanceof BitmapDrawable) {
// 设置了一张图片
mAreaBitmap = ((BitmapDrawable) areaBackground).getBitmap();
mAreaBackgroundMode = AREA_BACKGROUND_MODE_PIC;
} else if (areaBackground instanceof GradientDrawable) {
// XML
mAreaBitmap = drawable2Bitmap(areaBackground);
mAreaBackgroundMode = AREA_BACKGROUND_MODE_XML;
} else if (areaBackground instanceof ColorDrawable) {
// 色值
mAreaColor = ((ColorDrawable) areaBackground).getColor();
mAreaBackgroundMode = AREA_BACKGROUND_MODE_COLOR;
} else {
// 其他形式
mAreaBackgroundMode = AREA_BACKGROUND_MODE_DEFAULT;
}
} else {
// 没有设置背景
mAreaBackgroundMode = AREA_BACKGROUND_MODE_DEFAULT;
}
// 摇杆背景
Drawable rockerBackground = typedArray.getDrawable(R.styleable.RockerView_rockerBackground);
if (null != rockerBackground) {
// 设置了摇杆背景
if (rockerBackground instanceof BitmapDrawable) {
// 图片
mRockerBitmap = ((BitmapDrawable) rockerBackground).getBitmap();
mRockerBackgroundMode = ROCKER_BACKGROUND_MODE_PIC;
} else if (rockerBackground instanceof GradientDrawable) {
// XML
mRockerBitmap = drawable2Bitmap(rockerBackground);
mRockerBackgroundMode = ROCKER_BACKGROUND_MODE_XML;
} else if (rockerBackground instanceof ColorDrawable) {
// 色值
mRockerColor = ((ColorDrawable) rockerBackground).getColor();
mRockerBackgroundMode = ROCKER_BACKGROUND_MODE_COLOR;
} else {
// 其他形式
mRockerBackgroundMode = ROCKER_BACKGROUND_MODE_DEFAULT;
}
} else {
// 没有设置摇杆背景
mRockerBackgroundMode = ROCKER_BACKGROUND_MODE_DEFAULT;
}
// 摇杆半径
mRockerRadius = typedArray.getDimensionPixelOffset(R.styleable.RockerView_rockerRadius, DEFAULT_ROCKER_RADIUS);
Logger.i(TAG, "initAttribute: mAreaBackground = " + areaBackground + " mRockerBackground = " + rockerBackground + " mRockerRadius = " + mRockerRadius);
typedArray.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measureWidth, measureHeight;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
// 具体的值和match_parent
measureWidth = widthSize;
} else {
// wrap_content
measureWidth = DEFAULT_SIZE;
}
if (heightMode == MeasureSpec.EXACTLY) {
measureHeight = heightSize;
} else {
measureHeight = DEFAULT_SIZE;
}
Logger.i(TAG, "onMeasure: --------------------------------------");
Logger.i(TAG, "onMeasure: widthMeasureSpec = " + widthMeasureSpec + " heightMeasureSpec = " + heightMeasureSpec);
Logger.i(TAG, "onMeasure: widthMode = " + widthMode + " measureWidth = " + widthSize);
Logger.i(TAG, "onMeasure: heightMode = " + heightMode + " measureHeight = " + widthSize);
Logger.i(TAG, "onMeasure: measureWidth = " + measureWidth + " measureHeight = " + measureHeight);
setMeasuredDimension(measureWidth, measureHeight);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int measuredWidth = getMeasuredWidth();
int measuredHeight = getMeasuredHeight();
int cx = measuredWidth / 2;
int cy = measuredHeight / 2;
// 中心点
mCenterPoint.set(cx, cy);
// 可移动区域的半径
mAreaRadius = (measuredWidth <= measuredHeight) ? cx : cy;
// 摇杆位置
if (0 == mRockerPosition.x || 0 == mRockerPosition.y) {
mRockerPosition.set(mCenterPoint.x, mCenterPoint.y);
}
// 画可移动区域
if (AREA_BACKGROUND_MODE_PIC == mAreaBackgroundMode || AREA_BACKGROUND_MODE_XML == mAreaBackgroundMode) {
// 图片
Rect src = new Rect(0, 0, mAreaBitmap.getWidth(), mAreaBitmap.getHeight());
Rect dst = new Rect(mCenterPoint.x - mAreaRadius, mCenterPoint.y - mAreaRadius, mCenterPoint.x + mAreaRadius, mCenterPoint.y + mAreaRadius);
canvas.drawBitmap(mAreaBitmap, src, dst, mAreaBackgroundPaint);
} else if (AREA_BACKGROUND_MODE_COLOR == mAreaBackgroundMode) {
// 色值
mAreaBackgroundPaint.setColor(mAreaColor);
canvas.drawCircle(mCenterPoint.x, mCenterPoint.y, mAreaRadius, mAreaBackgroundPaint);
} else {
// 其他或者未设置
mAreaBackgroundPaint.setColor(Color.GRAY);
canvas.drawCircle(mCenterPoint.x, mCenterPoint.y, mAreaRadius, mAreaBackgroundPaint);
}
// 画摇杆
if (ROCKER_BACKGROUND_MODE_PIC == mRockerBackgroundMode || ROCKER_BACKGROUND_MODE_XML == mRockerBackgroundMode) {
// 图片
Rect src = new Rect(0, 0, mRockerBitmap.getWidth(), mRockerBitmap.getHeight());
Rect dst = new Rect(mRockerPosition.x - mRockerRadius, mRockerPosition.y - mRockerRadius, mRockerPosition.x + mRockerRadius, mRockerPosition.y + mRockerRadius);
canvas.drawBitmap(mRockerBitmap, src, dst, mRockerPaint);
} else if (ROCKER_BACKGROUND_MODE_COLOR == mRockerBackgroundMode) {
// 色值
mRockerPaint.setColor(mRockerColor);
canvas.drawCircle(mRockerPosition.x, mRockerPosition.y, mRockerRadius, mRockerPaint);
} else {
// 其他或者未设置
mRockerPaint.setColor(Color.RED);
canvas.drawCircle(mRockerPosition.x, mRockerPosition.y, mRockerRadius, mRockerPaint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:// 按下
// 回调 开始
callBackStart();
case MotionEvent.ACTION_MOVE:// 移动
float moveX = event.getX();
float moveY = event.getY();
mRockerPosition = getRockerPositionPoint(mCenterPoint, new Point((int) moveX, (int) moveY), mAreaRadius, mRockerRadius);
moveRocker(mRockerPosition.x, mRockerPosition.y);
break;
case MotionEvent.ACTION_UP:// 抬起
case MotionEvent.ACTION_CANCEL:// 移出区域
// 回调 结束
callBackFinish();
float upX = event.getX();
float upY = event.getY();
moveRocker(mCenterPoint.x, mCenterPoint.y);
Logger.i(TAG, "onTouchEvent: 抬起位置 : x = " + upX + " y = " + upY);
break;
}
return true;
}
/**
* 获取摇杆实际要显示的位置(点)
*
* @param centerPoint 中心点
* @param touchPoint 触摸点
* @param regionRadius 摇杆可活动区域半径
* @param rockerRadius 摇杆半径
* @return 摇杆实际显示的位置(点)
*/
private Point getRockerPositionPoint(Point centerPoint, Point touchPoint, float regionRadius, float rockerRadius) {
// 两点在X轴的距离
float lenX = (float) (touchPoint.x - centerPoint.x);
// 两点在Y轴距离
float lenY = (float) (touchPoint.y - centerPoint.y);
// 两点距离
float lenXY = (float) Math.sqrt((double) (lenX * lenX + lenY * lenY));
// 计算弧度
double radian = Math.acos(lenX / lenXY) * (touchPoint.y < centerPoint.y ? -1 : 1);
// 计算角度
double angle = radian2Angle(radian);
// 回调 返回参数
callBack(angle);
Logger.i(TAG, "getRockerPositionPoint: 角度 :" + angle);
if (lenXY + rockerRadius <= regionRadius) { // 触摸位置在可活动范围内
return touchPoint;
} else { // 触摸位置在可活动范围以外
// 计算要显示的位置
int showPointX = (int) (centerPoint.x + (regionRadius - rockerRadius) * Math.cos(radian));
int showPointY = (int) (centerPoint.y + (regionRadius - rockerRadius) * Math.sin(radian));
return new Point(showPointX, showPointY);
}
}
/**
* 移动摇杆到指定位置
*
* @param x x坐标
* @param y y坐标
*/
private void moveRocker(float x, float y) {
mRockerPosition.set((int) x, (int) y);
Logger.i(TAG, "onTouchEvent: 移动位置 : x = " + mRockerPosition.x + " y = " + mRockerPosition.y);
invalidate();
}
/**
* 弧度转角度
*
* @param radian 弧度
* @return 角度[0, 360)
*/
private double radian2Angle(double radian) {
double tmp = Math.round(radian / Math.PI * 180);
return tmp >= 0 ? tmp : 360 + tmp;
}
/**
* Drawable 转 Bitmap
*
* @param drawable Drawable
* @return Bitmap
*/
private Bitmap drawable2Bitmap(Drawable drawable) {
// 取 drawable 的长宽
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
// 取 drawable 的颜色格式
Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
// 建立对应 bitmap
Bitmap bitmap = Bitmap.createBitmap(width, height, config);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, width, height);
drawable.draw(canvas);
return bitmap;
}
/**
* 回调
* 开始
*/
private void callBackStart() {
tempDirection = Direction.DIRECTION_CENTER;
if (null != mOnAngleChangeListener) {
mOnAngleChangeListener.onStart();
}
if (null != mOnShakeListener) {
mOnShakeListener.onStart();
}
}
/**
* 回调
* 返回参数
*
* @param angle 摇动角度
*/
private void callBack(double angle) {
if (null != mOnAngleChangeListener) {
mOnAngleChangeListener.angle(angle);
}
if (null != mOnShakeListener) {
if (CallBackMode.CALL_BACK_MODE_MOVE == mCallBackMode) {
switch (mDirectionMode) {
case DIRECTION_2_HORIZONTAL:// 左右方向
if (ANGLE_0 <= angle && ANGLE_HORIZONTAL_2D_OF_0P > angle || ANGLE_HORIZONTAL_2D_OF_1P <= angle && ANGLE_360 > angle) {
// 右
mOnShakeListener.direction(Direction.DIRECTION_RIGHT);
} else if (ANGLE_HORIZONTAL_2D_OF_0P <= angle && ANGLE_HORIZONTAL_2D_OF_1P > angle) {
// 左
mOnShakeListener.direction(Direction.DIRECTION_LEFT);
}
break;
case DIRECTION_2_VERTICAL:// 上下方向
if (ANGLE_VERTICAL_2D_OF_0P <= angle && ANGLE_VERTICAL_2D_OF_1P > angle) {
// 下
mOnShakeListener.direction(Direction.DIRECTION_DOWN);
} else if (ANGLE_VERTICAL_2D_OF_1P <= angle && ANGLE_360 > angle) {
// 上
mOnShakeListener.direction(Direction.DIRECTION_UP);
}
break;
case DIRECTION_4_ROTATE_0:// 四个方向
if (ANGLE_4D_OF_0P <= angle && ANGLE_4D_OF_1P > angle) {
// 右下
mOnShakeListener.direction(Direction.DIRECTION_DOWN_RIGHT);
} else if (ANGLE_4D_OF_1P <= angle && ANGLE_4D_OF_2P > angle) {
// 左下
mOnShakeListener.direction(Direction.DIRECTION_DOWN_LEFT);
} else if (ANGLE_4D_OF_2P <= angle && ANGLE_4D_OF_3P > angle) {
// 左上
mOnShakeListener.direction(Direction.DIRECTION_UP_LEFT);
} else if (ANGLE_4D_OF_3P <= angle && ANGLE_360 > angle) {
// 右上
mOnShakeListener.direction(Direction.DIRECTION_UP_RIGHT);
}
break;
case DIRECTION_4_ROTATE_45:// 四个方向 旋转45度
if (ANGLE_0 <= angle && ANGLE_ROTATE45_4D_OF_0P > angle || ANGLE_ROTATE45_4D_OF_3P <= angle && ANGLE_360 > angle) {
// 右
mOnShakeListener.direction(Direction.DIRECTION_RIGHT);
} else if (ANGLE_ROTATE45_4D_OF_0P <= angle && ANGLE_ROTATE45_4D_OF_1P > angle) {
// 下
mOnShakeListener.direction(Direction.DIRECTION_DOWN);
} else if (ANGLE_ROTATE45_4D_OF_1P <= angle && ANGLE_ROTATE45_4D_OF_2P > angle) {
// 左
mOnShakeListener.direction(Direction.DIRECTION_LEFT);
} else if (ANGLE_ROTATE45_4D_OF_2P <= angle && ANGLE_ROTATE45_4D_OF_3P > angle) {
// 上
mOnShakeListener.direction(Direction.DIRECTION_UP);
}
break;
case DIRECTION_8:// 八个方向
if (ANGLE_0 <= angle && ANGLE_8D_OF_0P > angle || ANGLE_8D_OF_7P <= angle && ANGLE_360 > angle) {
// 右
mOnShakeListener.direction(Direction.DIRECTION_RIGHT);
} else if (ANGLE_8D_OF_0P <= angle && ANGLE_8D_OF_1P > angle) {
// 右下
mOnShakeListener.direction(Direction.DIRECTION_DOWN_RIGHT);
} else if (ANGLE_8D_OF_1P <= angle && ANGLE_8D_OF_2P > angle) {
// 下
mOnShakeListener.direction(Direction.DIRECTION_DOWN);
} else if (ANGLE_8D_OF_2P <= angle && ANGLE_8D_OF_3P > angle) {
// 左下
mOnShakeListener.direction(Direction.DIRECTION_DOWN_LEFT);
} else if (ANGLE_8D_OF_3P <= angle && ANGLE_8D_OF_4P > angle) {
// 左
mOnShakeListener.direction(Direction.DIRECTION_LEFT);
} else if (ANGLE_8D_OF_4P <= angle && ANGLE_8D_OF_5P > angle) {
// 左上
mOnShakeListener.direction(Direction.DIRECTION_UP_LEFT);
} else if (ANGLE_8D_OF_5P <= angle && ANGLE_8D_OF_6P > angle) {
// 上
mOnShakeListener.direction(Direction.DIRECTION_UP);
} else if (ANGLE_8D_OF_6P <= angle && ANGLE_8D_OF_7P > angle) {
// 右上
mOnShakeListener.direction(Direction.DIRECTION_UP_RIGHT);
}
break;
default:
break;
}
} else if (CallBackMode.CALL_BACK_MODE_STATE_CHANGE == mCallBackMode) {
switch (mDirectionMode) {
case DIRECTION_2_HORIZONTAL:// 左右方向
if ((ANGLE_0 <= angle && ANGLE_HORIZONTAL_2D_OF_0P > angle || ANGLE_HORIZONTAL_2D_OF_1P <= angle && ANGLE_360 > angle) && tempDirection != Direction.DIRECTION_RIGHT) {
// 右
tempDirection = Direction.DIRECTION_RIGHT;
mOnShakeListener.direction(Direction.DIRECTION_RIGHT);
} else if (ANGLE_HORIZONTAL_2D_OF_0P <= angle && ANGLE_HORIZONTAL_2D_OF_1P > angle && tempDirection != Direction.DIRECTION_LEFT) {
// 左
tempDirection = Direction.DIRECTION_LEFT;
mOnShakeListener.direction(Direction.DIRECTION_LEFT);
}
break;
case DIRECTION_2_VERTICAL:// 上下方向
if (ANGLE_VERTICAL_2D_OF_0P <= angle && ANGLE_VERTICAL_2D_OF_1P > angle && tempDirection != Direction.DIRECTION_DOWN) {
// 下
tempDirection = Direction.DIRECTION_DOWN;
mOnShakeListener.direction(Direction.DIRECTION_DOWN);
} else if (ANGLE_VERTICAL_2D_OF_1P <= angle && ANGLE_360 > angle && tempDirection != Direction.DIRECTION_UP) {
// 上
tempDirection = Direction.DIRECTION_UP;
mOnShakeListener.direction(Direction.DIRECTION_UP);
}
break;
case DIRECTION_4_ROTATE_0:// 四个方向
if (ANGLE_4D_OF_0P <= angle && ANGLE_4D_OF_1P > angle && tempDirection != Direction.DIRECTION_DOWN_RIGHT) {
// 右下
tempDirection = Direction.DIRECTION_DOWN_RIGHT;
mOnShakeListener.direction(Direction.DIRECTION_DOWN_RIGHT);
} else if (ANGLE_4D_OF_1P <= angle && ANGLE_4D_OF_2P > angle && tempDirection != Direction.DIRECTION_DOWN_LEFT) {
// 左下
tempDirection = Direction.DIRECTION_DOWN_LEFT;
mOnShakeListener.direction(Direction.DIRECTION_DOWN_LEFT);
} else if (ANGLE_4D_OF_2P <= angle && ANGLE_4D_OF_3P > angle && tempDirection != Direction.DIRECTION_UP_LEFT) {
// 左上
tempDirection = Direction.DIRECTION_UP_LEFT;
mOnShakeListener.direction(Direction.DIRECTION_UP_LEFT);
} else if (ANGLE_4D_OF_3P <= angle && ANGLE_360 > angle && tempDirection != Direction.DIRECTION_UP_RIGHT) {
// 右上
tempDirection = Direction.DIRECTION_UP_RIGHT;
mOnShakeListener.direction(Direction.DIRECTION_UP_RIGHT);
}
break;
case DIRECTION_4_ROTATE_45:// 四个方向 旋转45度
if ((ANGLE_0 <= angle && ANGLE_ROTATE45_4D_OF_0P > angle || ANGLE_ROTATE45_4D_OF_3P <= angle && ANGLE_360 > angle) && tempDirection != Direction.DIRECTION_RIGHT) {
// 右
tempDirection = Direction.DIRECTION_RIGHT;
mOnShakeListener.direction(Direction.DIRECTION_RIGHT);
} else if (ANGLE_ROTATE45_4D_OF_0P <= angle && ANGLE_ROTATE45_4D_OF_1P > angle && tempDirection != Direction.DIRECTION_DOWN) {
// 下
tempDirection = Direction.DIRECTION_DOWN;
mOnShakeListener.direction(Direction.DIRECTION_DOWN);
} else if (ANGLE_ROTATE45_4D_OF_1P <= angle && ANGLE_ROTATE45_4D_OF_2P > angle && tempDirection != Direction.DIRECTION_LEFT) {
// 左
tempDirection = Direction.DIRECTION_LEFT;
mOnShakeListener.direction(Direction.DIRECTION_LEFT);
} else if (ANGLE_ROTATE45_4D_OF_2P <= angle && ANGLE_ROTATE45_4D_OF_3P > angle && tempDirection != Direction.DIRECTION_UP) {
// 上
tempDirection = Direction.DIRECTION_UP;
mOnShakeListener.direction(Direction.DIRECTION_UP);
}
break;
case DIRECTION_8:// 八个方向
if ((ANGLE_0 <= angle && ANGLE_8D_OF_0P > angle || ANGLE_8D_OF_7P <= angle && ANGLE_360 > angle) && tempDirection != Direction.DIRECTION_RIGHT) {
// 右
tempDirection = Direction.DIRECTION_RIGHT;
mOnShakeListener.direction(Direction.DIRECTION_RIGHT);
} else if (ANGLE_8D_OF_0P <= angle && ANGLE_8D_OF_1P > angle && tempDirection != Direction.DIRECTION_DOWN_RIGHT) {
// 右下
tempDirection = Direction.DIRECTION_DOWN_RIGHT;
mOnShakeListener.direction(Direction.DIRECTION_DOWN_RIGHT);
} else if (ANGLE_8D_OF_1P <= angle && ANGLE_8D_OF_2P > angle && tempDirection != Direction.DIRECTION_DOWN) {
// 下
tempDirection = Direction.DIRECTION_DOWN;
mOnShakeListener.direction(Direction.DIRECTION_DOWN);
} else if (ANGLE_8D_OF_2P <= angle && ANGLE_8D_OF_3P > angle && tempDirection != Direction.DIRECTION_DOWN_LEFT) {
// 左下
tempDirection = Direction.DIRECTION_DOWN_LEFT;
mOnShakeListener.direction(Direction.DIRECTION_DOWN_LEFT);
} else if (ANGLE_8D_OF_3P <= angle && ANGLE_8D_OF_4P > angle && tempDirection != Direction.DIRECTION_LEFT) {
// 左
tempDirection = Direction.DIRECTION_LEFT;
mOnShakeListener.direction(Direction.DIRECTION_LEFT);
} else if (ANGLE_8D_OF_4P <= angle && ANGLE_8D_OF_5P > angle && tempDirection != Direction.DIRECTION_UP_LEFT) {
// 左上
tempDirection = Direction.DIRECTION_UP_LEFT;
mOnShakeListener.direction(Direction.DIRECTION_UP_LEFT);
} else if (ANGLE_8D_OF_5P <= angle && ANGLE_8D_OF_6P > angle && tempDirection != Direction.DIRECTION_UP) {
// 上
tempDirection = Direction.DIRECTION_UP;
mOnShakeListener.direction(Direction.DIRECTION_UP);
} else if (ANGLE_8D_OF_6P <= angle && ANGLE_8D_OF_7P > angle && tempDirection != Direction.DIRECTION_UP_RIGHT) {
// 右上
tempDirection = Direction.DIRECTION_UP_RIGHT;
mOnShakeListener.direction(Direction.DIRECTION_UP_RIGHT);
}
break;
default:
break;
}
}
}
}
/**
* 回调
* 结束
*/
private void callBackFinish() {
tempDirection = Direction.DIRECTION_CENTER;
if (null != mOnAngleChangeListener) {
mOnAngleChangeListener.onFinish();
}
if (null != mOnShakeListener) {
mOnShakeListener.onFinish();
}
}
/**
* 回调模式
*/
public enum CallBackMode {
// 有移动就立刻回调
CALL_BACK_MODE_MOVE,
// 只有状态变化的时候才回调
CALL_BACK_MODE_STATE_CHANGE
}
/**
* 设置回调模式
*
* @param mode 回调模式
*/
public void setCallBackMode(CallBackMode mode) {
mCallBackMode = mode;
}
/**
* 摇杆支持几个方向
*/
public enum DirectionMode {
DIRECTION_2_HORIZONTAL,// 横向 左右两个方向
DIRECTION_2_VERTICAL, // 纵向 上下两个方向
DIRECTION_4_ROTATE_0, // 四个方向
DIRECTION_4_ROTATE_45, // 四个方向 旋转45度
DIRECTION_8 // 八个方向
}
/**
* 方向
*/
public enum Direction {
DIRECTION_LEFT, // 左
DIRECTION_RIGHT, // 右
DIRECTION_UP, // 上
DIRECTION_DOWN, // 下
DIRECTION_UP_LEFT, // 左上
DIRECTION_UP_RIGHT, // 右上
DIRECTION_DOWN_LEFT, // 左下
DIRECTION_DOWN_RIGHT, // 右下
DIRECTION_CENTER // 中间
}
/**
* 添加摇杆摇动角度的监听
*
* @param listener 回调接口
*/
public void setOnAngleChangeListener(OnAngleChangeListener listener) {
mOnAngleChangeListener = listener;
}
/**
* 添加摇动的监听
*
* @param directionMode 监听的方向
* @param listener 回调
*/
public void setOnShakeListener(DirectionMode directionMode, OnShakeListener listener) {
mDirectionMode = directionMode;
mOnShakeListener = listener;
}
/**
* 摇动方向监听接口
*/
public interface OnShakeListener {
// 开始
void onStart();
/**
* 摇动方向
*
* @param direction 方向
*/
void direction(Direction direction);
// 结束
void onFinish();
}
/**
* 摇动角度的监听接口
*/
public interface OnAngleChangeListener {
// 开始
void onStart();
/**
* 摇杆角度变化
*
* @param angle 角度[0,360)
*/
void angle(double angle);
// 结束
void onFinish();
}
}
继续复写RocekerView 从中提取出距离值
/**
* 复写RocekerView 从中提取出距离值
*/
public class NewRocekerView extends RockerView {
//中心点
private Point mCenterPoint;
//距离值
private float lenXY;
private NewRocekerView.OnAngleChangeListener mOnAngleChangeListener;
public NewRocekerView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mCenterPoint = new Point();
}
@Override
protected void onDraw(Canvas canvas) {
int measuredWidth = this.getMeasuredWidth();
int measuredHeight = this.getMeasuredHeight();
int cx = measuredWidth / 2;
int cy = measuredHeight / 2;
this.mCenterPoint.set(cx, cy);
super.onDraw(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case 2:
float moveX = event.getX();
float moveY = event.getY();
Point touchPoint = new Point((int) moveX, (int) moveY);
float lenX = (float) (touchPoint.x - mCenterPoint.x);
float lenY = (float) (touchPoint.y - mCenterPoint.y);
lenXY = (float) Math.sqrt((double) (lenX * lenX + lenY * lenY));
break;
}
return super.onTouchEvent(event);
}
/**
* 获取距离值
* @return
*/
public float getSpeedParmParameter(){
return lenXY;
}
}
再然后,撸上自己的xml
<com.niubidashen.ui.NewRocekerView
android:id="@+id/circle_view"
android:layout_width="@dimen/ws120dp"
android:layout_height="@dimen/ws110dp"
kongqw:areaBackground="@mipmap/icon_circle_backgroup"
kongqw:rockerBackground="@mipmap/icon_circle_img"
kongqw:rockerRadius="30dp" />
下面直接贴上java代码 删去蓝牙部分
/**
* 运动控制
*/
public class MachineRunActivity extends BaseActivity implements BlueToothManager.BlueToothManagerCallBack {
@BindView(R.id.circle_view)
NewRocekerView circleView;
//小车速度
private int speed = 0;
//蓝牙发送延迟时间
private int delayTime = 10;
private long sendTime = 50;
//蓝牙接口回调
private BlueToothManager.BlueToothManagerCallBack blueToothManagerCallBack;
private BlueToothPresenter blueToothPresenter;
//是否是手动模式
private boolean isManualMode = false;
//防止多次发送
private static long lastSendTime = 0;
private SensorManager sensorManager;
private Sensor sensor = null;
//重力感应开关
private boolean sensorFlag = false;
private mySensorEventListener sensorEventListener = new mySensorEventListener();
@Override
protected int getContentViewId() {
return R.layout.activity_machine_run;
}
@SuppressLint("HandlerLeak")
Handler handler = new Handler() {
public void handleMessage(Message msg) {
// 要做的事情
super.handleMessage(msg);
long curClickTime = System.currentTimeMillis();
if ((curClickTime - lastSendTime) >= sendTime) {
switch (msg.what) {
case 1://上
blueToothPresenter.carMoveControl(0, speed, MachineRunActivity.this, blueToothManagerCallBack);
break;
case 2://下
blueToothPresenter.carMoveControl(128, speed, MachineRunActivity.this, blueToothManagerCallBack);
break;
case 3://右
blueToothPresenter.carMoveControl(64, speed, MachineRunActivity.this, blueToothManagerCallBack);
break;
case 4://左
blueToothPresenter.carMoveControl(192, speed, MachineRunActivity.this, blueToothManagerCallBack);
break;
case 5://左上
blueToothPresenter.carMoveControl(224, speed, MachineRunActivity.this, blueToothManagerCallBack);
break;
case 6://右上
blueToothPresenter.carMoveControl(96, speed, MachineRunActivity.this, blueToothManagerCallBack);
break;
case 7://左下
blueToothPresenter.carMoveControl(160, speed, MachineRunActivity.this, blueToothManagerCallBack);
break;
case 8://右下
blueToothPresenter.carMoveControl(96, speed, MachineRunActivity.this, blueToothManagerCallBack);
break;
}
lastSendTime = curClickTime;
}
Log.e("MyThread1", "run: " + System.currentTimeMillis() + "");
}
};
ScheduledExecutorService scheduledThreadPool = scheduledThreadPool = Executors.newScheduledThreadPool(1);
;
@Override
protected void init() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("onRefresh");
// 监听摇动方向
circleView.setOnAngleChangeListener(new NewRocekerView.OnAngleChangeListener() {
@Override
public void onStart() {
// mLogRight.setText(null);
}
@Override
public void angle(double angle) {
circleView.getSpeedParmParameter();
// // mLogRight.setText("摇动角度 : " + angle);
// Log.e("angle", (angle * 256 / 360) + "speed===="+circleView.getSpeedParmParameter());
// MachineRunActivity.this.speed = (int) (angle * 256 / 360);
MachineRunActivity.this.speed = speedCalculateHand(circleView.getSpeedParmParameter());
Log.e("angle", "speed====" + MachineRunActivity.this.speed);
}
@Override
public void onFinish() {
//mLogRight.setText(null);
}
});
circleView.setOnShakeListener(RockerView.DirectionMode.DIRECTION_4_ROTATE_45, new RockerView.OnShakeListener() {
@Override
public void onStart() {
//mLogLeft.setText(null);
}
@Override
public void direction(RockerView.Direction direction) {
//mLogLeft.setText("摇动方向 : " + getDirection(direction));
//延迟2秒后执行该任务
// DIRECTION_LEFT 左
// DIRECTION_RIGHT 右
// DIRECTION_UP 上
// DIRECTION_DOWN 下
// DIRECTION_UP_LEFT 左上
// DIRECTION_UP_RIGHT 右上
// DIRECTION_DOWN_LEFT 左下
// DIRECTION_DOWN_RIGHT 右下
// DIRECTION_CENTER 中间
Log.e("direction", "direction: " + direction);
//是否处于手动模式
if (isManualMode == false)
Toast.makeText(MachineRunActivity.this, "请选择手动模式", Toast.LENGTH_LONG);
else {
switch (direction) {
case DIRECTION_LEFT://左
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
message(4);
}
}, delayTime, TimeUnit.MILLISECONDS);
break;
case DIRECTION_RIGHT://右
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
message(3);
}
}, delayTime, TimeUnit.MILLISECONDS);
break;
case DIRECTION_UP://上
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
message(1);
}
}, delayTime, TimeUnit.MILLISECONDS);
break;
case DIRECTION_DOWN://下
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
message(2);
}
}, delayTime, TimeUnit.MILLISECONDS);
break;
case DIRECTION_UP_LEFT: //左上
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
message(5);
}
}, delayTime, TimeUnit.MILLISECONDS);
break;
case DIRECTION_UP_RIGHT: //右上
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
message(6);
}
}, delayTime, TimeUnit.MILLISECONDS);
break;
case DIRECTION_DOWN_LEFT: //左下
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
message(7);
}
}, delayTime, TimeUnit.MILLISECONDS);
break;
case DIRECTION_DOWN_RIGHT: //右下
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
message(8);
}
}, delayTime, TimeUnit.MILLISECONDS);
break;
}
}
}
@Override
public void onFinish() {
//mLogLeft.setText(null);
scheduledThreadPool.shutdown();
scheduledThreadPool = scheduledThreadPool = Executors.newScheduledThreadPool(1);
}
});
}
//发送handler消息
private void message(int what) {
Message message = new Message();
message.what = what;
handler.sendMessage(message);// 发送消息
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// TODO: add setContentView(...) invocation
ButterKnife.bind(this);
blueToothManagerCallBack = this;
blueToothPresenter = new BlueToothPresenter();
sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
@Override
public void onWriteSuccess() {
}
@Override
public void onWriteFailure(BleException exception) {
}
private class mySensorEventListener implements SensorEventListener {
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onSensorChanged(SensorEvent event) {
float xValue = event.values[0];
float yValue = event.values[1];
float zValue = event.values[2];
//左
if (yValue < -3) {
speed = speedCalculate(xValue, yValue);
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
message(4);
}
}, delayTime, TimeUnit.MILLISECONDS);
}
//右
if (yValue > 3) {
speed = speedCalculate(xValue, yValue);
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
message(3);
}
}, delayTime, TimeUnit.MILLISECONDS);
}
//后退
if (xValue > 3) {
speed = speedCalculate(xValue, yValue);
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
message(2);
}
}, delayTime, TimeUnit.MILLISECONDS);
}
//前进
if (xValue < -3) {
speed = speedCalculate(xValue, yValue);
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
message(1);
}
}, delayTime, TimeUnit.MILLISECONDS);
}
//停止
if ((3 > yValue & yValue > -3) & (3 > xValue & xValue > -3)) {
}
Log.d("SensorEventListener", "X: " + xValue + " " + "Y: " + yValue + " " + "Z: " + zValue + " " + "speed=" + speed);
/*if (3 > xValue && xValue > -3) {
button_a.setPressed(false);
button_d.setPressed(false);
send("停止",STOP);
}*/
}
}
//计算速度 重力模式
private int speedCalculate(float x, float y) {
int speedCalculate = 0;
if (x >= 10) x = 10;
if (x <= -10) x = -10;
if (y >= 10) y = 10;
if (y <= -10) y = -10;
speedCalculate = (int) (Math.sqrt(x * x + y * y) / 10 * 255);
if (speedCalculate > 255) speedCalculate = 255;
return speedCalculate;
}
//计算速度 手动模式
private int speedCalculateHand(float xy) {
int speedCalculate = 0;
if (xy >= 120) xy = 120;
speedCalculate = (int) (xy / 120 * 255);
if (speedCalculate > 255) speedCalculate = 255;
return speedCalculate;
}
}
这里面还有传感器控制小车的代码,可给给各位参考一下