自定义控件实现弹性旋转的圆形菜单
- 写这个之前参考了一下其他类似的控件,自己实现了一下并做如下记录
- 使用到的包含“事件拦截”、“三角函数”、“View测量”、“View布局”
- 控件的效果图如下,旋转动画、弹性旋转、item点击
流程梳理
- 首先要实现一个圆形的菜单控件我们选择继承ViewGroup;
- 第一步考虑在onMeasure中对所有Child进行测量,测量完成后onLayout才可以获取到Child的测量宽高;
- 第二步考虑Child排版问题,也就是核心代码中onLayout的过程;
- 在这个过程中,定义了“当前旋转角度”、“旋转中心”、旋转半径;
- 获取容器内部Child个数,计算相邻Child角度间距;
- 在已知旋转中心、半径、当前旋转角度、相邻Child角度间距后就可以为Child排版啦;
- 这里根据角度及其所在象限,计算出该角度相对于每个象限的角度并计算其正切值;
- 在已知半径、正切值就可以通过三角函数tan(A)=a边/b边、勾股定理a²+b²=c²计算出a边长、b边长;
- 根据Child所在的象限,我们可以利用a边、b边计算出该Child中心点所在的坐标;
- 知道这样就可以对该Child进行布局了,即调用Child.onLayout(l,t,r,b),布局时需要使用Child的测量宽高;
- 第三步考虑触摸事件的拦截,这里使用容器onInterceptTouchEvent 方法在滑动距离大于系统touchSlop时进行拦截;
- 一旦ViewGroup决定拦截该事件,那么后续的事件都会调用容器onTouchEvent;
- 我们在onTouchEvent 的Move时,计算前后2次事件的偏转角度来更改“当前选中角度”并要求容器重新布局 requestLayout(); 以此达到旋转的目的;
- 我们在onTouchEvent 的Up时计算本次旋转在1秒内旋转过的角度是否达到弹性旋转
- 如果需要弹性旋转则根据当前的速度,设定Runnable 进行弹性旋转;
代码块
控件的代码如下:
public class RotateView extends ViewGroup {
private static final String TAG = "RotateView";
/**
* 当前已旋转的角度 ,当改变该角度时,并重新布局则达到旋转的效果
*/
private float mCurAngle = 0f;
/**
* 记录每次旋转开始时的角度
*/
private float mStartRotateAngle;
/**
* 当前ViewGroup 旋转的中心点坐标
*/
private PointF mCenterPoint;
/**
* 围绕中心点旋转的半径
*/
private double mR;
/**
* 缓存每个Child 布局时所在的位置
*/
private PointF mChildPoint = new PointF();
/**
* 系统可检测的最小滑动距离
*/
private int touchSlop;
/**
* 记录每次MotionEvent 的坐标值
*/
private float mLastX;
private float mLastY;
/**
* 记录开始滑动的时间
*/
private long mStartRotateTime;
/**
* 触发弹性旋转的边界值
*/
private static final float ROTATE_RATE = 500;
/**
* 弹性旋转Runnable
*/
private RotateRunnable action;
/**
* 当前是否处于弹性旋转状态
*/
private boolean isFling;
public RotateView(Context context) {
this(context, null);
}
public RotateView(Context context, AttributeSet attrs) {
super(context, attrs);
mCenterPoint = new PointF();
touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
@Override
protected void