自定义View练习
来源:互联网 发布:第一会软件 编辑:程序博客网 时间:2024/05/19 18:48
自定义View练习 - 圆形菜单
来源
自定义view实战笔记–CircleMenu
关键代码都在这篇博客中有说明.
效果图
主要实现类
CircleMenuLayout.java
自定义ViewGroup的重点是测量和布局
@Override protected 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; //测量子View for (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); }
@Override protected 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; @Override public 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; } }}
学到的知识
- 在自定义ViewGroup中不仅要测量自己的宽高, 还需要测量子View的大小.
- 重写onLayout主要是对子View坐标的计算. 通过规律获得坐标值.
- onTouchEvent返回true, 表示当前控件消费了touch事件.
遗留的问题
- 在onTouchEvent中为什么在第一和第四象限计算角度会是正数,而其他象限是负数? 我通过打印日志看到, end角度在 -90度 ~0和0~ 90度 之间变化. 这个规律不是很清楚.
github地址: https://github.com/cizkey/CustomPractice/tree/master/CircleMenu
阅读全文
0 0
- 自定义View小练习
- 自定义View练习
- 自定义View练习
- 自定义View练习
- 自定义View练习
- 自定义View练习
- 自定义View练习一
- 自定义view-canvas练习
- 自定义View练习1
- 自定义View练习2
- android自定义View练习之波浪View
- Android 自定义View练习之水波纹
- 自定义 view 练习(2):卫星式菜单
- 自定义View练习(一)饼状图
- 自定义View练习(二)二阶贝塞尔曲线
- HenCoder 自定义 View 1-1 练习项目
- 新手自定义view练习实例之(二) 波浪view
- 自定义View练习(一个自定义的折线图)
- 括号匹配检测(难度:半颗星)
- sqlserver连接数据库
- Android:打造一个属于自己的浏览器(2)
- 什么时候使用字节流、什么时候使用字符流,二者的区别
- Unix、Posix和标准UniX规范
- 自定义View练习
- P1209 几何图形还原
- Android笔记五(Binder例子)
- Leetcode 151(Java)
- .Net实现邮件定时发送问题总结
- 【ubuntu】1404 安装ssh-server时出现错误
- hdu1496—Equations(枚举)
- 数据结构实验之栈七:出栈序列判定
- Win2008系统创建DNS服务器