作者:林伯勋玉萍竣梅 | 来源:互联网 | 2023-08-31 16:52
自定义View练习-圆形菜单来源自定义view实战笔记–CircleMenu关键代码都在这篇博客中有说明.效果图主要实现类CircleMenuLayout.java自定
自定义View练习 - 圆形菜单
来源
自定义view实战笔记–CircleMenu
关键代码都在这篇博客中有说明.
效果图
主要实现类
CircleMenuLayout.java
自定义ViewGroup的重点是测量和布局
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int measureWidth;int measureHeight;int size = MeasureSpec.getSize(widthMeasureSpec);int mode = MeasureSpec.getMode(widthMeasureSpec);if (mode == MeasureSpec.EXACTLY) { measureWidth = measureHeight = Math.min(size, getDefaultWidth());} else { int suggestedMinimumWidth = getSuggestedMinimumWidth();if (suggestedMinimumWidth == 0) { measureWidth = measureHeight = getDefaultWidth();} else { measureWidth = measureHeight = Math.min(suggestedMinimumWidth, getDefaultWidth());}}setMeasuredDimension(measureWidth, measureHeight);diameter = measureWidth;for (int i = 0; i
@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {for (int i = 0; i int childWidth = childView.getMeasuredWidth();int left = (int) (radius + distance * Math.cos(Math.toRadians(startAngle)) - childWidth / 2);int top = (int) (radius + distance * Math.sin(Math.toRadians(startAngle)) - childWidth / 2);int right = left + childWidth;int bottom = top + childWidth;childView.layout(left, top, right, bottom);startAngle += 360 / getChildCount();}}
触摸控件,让子View伴随着滚动. 这里需要重写onTouchEvent.
private float lastX;private float lastY;@Overridepublic boolean onTouchEvent(MotionEvent event) {float x = event.getX();float y = event.getY();switch (event.getAction()) {case MotionEvent.ACTION_DOWN:lastX = x;lastY = y;break;case MotionEvent.ACTION_MOVE:float start = CircleUtil.getAngle(lastX, lastY, diameter); float end = CircleUtil.getAngle(x, y, diameter); float angle; if (CircleUtil.getQuadrant(x, y, diameter) == 1 || CircleUtil.getQuadrant(x, y, diameter) == 4) {angle = end - start;} else {angle = start - end;}startAngle += angle;requestLayout();lastX = x;lastY = y;break;case MotionEvent.ACTION_UP:break;}return true;}
其中使用了工具类CircleUtils
public class CircleUtil {/*** 根据触摸的位置,计算角度** @param xTouch* @param yTouch* @param d 直径* @return*/public static float getAngle(float xTouch, float yTouch,int d) {double x = xTouch - (d / 2f);double y = yTouch - (d / 2f);return (float) (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);}/*** 根据当前位置计算象限** @param x* @param y* @param d 直径* @return*/public static int getQuadrant(float x, float y,int d) {int tmpX = (int) (x - d / 2);int tmpY = (int) (y - d / 2);if (tmpX >= 0) {return tmpY >= 0 ? 4 : 1;} else {return tmpY >= 0 ? 3 : 2;}}
}
学到的知识
- 在自定义ViewGroup中不仅要测量自己的宽高, 还需要测量子View的大小.
- 重写onLayout主要是对子View坐标的计算. 通过规律获得坐标值.
- onTouchEvent返回true, 表示当前控件消费了touch事件.
遗留的问题
- 在onTouchEvent中为什么在第一和第四象限计算角度会是正数,而其他象限是负数? 我通过打印日志看到, end角度在 -90度 ~0和0~ 90度 之间变化. 这个规律不是很清楚.
github地址: https://github.com/cizkey/CustomPractice/tree/master/CircleMenu