自定义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 { //wrap_content模式//获得指定的背景大小,背景有多大,控件就有多大int suggestedMinimumWidth = getSuggestedMinimumWidth();//如果suggestedMinimumWidth为0, 则没有背景if (suggestedMinimumWidth == 0) { //如果没有背景,则设置为默认宽度measureWidth = measureHeight = getDefaultWidth();} else { //如果有背景,则设置为背景宽和默认宽度的最小值measureWidth = measureHeight = Math.min(suggestedMinimumWidth, getDefaultWidth());}}setMeasuredDimension(measureWidth, measureHeight);//直径为测量的宽度diameter = measureWidth;//测量子Viewfor (int i = 0; i < getChildCount(); i++) {View childView = getChildAt(i);//默认系统是通过onMeasure给予MeasureSpec参数的,而对于inflate进来的子视图是没有MeasureSpec参数的//因此,我们需要自己设计MeasureSpec,传递给子视图int makeMeasureSpec = MeasureSpec.makeMeasureSpec(diameter / 3, MeasureSpec.EXACTLY);childView.measure(makeMeasureSpec, makeMeasureSpec);}}/*** 获得屏幕宽高中的较小值*/public int getDefaultWidth() {DisplayMetrics displayMetrics = getResources().getDisplayMetrics();int widthPixels = displayMetrics.widthPixels;int heightPixels = displayMetrics.heightPixels;return Math.min(widthPixels, heightPixels);}
  @Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {for (int i = 0; i < getChildCount(); i++) {View childView = getChildAt(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; //旋转的角度//todo 这里有很大的疑问? 为什么这个角度是负数?//判断点击的点所处的象限,如果是1,4象限,角度值是正数,否则是负数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);//hypot:通过直角边,求斜边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;}}
}

学到的知识

  1. 在自定义ViewGroup中不仅要测量自己的宽高, 还需要测量子View的大小.
  2. 重写onLayout主要是对子View坐标的计算. 通过规律获得坐标值.
  3. onTouchEvent返回true, 表示当前控件消费了touch事件.

遗留的问题

  1. 在onTouchEvent中为什么在第一和第四象限计算角度会是正数,而其他象限是负数? 我通过打印日志看到, end角度在 -90度 ~0和0~ 90度 之间变化. 这个规律不是很清楚.

github地址: https://github.com/cizkey/CustomPractice/tree/master/CircleMenu

自定义View练习 - 圆形菜单相关推荐

  1. android view 渐变动画,Android自定义view渐变圆形动画

    本文实例为大家分享了Android自定义view渐变圆形动画的具体代码,供大家参考,具体内容如下 直接上效果图 自定义属性 attrs.xml文件 创建一个类 ProgressRing继承自 view ...

  2. android 清空canvas部分内容_Android自定义View实现圆形头像效果

    在我们的APP中通常会遇到,展示圆形头像的需求,一般通过Glide就能实现,但是让我们做一个圆形头像,如果让我们自定义实现这种效果,该怎样做呢? 好,接下来本文通过三种方式来实现这种效果! 注意:这是 ...

  3. android刷新时的圆形动画_Android自定义view渐变圆形动画

    本文实例为大家分享了Android自定义view渐变圆形动画的具体代码,供大家参考,具体内容如下 直接上效果图 自定义属性 attrs.xml文件 创建一个类 ProgressRing继承自 view ...

  4. Android自定义滑动进度条,Android自定义View实现圆形水波进度条

    每次听到某大牛谈论自定义View,顿时敬佩之心,如滔滔江水连绵不绝,心想我什么时候能有如此境界,好了,心动不如行动,于是我开始了自定义View之路,虽然过程有坎坷,但是结果我还是挺满意的.我知道大牛还 ...

  5. Android 自定义View实现圆形头像(适用于任意布局)

    先看效果图: 先来说下我的思路:首先我需要在自定义View中动态获取头像id,那么就需要在attrs文件中,写一个关于该View类的自定义属性.这里仿照ImageView,取名为src,类型为refe ...

  6. 自定义View实现圆形水波进度条

    每次听到某大牛谈论自定义View,顿时敬佩之心,如滔滔江水连绵不绝,心想我什么时候能有如此境界,好了,心动不如行动,于是我开始了自定义View之路,虽然过程有坎坷,但是结果我还是挺满意的.我知道大牛还 ...

  7. Android自定义View之圆形头像

    记录贴 现在制作圆形头像的第三方工具已经很多了,本帖只为记录自定义view学习过程. 1.主体代码部分 public class CirclePhotoView extends View {priva ...

  8. Android自定义view,圆形的TextView,并通过xml设置属性,AttributeSet中取值

    Android自定义view设置xml属性 一个圆形的自定义TextView,通过xml来设置背景颜色的属性 values/attrs <declare-styleable name=" ...

  9. 自定义View,圆形头像

    1. 效果图 2. xml中 <com.etoury.etoury.ui.view.CircleImgandroid:id="@+id/user_info_head_img" ...

最新文章

  1. Windows Azure Platform Introduction (2) 云计算的分类和服务层次
  2. 文本省略并显示省略号
  3. 如何解决提示the operation % is undefined for the argument type string,int的错误
  4. 数字语音信号处理学习笔记——语音信号的短时时域分析(2)
  5. 【案例】护士发错药怎么处理?
  6. 计算机应用基础自考,自考计算机应用基础
  7. 频发:故障排除之又见 ORA-4031丨云和恩墨技术通讯
  8. eclipse中选中一个单词 其他相同的也被选中 怎么设置
  9. 使用jquery 动态操作添加/删除tr td
  10. python源码中的学习笔记_第9章_类与对象
  11. 显示器、显卡的接口类型
  12. 五年产品经理的转正述职报告(附PPT下载)
  13. input隐藏边框和选中样式
  14. 如何用ftp上传到服务器视频文件,ftp如何将文件上传到服务器上
  15. pg数据库数据量很小但是data目录很大的排查思路
  16. 【PHP项目部署一】PHP环境配置
  17. 元宇宙的运行之“DAO”:在元宇宙中创作、分享,付出的劳动如何获得回报?...
  18. EasyBuilder8000的安装(古月金真)
  19. 基于Arduino的温控风扇
  20. 苹果设备模拟器 所有XCcode正式版本 下载方法

热门文章

  1. 15款业界公认的最佳视频处理软件
  2. 阿里云服务器部署多个tomcat服务
  3. 快手上市,投资人张斐说:多数人惊叹算法威力,少数人理解网络结构
  4. 分众无线MSN强强联手 引发无线营销新热潮
  5. Notebook环境配置、安装
  6. XXX收集了三年的最好的设计网站
  7. centos7 安装两个mysql_centos7安装运行多个mysql实例笔记
  8. docker运行yyets_docker 后台运行和进入后台运行的容器
  9. leetcode刷题笔记——二分查找
  10. Qt5 on Wayland