android 摇杆控件实现RockerView
Posted andylauren
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android 摇杆控件实现RockerView相关的知识,希望对你有一定的参考价值。
我是在下面两个源码的基础上进行的修改。
如果需要使用可以先看看下面两个代码是否能够满足要求如果可以满足优先使用下面两个。我改动是因为我需要一个方向和级别同时返回的回调而已,所以修改了一下。
RockerView: 虚拟摇杆实现方向: 两个方向、四个方向、八个方向(集成好,可选)实现距离: 自定义分级,摇杆剧中心的距离(XML可设置)实现触发方式:状态改变和移动
首先实现一个RockView类
package com.example.myesptest.RockView;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import com.example.myesptest.R;
public class RockerView extends View
private static final int DEFAULT_SIZE = 400;
private static final float DEFAULT_ROCKER_SCALE = 0.5f;//默认半径为背景的1/2
private final Paint mAreaBackgroundPaint;
private final Paint mRockerPaint;
private Point mRockerPosition;
private final Point mCenterPoint;
private int mAreaRadius;
private float mRockerScale;
private int mRockerRadius;
private CallBackMode mCallBackMode = CallBackMode.CALL_BACK_MODE_MOVE;
private OnAngleChangeListener mOnAngleChangeListener;
private OnShakeListener mOnShakeListener;
private OnDistanceLevelListener mOnDistanceLevelListener;
private OnShakeAngleDistanceLevelListener mOnShakeAngleDistanceLevelListener;
private DirectionMode mDirectionMode = DirectionMode.DIRECTION_8;
private Direction tempDirection = Direction.DIRECTION_CENTER;
private Direction temp1Direction = Direction.DIRECTION_CENTER;
private float last1Level = 0;
private double last1Angle = 0;
private float lastDistance = 0;
private double lastAngle = 0;
private float baseDistance = 0;
private int mDistanceLevel = 10;//分成10分
// 角度
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);
// 移动区域画笔
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;
// 摇杆半径
mRockerScale = typedArray.getFloat(R.styleable.RockerView_rockerScale, DEFAULT_ROCKER_SCALE);
//距离级别
mDistanceLevel = typedArray.getInt(R.styleable.RockerView_rockerSpeedLevel, 10);
//回调模式
mCallBackMode = getCallBackMode(typedArray.getInt(R.styleable.RockerView_rockerCallBackMode, 0));
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;
setMeasuredDimension(measureWidth, measureHeight);
Rect src = new Rect();
Rect dst = new Rect();
@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) ? (int) (cx / (mRockerScale + 1)) : (int) (cy / (mRockerScale + 1));
mRockerRadius = (int) (mAreaRadius * mRockerScale);
// 摇杆位置
if (0 == mRockerPosition.x || 0 == mRockerPosition.y)
mRockerPosition.set(mCenterPoint.x, mCenterPoint.y);
// 画可移动区域
if (AREA_BACKGROUND_MODE_PIC == mAreaBackgroundMode || AREA_BACKGROUND_MODE_XML == mAreaBackgroundMode)
// 图片
src.set(0, 0, mAreaBitmap.getWidth(), mAreaBitmap.getHeight());
dst.set(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)
// 图片
src.set(0, 0, mRockerBitmap.getWidth(), mRockerBitmap.getHeight());
dst.set(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();
baseDistance = mAreaRadius + 2;
// Log.e("baseDistance",baseDistance+"");
mRockerPosition = getRockerPositionPoint(mCenterPoint, new Point((int) moveX, (int) moveY), mAreaRadius + mRockerRadius, mRockerRadius);
moveRocker(mRockerPosition.x, mRockerPosition.y);
break;
case MotionEvent.ACTION_UP:// 抬起
case MotionEvent.ACTION_CANCEL:// 移出区域
// 回调 结束
callBackFinish();
if (mOnShakeListener != null)
mOnShakeListener.direction(Direction.DIRECTION_CENTER);
moveRocker(mCenterPoint.x, mCenterPoint.y);
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(lenX * lenX + lenY * lenY);
// 计算弧度
double radian = Math.acos(lenX / lenXY) * (touchPoint.y < centerPoint.y ? -1 : 1);
// 计算角度
double angle = radian2Angle(radian);
if (lenXY + rockerRadius <= regionRadius) // 触摸位置在可活动范围内
// 回调 返回参数
callBack(angle, (int) lenXY);
return touchPoint;
else // 触摸位置在可活动范围以外
// 计算要显示的位置
int showPointX = (int) (centerPoint.x + (regionRadius - rockerRadius) * Math.cos(radian));
int showPointY = (int) (centerPoint.y + (regionRadius - rockerRadius) * Math.sin(radian));
callBack(angle, (int) Math.sqrt((showPointX - centerPoint.x) * (showPointX - centerPoint.x) + (showPointY - centerPoint.y) * (showPointY - centerPoint.y)));
return new Point(showPointX, showPointY);
/**
* 移动摇杆到指定位置
*
* @param x x坐标
* @param y y坐标
*/
private void moveRocker(float x, float y)
mRockerPosition.set((int) x, (int) 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;
temp1Direction = Direction.DIRECTION_CENTER;
if (null != mOnAngleChangeListener)
mOnAngleChangeListener.onStart();
if (null != mOnShakeListener)
mOnShakeListener.onStart();
if (null != mOnShakeAngleDistanceLevelListener)
mOnShakeAngleDistanceLevelListener.onStart();
/**
* 回调
* 返回参数
*
* @param angle 摇动角度
*/
private void callBack(double angle, float distance)
if (Math.abs(distance - lastDistance) >= (baseDistance / mDistanceLevel))
lastDistance = distance;
if (null != mOnDistanceLevelListener)
int level = (int) (distance / (baseDistance / mDistanceLevel));
mOnDistanceLevelListener.onDistanceLevel(level);
if (lastAngle != angle)
lastAngle = 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;
if (null != mOnShakeAngleDistanceLevelListener)
int level = (int) (distance / (baseDistance / mDistanceLevel));
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)
// 右
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_RIGHT, angle, level);
else if (ANGLE_HORIZONTAL_2D_OF_0P <= angle && ANGLE_HORIZONTAL_2D_OF_1P > angle)
// 左
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_LEFT, angle, level);
break;
case DIRECTION_2_VERTICAL:// 上下方向
if (ANGLE_VERTICAL_2D_OF_0P <= angle && ANGLE_VERTICAL_2D_OF_1P > angle)
// 下
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_DOWN, angle, level);
else if (ANGLE_VERTICAL_2D_OF_1P <= angle && ANGLE_360 > angle)
// 上
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_UP, angle, level);
break;
case DIRECTION_4_ROTATE_0:// 四个方向
if (ANGLE_4D_OF_0P <= angle && ANGLE_4D_OF_1P > angle)
// 右下
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_DOWN_RIGHT, angle, level);
else if (ANGLE_4D_OF_1P <= angle && ANGLE_4D_OF_2P > angle)
// 左下
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_DOWN_LEFT, angle, level);
else if (ANGLE_4D_OF_2P <= angle && ANGLE_4D_OF_3P > angle)
// 左上
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_UP_LEFT, angle, level);
else if (ANGLE_4D_OF_3P <= angle && ANGLE_360 > angle)
// 右上
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_UP_RIGHT, angle, level);
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)
// 右
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_RIGHT, angle, level);
else if (ANGLE_ROTATE45_4D_OF_0P <= angle && ANGLE_ROTATE45_4D_OF_1P > angle)
// 下
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_DOWN, angle, level);
else if (ANGLE_ROTATE45_4D_OF_1P <= angle && ANGLE_ROTATE45_4D_OF_2P > angle)
// 左
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_LEFT, angle, level);
else if (ANGLE_ROTATE45_4D_OF_2P <= angle && ANGLE_ROTATE45_4D_OF_3P > angle)
// 上
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_UP, angle, level);
break;
case DIRECTION_8:// 八个方向
if (ANGLE_0 <= angle && ANGLE_8D_OF_0P > angle || ANGLE_8D_OF_7P <= angle && ANGLE_360 > angle)
// 右
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_RIGHT, angle, level);
else if (ANGLE_8D_OF_0P <= angle && ANGLE_8D_OF_1P > angle)
// 右下
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_DOWN_RIGHT, angle, level);
else if (ANGLE_8D_OF_1P <= angle && ANGLE_8D_OF_2P > angle)
// 下
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_DOWN, angle, level);
else if (ANGLE_8D_OF_2P <= angle && ANGLE_8D_OF_3P > angle)
// 左下
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_DOWN_LEFT, angle, level);
else if (ANGLE_8D_OF_3P <= angle && ANGLE_8D_OF_4P > angle)
// 左
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_LEFT, angle, level);
else if (ANGLE_8D_OF_4P <= angle && ANGLE_8D_OF_5P > angle)
// 左上
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_UP_LEFT, angle, level);
else if (ANGLE_8D_OF_5P <= angle && ANGLE_8D_OF_6P > angle)
// 上
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_UP, angle, level);
else if (ANGLE_8D_OF_6P <= angle && ANGLE_8D_OF_7P > angle)
// 右上
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_UP_RIGHT, angle, level);
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) &&
(temp1Direction != Direction.DIRECTION_RIGHT || last1Angle != angle || last1Level != level))
// 右
temp1Direction = Direction.DIRECTION_RIGHT;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_RIGHT, angle, level);
else if (ANGLE_HORIZONTAL_2D_OF_0P <= angle && ANGLE_HORIZONTAL_2D_OF_1P > angle &&
(temp1Direction != Direction.DIRECTION_LEFT || last1Angle != angle || last1Level != level))
// 左
temp1Direction = Direction.DIRECTION_LEFT;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_LEFT, angle, level);
break;
case DIRECTION_2_VERTICAL:// 上下方向
if (ANGLE_VERTICAL_2D_OF_0P <= angle && ANGLE_VERTICAL_2D_OF_1P > angle &&
(temp1Direction != Direction.DIRECTION_DOWN || last1Angle != angle || last1Level != level))
// 下
temp1Direction = Direction.DIRECTION_DOWN;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_DOWN, angle, level);
else if (ANGLE_VERTICAL_2D_OF_1P <= angle && ANGLE_360 > angle &&
(temp1Direction != Direction.DIRECTION_UP || last1Angle != angle || last1Level != level))
// 上
temp1Direction = Direction.DIRECTION_UP;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_UP, angle, level);
break;
case DIRECTION_4_ROTATE_0:// 四个方向
if (ANGLE_4D_OF_0P <= angle && ANGLE_4D_OF_1P > angle &&
(temp1Direction != Direction.DIRECTION_DOWN_RIGHT || last1Angle != angle || last1Level != level))
// 右下
temp1Direction = Direction.DIRECTION_DOWN_RIGHT;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_DOWN_RIGHT, angle, level);
else if (ANGLE_4D_OF_1P <= angle && ANGLE_4D_OF_2P > angle &&
(temp1Direction != Direction.DIRECTION_DOWN_LEFT || last1Angle != angle || last1Level != level))
// 左下
temp1Direction = Direction.DIRECTION_DOWN_LEFT;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_DOWN_LEFT, angle, level);
else if (ANGLE_4D_OF_2P <= angle && ANGLE_4D_OF_3P > angle &&
(temp1Direction != Direction.DIRECTION_UP_LEFT || last1Angle != angle || last1Level != level))
// 左上
temp1Direction = Direction.DIRECTION_UP_LEFT;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_UP_LEFT, angle, level);
else if (ANGLE_4D_OF_3P <= angle && ANGLE_360 > angle &&
(temp1Direction != Direction.DIRECTION_UP_RIGHT || last1Angle != angle || last1Level != level))
// 右上
temp1Direction = Direction.DIRECTION_UP_RIGHT;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_UP_RIGHT, angle, level);
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) &&
(temp1Direction != Direction.DIRECTION_RIGHT || last1Angle != angle || last1Level != level))
// 右
temp1Direction = Direction.DIRECTION_RIGHT;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_RIGHT, angle, level);
else if (ANGLE_ROTATE45_4D_OF_0P <= angle && ANGLE_ROTATE45_4D_OF_1P > angle &&
(temp1Direction != Direction.DIRECTION_DOWN || last1Angle != angle || last1Level != level))
// 下
temp1Direction = Direction.DIRECTION_DOWN;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_DOWN, angle, level);
else if (ANGLE_ROTATE45_4D_OF_1P <= angle && ANGLE_ROTATE45_4D_OF_2P > angle &&
(temp1Direction != Direction.DIRECTION_LEFT || last1Angle != angle || last1Level != level))
// 左
temp1Direction = Direction.DIRECTION_LEFT;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_LEFT, angle, level);
else if (ANGLE_ROTATE45_4D_OF_2P <= angle && ANGLE_ROTATE45_4D_OF_3P > angle &&
(temp1Direction != Direction.DIRECTION_UP || last1Angle != angle || last1Level != level))
// 上
temp1Direction = Direction.DIRECTION_UP;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_UP, angle, level);
break;
case DIRECTION_8:// 八个方向
if ((ANGLE_0 <= angle && ANGLE_8D_OF_0P > angle || ANGLE_8D_OF_7P <= angle && ANGLE_360 > angle)
&& (temp1Direction != Direction.DIRECTION_RIGHT || last1Angle != angle || last1Level != level))
// 右
temp1Direction = Direction.DIRECTION_RIGHT;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_RIGHT, angle, level);
else if (ANGLE_8D_OF_0P <= angle && ANGLE_8D_OF_1P > angle &&
(temp1Direction != Direction.DIRECTION_DOWN_RIGHT || last1Angle != angle || last1Level != level))
// 右下
temp1Direction = Direction.DIRECTION_DOWN_RIGHT;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_DOWN_RIGHT, angle, level);
else if (ANGLE_8D_OF_1P <= angle && ANGLE_8D_OF_2P > angle &&
(temp1Direction != Direction.DIRECTION_DOWN || last1Angle != angle || last1Level != level))
// 下
temp1Direction = Direction.DIRECTION_DOWN;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_DOWN, angle, level);
else if (ANGLE_8D_OF_2P <= angle && ANGLE_8D_OF_3P > angle &&
(temp1Direction != Direction.DIRECTION_DOWN_LEFT || last1Angle != angle || last1Level != level))
// 左下
temp1Direction = Direction.DIRECTION_DOWN_LEFT;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_DOWN_LEFT, angle, level);
else if (ANGLE_8D_OF_3P <= angle && ANGLE_8D_OF_4P > angle &&
(temp1Direction != Direction.DIRECTION_LEFT || last1Angle != angle || last1Level != level))
// 左
temp1Direction = Direction.DIRECTION_LEFT;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_LEFT, angle, level);
else if (ANGLE_8D_OF_4P <= angle && ANGLE_8D_OF_5P > angle &&
(temp1Direction != Direction.DIRECTION_UP_LEFT || last1Angle != angle || last1Level != level))
// 左上
temp1Direction = Direction.DIRECTION_UP_LEFT;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_UP_LEFT, angle, level);
else if (ANGLE_8D_OF_5P <= angle && ANGLE_8D_OF_6P > angle &&
(temp1Direction != Direction.DIRECTION_UP || last1Angle != angle || last1Level != level))
// 上
temp1Direction = Direction.DIRECTION_UP;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_UP, angle, level);
else if (ANGLE_8D_OF_6P <= angle && ANGLE_8D_OF_7P > angle &&
(temp1Direction != Direction.DIRECTION_UP_RIGHT || last1Angle != angle || last1Level != level))
// 右上
temp1Direction = Direction.DIRECTION_UP_RIGHT;
last1Angle = angle;
last1Level = level;
mOnShakeAngleDistanceLevelListener.onShakeAngleDistanceLevel(Direction.DIRECTION_UP_RIGHT, angle, level);
break;
default:
break;
/**
* 回调
* 结束
*/
private void callBackFinish()
tempDirection = Direction.DIRECTION_CENTER;
temp1Direction = Direction.DIRECTION_CENTER;
if (null != mOnAngleChangeListener)
mOnAngleChangeListener.onFinish();
if (null != mOnShakeListener)
mOnShakeListener.onFinish();
if (null != mOnShakeAngleDistanceLevelListener)
mOnShakeAngleDistanceLevelListener.onFinish();
/**
* 回调模式
*/
public enum CallBackMode
// 有移动就立刻回调
CALL_BACK_MODE_MOVE,
// 只有状态变化的时候才回调
CALL_BACK_MODE_STATE_CHANGE,
//只有状态变化或者距离变化的时候才回调
CALL_BACK_MODE_STATE_DISTANCE_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 void setOnDistanceLevelListener(OnDistanceLevelListener listener)
mOnDistanceLevelListener = listener;
/**
* 添加变化回调
*/
public void setOnShakeAngleDistanceLevelListener(OnShakeAngleDistanceLevelListener listener)
mOnShakeAngleDistanceLevelListener = 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();
/**
* 摇动距离
*/
public interface OnDistanceLevelListener
void onDistanceLevel(int level);
/**
* 摇动方向+角度+距离
*/
public interface OnShakeAngleDistanceLevelListener
// 开始
void onStart();
void onShakeAngleDistanceLevel(Direction direction, double angle, int level);
// 结束
void onFinish();
private CallBackMode getCallBackMode(int mode)
switch (mode)
case 0:
return CallBackMode.CALL_BACK_MODE_MOVE;
case 1:
return CallBackMode.CALL_BACK_MODE_STATE_CHANGE;
return mCallBackMode;
然后在value文件夹下创建attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--
areaBackground 设置区域背景
rockerBackground 设置摇杆的样式
rockerScale 设置摇杆的相对于背景的比例
rockerSpeedLevel 设置当前位置相对于中心点的距离的比例 如:10 则中心点到边缘的距离分为 10 分越靠外数值越大 0-10
rockerCallBackMode 有变化就回调,或者是方向改变才会回调-->
<declare-styleable name="RockerView">
<attr name="areaBackground" format="color|reference" />
<attr name="rockerBackground" format="color|reference" />
<attr name="rockerScale" format="float"/>
<attr name="rockerSpeedLevel" format="integer" />
<attr name="rockerCallBackMode">
<flag name="CALL_BACK_MODE_MOVE" value="0" />
<flag name="CALL_BACK_MODE_STATE_CHANGE" value="1" />
</attr>
</declare-styleable>
</resources>
使用非常简单
mViewBinding.rockerView.setOnShakeAngleDistanceLevelListener(new RockerView.OnShakeAngleDistanceLevelListener()
@Override
public void onStart()
@Override
public void onShakeAngleDistanceLevel(RockerView.Direction direction, double angle, int level)
LogUtil.d(TAG, "onShakeAngleDistanceLevel:" + direction + ":" + angle + ":" + level);
@Override
public void onFinish()
);
其他具体的回调用法可以看源码可以看readme。
改后的源码
以上是关于android 摇杆控件实现RockerView的主要内容,如果未能解决你的问题,请参考以下文章
求助:android两个及以上控件点击事件同时监听响应如何实现