原文:Android笔记--自定义控件仿遥控器的圆形上下左右OK圆盘按钮

上面就是几张预览图!代码在最底下

主要就两个步骤,画图、监听点击

1、整个控件基本上是一步步画出来的,重写onDraw方法开始for循环画扇形出来,画扇形的时候同时画扇形内的图标,扇形画完了之后画中心圆按钮,中心画了圆之后吧OK按钮画上。

2、重写onTouch方法监听手指触发的位置,根据按下的位置确定按钮在哪个扇形的范围上,然后重绘一下onDraw把按下的灰色背景绘制出来。难点在于如何确定按下的位置是否处于扇形范围,换了好几种方案,奈何数学不好,最后选了现在使用的方案。

  1. package com.imxiaoyu.common.widget;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.graphics.Canvas;
  5. import android.graphics.Matrix;
  6. import android.graphics.Paint;
  7. import android.graphics.RectF;
  8. import android.util.AttributeSet;
  9. import android.view.MotionEvent;
  10. import android.view.View;
  11. import java.util.ArrayList;
  12. import java.util.Date;
  13. import java.util.List;
  14. /**
  15. * 仿遥控器上下左右ok圆形菜单
  16. * Created by 庞光渝 on 2017/3/9.博客:https://my.oschina.net/u/1462828/blog
  17. */
  18. public class RoundMenuView extends View {
  19. /**
  20. * 变量
  21. */
  22. private int coreX;//中心点的坐标X
  23. private int coreY;//中心点的坐标Y
  24. private List<RoundMenu> roundMenus;//菜单列表
  25. private boolean isCoreMenu = false;//是否有中心按钮
  26. private int coreMenuColor;//中心按钮的默认背景--最好不要透明色
  27. private int coreMenuStrokeColor;//中心按钮描边颜色
  28. private int coreMenuStrokeSize;//中心按钮描边粗细
  29. private int coreMenuSelectColor;//中心按钮选中时的背景颜色
  30. private Bitmap coreBitmap;//OK图片
  31. private OnClickListener onCoreClickListener;//中心按钮的点击回调
  32. private float deviationDegree;//偏移角度
  33. private int onClickState = -2;//-2是无点击,-1是点击中心圆,其他是点击菜单
  34. private int roundRadius;//中心圆的半径
  35. private double radiusDistance;//半径的长度比(中心圆半径=大圆半径*radiusDistance)
  36. private long touchTime;//按下时间,抬起的时候判定一下,超过300毫秒算点击
  37. public RoundMenuView(Context context) {
  38. super(context);
  39. }
  40. public RoundMenuView(Context context, AttributeSet attrs) {
  41. super(context, attrs);
  42. }
  43. public RoundMenuView(Context context, AttributeSet attrs, int defStyle) {
  44. super(context, attrs, defStyle);
  45. }
  46. @Override
  47. protected void onDraw(Canvas canvas) {
  48. coreX = getWidth() / 2;
  49. coreY = getHeight() / 2;
  50. roundRadius = (int) (getWidth()/2 * radiusDistance);//计算中心圆圈半径
  51. RectF rect = new RectF(0, 0, getWidth(), getHeight());
  52. if (roundMenus != null && roundMenus.size() > 0) {
  53. float sweepAngle = 360 / roundMenus.size();//每个弧形的角度
  54. deviationDegree = sweepAngle / 2;//其实的偏移角度,如果4个扇形的时候是X形状,而非+,设为0试试就知道什么意思了
  55. for (int i = 0; i < roundMenus.size(); i++) {
  56. RoundMenu roundMenu = roundMenus.get(i);
  57. //填充
  58. Paint paint = new Paint();
  59. paint.setAntiAlias(true);
  60. if (onClickState == i) {
  61. //选中
  62. paint.setColor(roundMenu.selectSolidColor);
  63. } else {
  64. //未选中
  65. paint.setColor(roundMenu.solidColor);
  66. }
  67. canvas.drawArc(rect, deviationDegree + (i * sweepAngle), sweepAngle, true, paint);
  68. //画描边
  69. paint = new Paint();
  70. paint.setAntiAlias(true);
  71. paint.setStrokeWidth(roundMenu.strokeSize);
  72. paint.setStyle(Paint.Style.STROKE);
  73. paint.setColor(roundMenu.strokeColor);
  74. canvas.drawArc(rect, deviationDegree + (i * sweepAngle), sweepAngle, roundMenu.useCenter, paint);
  75. //画图案
  76. Matrix matrix = new Matrix();
  77. matrix.postTranslate((float) ((coreX + getWidth() / 2 * roundMenu.iconDistance) - (roundMenu.icon.getWidth() / 2)), coreY - (roundMenu.icon.getHeight() / 2));
  78. matrix.postRotate(((i + 1) * sweepAngle), coreX, coreY);
  79. canvas.drawBitmap(roundMenu.icon, matrix, null);
  80. }
  81. }
  82. //画中心圆圈
  83. if (isCoreMenu) {
  84. //填充
  85. RectF rect1 = new RectF(coreX - roundRadius, coreY - roundRadius, coreX + roundRadius, coreY + roundRadius);
  86. Paint paint = new Paint();
  87. paint.setAntiAlias(true);
  88. paint.setStrokeWidth(coreMenuStrokeSize);
  89. if (onClickState == -1) {
  90. paint.setColor(coreMenuSelectColor);
  91. } else {
  92. paint.setColor(coreMenuColor);
  93. }
  94. canvas.drawArc(rect1, 0, 360, true, paint);
  95. //画描边
  96. paint = new Paint();
  97. paint.setAntiAlias(true);
  98. paint.setStrokeWidth(coreMenuStrokeSize);
  99. paint.setStyle(Paint.Style.STROKE);
  100. paint.setColor(coreMenuStrokeColor);
  101. canvas.drawArc(rect1, 0, 360, true, paint);
  102. if (coreBitmap != null) {
  103. //画中心圆圈的“OK”图标
  104. canvas.drawBitmap(coreBitmap, coreX - coreBitmap.getWidth() / 2, coreY - coreBitmap.getHeight() / 2, null);//在 0,0坐标开始画入src
  105. }
  106. }
  107. }
  108. public boolean onTouchEvent(MotionEvent event) {
  109. switch (event.getAction()) {
  110. case MotionEvent.ACTION_DOWN:
  111. touchTime = new Date().getTime();
  112. float textX = event.getX();
  113. float textY = event.getY();
  114. int distanceLine = (int) getDisForTwoSpot(coreX, coreY, textX, textY);//距离中心点之间的直线距离
  115. if (distanceLine <= roundRadius) {
  116. //点击的是中心圆;按下点到中心点的距离小于中心园半径,那就是点击中心园了
  117. onClickState = -1;
  118. } else if (distanceLine <= getWidth() / 2) {
  119. //点击的是某个扇形;按下点到中心点的距离大于中心圆半径小于大圆半径,那就是点击某个扇形了
  120. float sweepAngle = 360 / roundMenus.size();//每个弧形的角度
  121. int angle = getRotationBetweenLines(coreX, coreY, textX, textY);
  122. //这个angle的角度是从正Y轴开始,而我们的扇形是从正X轴开始,再加上偏移角度,所以需要计算一下
  123. angle = (angle + 360 - 90 - (int) deviationDegree) % 360;
  124. onClickState = (int) (angle / sweepAngle);//根据角度得出点击的是那个扇形
  125. } else {
  126. //点击了外面
  127. onClickState = -2;
  128. }
  129. invalidate();
  130. break;
  131. case MotionEvent.ACTION_UP:
  132. if ((new Date().getTime() - touchTime) < 300) {
  133. //点击小于300毫秒算点击
  134. OnClickListener onClickListener = null;
  135. if (onClickState == -1) {
  136. onClickListener = onCoreClickListener;
  137. } else if (onClickState >= 0 && onClickState < roundMenus.size()) {
  138. onClickListener = roundMenus.get(onClickState).onClickListener;
  139. }
  140. if (onClickListener != null) {
  141. onClickListener.onClick(this);
  142. }
  143. }
  144. onClickState = -2;
  145. invalidate();
  146. break;
  147. }
  148. return true;
  149. }
  150. /**
  151. * 添加菜单
  152. *
  153. * @param roundMenu
  154. */
  155. public void addRoundMenu(RoundMenu roundMenu) {
  156. if (roundMenu == null) {
  157. return;
  158. }
  159. if (roundMenus == null) {
  160. roundMenus = new ArrayList<>();
  161. }
  162. roundMenus.add(roundMenu);
  163. invalidate();
  164. }
  165. /**
  166. * 添加中心菜单按钮
  167. *
  168. * @param coreMenuColor
  169. * @param coreMenuSelectColor
  170. * @param onClickListener
  171. */
  172. public void setCoreMenu(int coreMenuColor, int coreMenuSelectColor, int coreMenuStrokeColor,
  173. int coreMenuStrokeSize, double radiusDistance,Bitmap bitmap, OnClickListener onClickListener) {
  174. isCoreMenu = true;
  175. this.coreMenuColor = coreMenuColor;
  176. this.radiusDistance = radiusDistance;
  177. this.coreMenuSelectColor = coreMenuSelectColor;
  178. this.coreMenuStrokeColor = coreMenuStrokeColor;
  179. this.coreMenuStrokeSize = coreMenuStrokeSize;
  180. coreBitmap = bitmap;
  181. this.onCoreClickListener = onClickListener;
  182. invalidate();
  183. }
  184. /**
  185. * 获取两条线的夹角
  186. *
  187. * @param centerX
  188. * @param centerY
  189. * @param xInView
  190. * @param yInView
  191. * @return
  192. */
  193. public static int getRotationBetweenLines(float centerX, float centerY, float xInView, float yInView) {
  194. double rotation = 0;
  195. double k1 = (double) (centerY - centerY) / (centerX * 2 - centerX);
  196. double k2 = (double) (yInView - centerY) / (xInView - centerX);
  197. double tmpDegree = Math.atan((Math.abs(k1 - k2)) / (1 + k1 * k2)) / Math.PI * 180;
  198. if (xInView > centerX && yInView < centerY) { //第一象限
  199. rotation = 90 - tmpDegree;
  200. } else if (xInView > centerX && yInView > centerY) //第二象限
  201. {
  202. rotation = 90 + tmpDegree;
  203. } else if (xInView < centerX && yInView > centerY) { //第三象限
  204. rotation = 270 - tmpDegree;
  205. } else if (xInView < centerX && yInView < centerY) { //第四象限
  206. rotation = 270 + tmpDegree;
  207. } else if (xInView == centerX && yInView < centerY) {
  208. rotation = 0;
  209. } else if (xInView == centerX && yInView > centerY) {
  210. rotation = 180;
  211. }
  212. return (int) rotation;
  213. }
  214. /**
  215. * 求两个点之间的距离
  216. *
  217. * @return
  218. */
  219. public static double getDisForTwoSpot(float x1, float y1, float x2, float y2) {
  220. float width, height;
  221. if (x1 > x2) {
  222. width = x1 - x2;
  223. } else {
  224. width = x2 - x1;
  225. }
  226. if (y1 > y2) {
  227. height = y2 - y1;
  228. } else {
  229. height = y2 - y1;
  230. }
  231. return Math.sqrt((width * width) + (height * height));
  232. }
  233. /**
  234. * 扇形的对象类
  235. */
  236. public static class RoundMenu {
  237. public boolean useCenter = true;//扇形是否画连接中心点的直线
  238. public int solidColor = 0x00000000;//背景颜色,默认透明
  239. public int selectSolidColor = 0x00000000;//背景颜色,默认透明
  240. public int strokeColor = 0x00000000;//描边颜色,默认透明
  241. public int strokeSize = 1;//描边的宽度,默认1
  242. public Bitmap icon;//菜单的图片
  243. public OnClickListener onClickListener;//点击监听
  244. public double iconDistance = 0.63;//图标距离中心点的距离
  245. }
  246. }

然后是调用,调用代码就简单的放几句吧,应该看得懂的

  1. RoundMenuView.RoundMenu roundMenu = new RoundMenuView.RoundMenu();
  2. roundMenu.selectSolidColor = ColorUtils.getColor(getActivity(), R.color.gray_9999);
  3. roundMenu.strokeColor = ColorUtils.getColor(getActivity(), R.color.gray_9999);
  4. roundMenu.icon=ImageUtils.drawable2Bitmap(getActivity(),R.drawable.ic_right);
  5. roundMenu.onClickListener=new OnClickListener() {
  6. @Override
  7. public void onClick(View view) {
  8. ToastUtils.showToast(getActivity(),"点击了1");
  9. }
  10. };
  11. roundMenuView.addRoundMenu(roundMenu);
  12. roundMenu = new RoundMenuView.RoundMenu();
  13. roundMenu.selectSolidColor = ColorUtils.getColor(getActivity(), R.color.gray_9999);
  14. roundMenu.strokeColor = ColorUtils.getColor(getActivity(), R.color.gray_9999);
  15. roundMenu.icon=ImageUtils.drawable2Bitmap(getActivity(),R.drawable.ic_right);
  16. roundMenu.onClickListener=new OnClickListener() {
  17. @Override
  18. public void onClick(View view) {
  19. ToastUtils.showToast(getActivity(),"点击了2");
  20. }
  21. };
  22. roundMenuView.addRoundMenu(roundMenu);
  23. roundMenu = new RoundMenuView.RoundMenu();
  24. roundMenu.selectSolidColor = ColorUtils.getColor(getActivity(), R.color.gray_9999);
  25. roundMenu.strokeColor = ColorUtils.getColor(getActivity(), R.color.gray_9999);
  26. roundMenu.icon=ImageUtils.drawable2Bitmap(getActivity(),R.drawable.ic_right);
  27. roundMenu.onClickListener=new OnClickListener() {
  28. @Override
  29. public void onClick(View view) {
  30. ToastUtils.showToast(getActivity(),"点击了3");
  31. }
  32. };
  33. roundMenuView.addRoundMenu(roundMenu);
  34. roundMenu = new RoundMenuView.RoundMenu();
  35. roundMenu.selectSolidColor = ColorUtils.getColor(getActivity(), R.color.gray_9999);
  36. roundMenu.strokeColor = ColorUtils.getColor(getActivity(), R.color.gray_9999);
  37. roundMenu.icon=ImageUtils.drawable2Bitmap(getActivity(),R.drawable.ic_right);
  38. roundMenu.onClickListener=new OnClickListener() {
  39. @Override
  40. public void onClick(View view) {
  41. ToastUtils.showToast(getActivity(),"点击了4");
  42. }
  43. };
  44. roundMenuView.addRoundMenu(roundMenu);
  45. roundMenuView.setCoreMenu(ColorUtils.getColor(getActivity(), R.color.gray_f2f2),
  46. ColorUtils.getColor(getActivity(), R.color.gray_9999), ColorUtils.getColor(getActivity(), R.color.gray_9999)
  47. , 1, 0.43,ImageUtils.drawable2Bitmap(getActivity(),R.drawable.ic_ok), new OnClickListener() {
  48. @Override
  49. public void onClick(View view) {
  50. ToastUtils.showToast(getActivity(),"点击了中心圆圈");
  51. }
  52. });

 

http://doutugongchang.com

Android笔记--自定义控件仿遥控器的圆形上下左右OK圆盘按钮的更多相关文章

  1. Android笔记(六十七) 自定义控件

    实际编程中,系统提供的控件往往无法满足我们的需求,一来是样子丑陋,二来是一些复杂的组合需要多次使用的话,每次都写一堆控件的组合会很耗费时间,所以我们将这些组件的组合自定义为一个新的控件,以后使用的时候 ...

  2. Android中自定义控件TextSize属性问题

    本文主要说明一个自定义控件添加TextSize属性的坑,刚刚从坑里面爬出来,写个随笔,记录一下: *************************************************** ...

  3. android版高仿淘宝客户端源码V2.3

    android版高仿淘宝客户端源码V2.3,这个版本我已经更新到2.3了,源码也上传到源码天堂那里了,大家可以看一下吧,该应用实现了我们常用的购物功能了,也就是在手机上进行网购的流程的,如查看产品(浏 ...

  4. Android笔记——Android中数据的存储方式(二)

    我们在实际开发中,有的时候需要储存或者备份比较复杂的数据.这些数据的特点是,内容多.结构大,比如短信备份等.我们知道SharedPreferences和Files(文本文件)储存这种数据会非常的没有效 ...

  5. Android笔记:触摸事件的分析与总结----TouchEvent处理机制

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://glblong.blog.51cto.com/3058613/1559320   ...

  6. [Android] Android 手机下 仿 微信 客户端 界面 -- 微聊

    Android 手机下 仿 微信 客户端 界面 -- 微聊 (包括聊天列表 + 聊天对话页 + 朋友圈列表页 + 我的/发现 列表页) 项目演示: 功能说明: 1)底部标签切换 (TabHost + ...

  7. Android 笔记之 R 文件

    Android笔记之R文件 h2{ color: #4abcde; } a{ color: blue; text-decoration: none; } a:hover{ color: red; te ...

  8. Android 笔记之 Android 系统架构

    Android笔记之Android系统架构 h2{ color: #4abcde; } a{ color: blue; text-decoration: none; } a:hover{ color: ...

  9. Android动画之仿美团加载数据等待时,小人奔跑进度动画对话框(附顺丰快递员奔跑效果)

    Android动画之仿美团加载数据等待时,小人奔跑进度动画对话框(附顺丰快递员奔跑效果) 首句依然是那句老话,你懂得! finddreams :(http://blog.csdn.net/finddr ...

随机推荐

  1. hive SQL优化之distribute by和sort by

    近期在优化hiveSQL. 以下是一段排序,分组后取每组第一行记录的SQL INSERT OVERWRITE TABLE t_wa_funnel_distinct_temp PARTITION (pt ...

  2. 【b702】字符串的展开

    Time Limit: 1 second Memory Limit: 50 MB [问题描述] 在初赛普及组的"阅读程序写结果"的问题中,我们曾给出一个字符串展开的例子:如果在输入 ...

  3. AE开发概念辨析

    樱木 原文 AE开发之概念辨析2,AE开发涉及相关概念,AE开发相关概念 1 AE中的类库 AE总共包括了21个子库,分别是SYSTEM,SYSTEMUI,GEOMETRY,DISPLAY,SERVE ...

  4. github-vimium-compile-crx

    chrome-62.x版本上安装vimium 1.61.1 https://blog.csdn.net/ZHUJIANWEILI4/article/details/78385346

  5. 学习金字塔 分类: T_TALENT 2014-05-21 09:25 331人阅读 评论(0) 收藏

    学习金字塔是美国缅因州的国家训练实验室研究成果,它用数字形式形象显示了:采用不同的学习方式,学习者在两周以后还能记住内容(平均学习保持率)的多少.它是一种现代学习方式的理论.最早它是由美国学者.著名的 ...

  6. TextView之一:子类的常用属性 分类: H1_ANDROID 2013-10-30 15:14 770人阅读 评论(0) 收藏

    TextView常见的子类包括EditText,Button,CheckBox, RadioButton等. 1.EditText EditText继承自TextView,因此TextView所有属性 ...

  7. 段的创建表user_segments 分类: H2_ORACLE 2013-08-10 11:13 714人阅读 评论(0) 收藏

    1.段的定义及类型 Oracle中的段(segment)是占用磁盘空间的一个对象,最常见的段类型包括: l  聚簇cluster l  表table l  表分区 tablepartition l  ...

  8. php面试题8

    php面试题8 一.总结 二.php面试题8 1.表单数据提交方式 POST 和 GET 的区别,URL 地址传递的数据最大长度是多少?$_GET 传参是请求 HTTP 协议通过 url 参数传递和接 ...

  9. jquery ajax跨域 thinkphp getjson

    jquery中post的应该是不能跨域,网上说get的可以跨域,但是我试了一下也不行,然后就进行最后的拼搏getjson,结果成功,哈哈 js处写作 $.getJSON( "/index.p ...

  10. event.relatedTarget、event.fromElement、event.toElement

    在标准DOM中,mouseover和mouseout所发生的元素可以通过event.target来访问,相关元素通过event.relatedTarget属性来访问.event.relatedTarg ...