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

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

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

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

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

package com.imxiaoyu.common.widget;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View; import java.util.ArrayList;
import java.util.Date;
import java.util.List; /**
* 仿遥控器上下左右ok圆形菜单
* Created by 庞光渝 on 2017/3/9.博客:https://my.oschina.net/u/1462828/blog
*/ public class RoundMenuView extends View { /**
* 变量
*/
private int coreX;//中心点的坐标X
private int coreY;//中心点的坐标Y private List<RoundMenu> roundMenus;//菜单列表 private boolean isCoreMenu = false;//是否有中心按钮
private int coreMenuColor;//中心按钮的默认背景--最好不要透明色
private int coreMenuStrokeColor;//中心按钮描边颜色
private int coreMenuStrokeSize;//中心按钮描边粗细
private int coreMenuSelectColor;//中心按钮选中时的背景颜色
private Bitmap coreBitmap;//OK图片
private OnClickListener onCoreClickListener;//中心按钮的点击回调 private float deviationDegree;//偏移角度
private int onClickState = -2;//-2是无点击,-1是点击中心圆,其他是点击菜单
private int roundRadius;//中心圆的半径
private double radiusDistance;//半径的长度比(中心圆半径=大圆半径*radiusDistance)
private long touchTime;//按下时间,抬起的时候判定一下,超过300毫秒算点击 public RoundMenuView(Context context) {
super(context);
} public RoundMenuView(Context context, AttributeSet attrs) {
super(context, attrs);
} public RoundMenuView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
} @Override
protected void onDraw(Canvas canvas) {
coreX = getWidth() / 2;
coreY = getHeight() / 2;
roundRadius = (int) (getWidth()/2 * radiusDistance);//计算中心圆圈半径 RectF rect = new RectF(0, 0, getWidth(), getHeight());
if (roundMenus != null && roundMenus.size() > 0) {
float sweepAngle = 360 / roundMenus.size();//每个弧形的角度
deviationDegree = sweepAngle / 2;//其实的偏移角度,如果4个扇形的时候是X形状,而非+,设为0试试就知道什么意思了
for (int i = 0; i < roundMenus.size(); i++) {
RoundMenu roundMenu = roundMenus.get(i);
//填充
Paint paint = new Paint();
paint.setAntiAlias(true);
if (onClickState == i) {
//选中
paint.setColor(roundMenu.selectSolidColor);
} else {
//未选中
paint.setColor(roundMenu.solidColor);
}
canvas.drawArc(rect, deviationDegree + (i * sweepAngle), sweepAngle, true, paint); //画描边
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(roundMenu.strokeSize);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(roundMenu.strokeColor);
canvas.drawArc(rect, deviationDegree + (i * sweepAngle), sweepAngle, roundMenu.useCenter, paint); //画图案
Matrix matrix = new Matrix();
matrix.postTranslate((float) ((coreX + getWidth() / 2 * roundMenu.iconDistance) - (roundMenu.icon.getWidth() / 2)), coreY - (roundMenu.icon.getHeight() / 2));
matrix.postRotate(((i + 1) * sweepAngle), coreX, coreY);
canvas.drawBitmap(roundMenu.icon, matrix, null);
}
} //画中心圆圈
if (isCoreMenu) {
//填充
RectF rect1 = new RectF(coreX - roundRadius, coreY - roundRadius, coreX + roundRadius, coreY + roundRadius);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(coreMenuStrokeSize);
if (onClickState == -1) {
paint.setColor(coreMenuSelectColor);
} else {
paint.setColor(coreMenuColor);
}
canvas.drawArc(rect1, 0, 360, true, paint); //画描边
paint = new Paint();
paint.setAntiAlias(true);
paint.setStrokeWidth(coreMenuStrokeSize);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(coreMenuStrokeColor);
canvas.drawArc(rect1, 0, 360, true, paint);
if (coreBitmap != null) {
//画中心圆圈的“OK”图标
canvas.drawBitmap(coreBitmap, coreX - coreBitmap.getWidth() / 2, coreY - coreBitmap.getHeight() / 2, null);//在 0,0坐标开始画入src
}
}
} public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchTime = new Date().getTime();
float textX = event.getX();
float textY = event.getY();
int distanceLine = (int) getDisForTwoSpot(coreX, coreY, textX, textY);//距离中心点之间的直线距离
if (distanceLine <= roundRadius) {
//点击的是中心圆;按下点到中心点的距离小于中心园半径,那就是点击中心园了
onClickState = -1;
} else if (distanceLine <= getWidth() / 2) {
//点击的是某个扇形;按下点到中心点的距离大于中心圆半径小于大圆半径,那就是点击某个扇形了
float sweepAngle = 360 / roundMenus.size();//每个弧形的角度
int angle = getRotationBetweenLines(coreX, coreY, textX, textY);
//这个angle的角度是从正Y轴开始,而我们的扇形是从正X轴开始,再加上偏移角度,所以需要计算一下
angle = (angle + 360 - 90 - (int) deviationDegree) % 360;
onClickState = (int) (angle / sweepAngle);//根据角度得出点击的是那个扇形
} else {
//点击了外面
onClickState = -2;
}
invalidate();
break;
case MotionEvent.ACTION_UP:
if ((new Date().getTime() - touchTime) < 300) {
//点击小于300毫秒算点击
OnClickListener onClickListener = null;
if (onClickState == -1) {
onClickListener = onCoreClickListener;
} else if (onClickState >= 0 && onClickState < roundMenus.size()) {
onClickListener = roundMenus.get(onClickState).onClickListener;
}
if (onClickListener != null) {
onClickListener.onClick(this);
}
}
onClickState = -2;
invalidate();
break;
}
return true;
} /**
* 添加菜单
*
* @param roundMenu
*/
public void addRoundMenu(RoundMenu roundMenu) {
if (roundMenu == null) {
return;
}
if (roundMenus == null) {
roundMenus = new ArrayList<>();
}
roundMenus.add(roundMenu);
invalidate();
} /**
* 添加中心菜单按钮
*
* @param coreMenuColor
* @param coreMenuSelectColor
* @param onClickListener
*/
public void setCoreMenu(int coreMenuColor, int coreMenuSelectColor, int coreMenuStrokeColor,
int coreMenuStrokeSize, double radiusDistance,Bitmap bitmap, OnClickListener onClickListener) {
isCoreMenu = true;
this.coreMenuColor = coreMenuColor;
this.radiusDistance = radiusDistance;
this.coreMenuSelectColor = coreMenuSelectColor;
this.coreMenuStrokeColor = coreMenuStrokeColor;
this.coreMenuStrokeSize = coreMenuStrokeSize;
coreBitmap = bitmap;
this.onCoreClickListener = onClickListener;
invalidate();
} /**
* 获取两条线的夹角
*
* @param centerX
* @param centerY
* @param xInView
* @param yInView
* @return
*/
public static int getRotationBetweenLines(float centerX, float centerY, float xInView, float yInView) {
double rotation = 0; double k1 = (double) (centerY - centerY) / (centerX * 2 - centerX);
double k2 = (double) (yInView - centerY) / (xInView - centerX);
double tmpDegree = Math.atan((Math.abs(k1 - k2)) / (1 + k1 * k2)) / Math.PI * 180; if (xInView > centerX && yInView < centerY) { //第一象限
rotation = 90 - tmpDegree;
} else if (xInView > centerX && yInView > centerY) //第二象限
{
rotation = 90 + tmpDegree;
} else if (xInView < centerX && yInView > centerY) { //第三象限
rotation = 270 - tmpDegree;
} else if (xInView < centerX && yInView < centerY) { //第四象限
rotation = 270 + tmpDegree;
} else if (xInView == centerX && yInView < centerY) {
rotation = 0;
} else if (xInView == centerX && yInView > centerY) {
rotation = 180;
}
return (int) rotation;
} /**
* 求两个点之间的距离
*
* @return
*/
public static double getDisForTwoSpot(float x1, float y1, float x2, float y2) {
float width, height;
if (x1 > x2) {
width = x1 - x2;
} else {
width = x2 - x1;
} if (y1 > y2) {
height = y2 - y1;
} else {
height = y2 - y1;
}
return Math.sqrt((width * width) + (height * height));
} /**
* 扇形的对象类
*/
public static class RoundMenu {
public boolean useCenter = true;//扇形是否画连接中心点的直线
public int solidColor = 0x00000000;//背景颜色,默认透明
public int selectSolidColor = 0x00000000;//背景颜色,默认透明
public int strokeColor = 0x00000000;//描边颜色,默认透明
public int strokeSize = 1;//描边的宽度,默认1
public Bitmap icon;//菜单的图片
public OnClickListener onClickListener;//点击监听
public double iconDistance = 0.63;//图标距离中心点的距离
}
}

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

RoundMenuView.RoundMenu roundMenu = new RoundMenuView.RoundMenu();
roundMenu.selectSolidColor = ColorUtils.getColor(getActivity(), R.color.gray_9999);
roundMenu.strokeColor = ColorUtils.getColor(getActivity(), R.color.gray_9999);
roundMenu.icon=ImageUtils.drawable2Bitmap(getActivity(),R.drawable.ic_right);
roundMenu.onClickListener=new OnClickListener() {
@Override
public void onClick(View view) {
ToastUtils.showToast(getActivity(),"点击了1");
}
};
roundMenuView.addRoundMenu(roundMenu); roundMenu = new RoundMenuView.RoundMenu();
roundMenu.selectSolidColor = ColorUtils.getColor(getActivity(), R.color.gray_9999);
roundMenu.strokeColor = ColorUtils.getColor(getActivity(), R.color.gray_9999);
roundMenu.icon=ImageUtils.drawable2Bitmap(getActivity(),R.drawable.ic_right);
roundMenu.onClickListener=new OnClickListener() {
@Override
public void onClick(View view) {
ToastUtils.showToast(getActivity(),"点击了2");
}
};
roundMenuView.addRoundMenu(roundMenu); roundMenu = new RoundMenuView.RoundMenu();
roundMenu.selectSolidColor = ColorUtils.getColor(getActivity(), R.color.gray_9999);
roundMenu.strokeColor = ColorUtils.getColor(getActivity(), R.color.gray_9999);
roundMenu.icon=ImageUtils.drawable2Bitmap(getActivity(),R.drawable.ic_right);
roundMenu.onClickListener=new OnClickListener() {
@Override
public void onClick(View view) {
ToastUtils.showToast(getActivity(),"点击了3");
}
};
roundMenuView.addRoundMenu(roundMenu); roundMenu = new RoundMenuView.RoundMenu();
roundMenu.selectSolidColor = ColorUtils.getColor(getActivity(), R.color.gray_9999);
roundMenu.strokeColor = ColorUtils.getColor(getActivity(), R.color.gray_9999);
roundMenu.icon=ImageUtils.drawable2Bitmap(getActivity(),R.drawable.ic_right);
roundMenu.onClickListener=new OnClickListener() {
@Override
public void onClick(View view) {
ToastUtils.showToast(getActivity(),"点击了4");
}
};
roundMenuView.addRoundMenu(roundMenu); roundMenuView.setCoreMenu(ColorUtils.getColor(getActivity(), R.color.gray_f2f2),
ColorUtils.getColor(getActivity(), R.color.gray_9999), ColorUtils.getColor(getActivity(), R.color.gray_9999)
, 1, 0.43,ImageUtils.drawable2Bitmap(getActivity(),R.drawable.ic_ok), new OnClickListener() {
@Override
public void onClick(View view) {
ToastUtils.showToast(getActivity(),"点击了中心圆圈");
}
});

 

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. Ubuntu 16.04/16.10安装 KDE Plasma

    KDE目前的最新版本是Plasma 5.7(2016-10).Ubuntu默认的桌面环境是Unity,下面记录怎么在Ubuntu 16.04/16.10上安装KDE. Ubuntu 16.04/16. ...

  2. 如何使用google地图的api(整理)

    如何使用google地图的api(整理) 一.总结 一句话总结:直接用script标签引google地图api即可. 1.如何使用google地图的api? 页面引用javascript文件<s ...

  3. php课程 8-28 php如何绘制生成显示图片

    php课程 8-28 php如何绘制生成显示图片 一.总结 一句话总结:gd库轻松解决 1.php图片操作生成的图的两种去向是什么? 一种在页面直接输出,一种存进本地磁盘 2.php操作图片的库有哪些 ...

  4. linux系统进程的查看与控制

    原文:linux系统进程的查看与控制 一.什么是进程? 进程就是系统未完成并且正在进行的工作. 二.查看系统进程 1.图形方式查看 gnome-system-monitor 2.进程查看命令 ps - ...

  5. 【HDU】病毒侵袭(AC自己主动机模板题)

    AC自己主动机的模板题.因为输入的字符串中的字符不保证全为小写字母.所以范围应该在130之前,而前31位字符是不可能出如今字符串的(不懂得查下ACSII表即可了).所以仅仅须要开的结点数组大小为130 ...

  6. [React] Create & Deploy a Universal React App using Zeit Next

    In this lesson, we'll use next to create a universal React application with no configuration. We'll ...

  7. oracle 列授权相关测试

    create tablespace liangtbs datafile '/home/oradata/lgjdb/liangtbs01.dbf' size 50m autoextend on;crea ...

  8. php 过滤js字符串代码

    $str = '<script type="text/javascript" src="dd.js"></script> 测试php正则 ...

  9. 用CMake代替makefile进行跨平台交叉编译

    在开始介绍如何使用CMake编译跨平台的静态库之前,先讲讲我在没有使用CMake之前所趟过的坑.因为很多开源的程序,比如png,都是自带编译脚本的.我们可以使用下列脚本来进行编译: 1 2 3 ./c ...

  10. php课程 5-18 数组排序和合并拆分函数有哪些

    php课程  5-18   数组排序和合并拆分函数有哪些 一.总结 一句话总结:分类来记.这些函数自己都可以写,费点时间而已. 1.array_combine()和array_merge()的区别是什 ...