前几天需要做一个评星的功能,于是先尝试了一下系统自带的RatingBar,但是使用的时候发现,在手机上很容易误点!比如我想点4星,结果很容易点中5星。另外一个问题是星星之间的间距是不能设置的,于是到GitHub上找,发现有的效果做得很好看,但是还是无法解决容易误点的问题,于是打算自定义一个。

先上一个效果图

需求

先说一下我的需要:

  • 不易引起误点操作
  • 支持修改颜色(选中和未选中的颜色)
  • 不需要像系统自带的RatingBar那样支持拖动
  • 支持设置星星总数和大小
  • 支持设置星星之间的间距

实现

有了需求之后就开始考虑实现的步骤:

  1. 创建自定义的属性
  2. 实现View的子类,重写onMeasure、onDraw等方法

首先是属性定义:在res/value目录下创建一个attrs.xml文件,并添加以下的属性,
解释一下starType属性,考虑到有的场景可能会用到心形,所以定义这个属性来决定评星是星星的形状还是心形。

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="SimpleRatingView"><attr name="starType" format="enum"><enum name="star" value="1"/><enum name="heart" value="2"/></attr><attr name="starSize" format="dimension" /><attr name="starSpacing" format="dimension" /><attr name="numStars" format="integer" /><attr name="rating" format="integer" /><attr name="primaryColor" format="color" /><attr name="secondaryColor" format="color" /></declare-styleable>
</resources>

创建一个类继承View类,重写onMeasure、onDraw方法:

 @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int paddingLeft = getPaddingLeft();int paddingRight = getPaddingRight();int paddingTop = getPaddingTop();int paddingBottom = getPaddingBottom();int width = (int) (paddingLeft + paddingRight + starSize * numStars + starSpacing * (numStars - 1));int height = (int) (paddingTop + paddingBottom + starSize);setMeasuredDimension(width, height);}@Overrideprotected void onDraw(Canvas canvas) {int paddingLeft = getPaddingLeft();int paddingTop = getPaddingTop();// 以24为基准做缩放float scale = starSize / 24;// 校正参数if (numStars <= 0) {throw new IllegalArgumentException("numStars不能小于等于0");}if (rating < 0) {rating = 0;}if (rating > numStars) {rating = numStars;}// 先绘制primaryColor的星星for (int i = 0; i < numStars; i++) {if (i < rating) {paint.setColor(primaryColor);} else {paint.setColor(secondaryColor);}canvas.save();canvas.translate(paddingLeft + (starSize + starSpacing) * i, paddingTop);path.reset();if (starType == STAR) {// 星星path.moveTo(scale * 12f, scale * 17.27f);path.lineTo(scale * 18.18f, scale * 21f);path.rLineTo(scale * -1.64f, scale * -7.03f);path.lineTo(scale * 22f, scale * 9.24f);path.rLineTo(scale * -7.19f, scale * -0.61f);path.lineTo(scale * 12f, scale * 2f);path.lineTo(scale * 9.19f, scale * 8.63f);path.lineTo(scale * 2f, scale * 9.24f);path.rLineTo(scale * 5.46f, scale * 4.73f);path.lineTo(scale * 5.82f, scale * 21f);path.close();} else if (starType == HEART) {// 心形path.moveTo(scale * 12f,scale * 21.35f);path.rLineTo(scale * -1.45f,scale * -1.32f);path.cubicTo(scale * 5.4f,scale * 15.36f,scale * 2f,scale * 12.28f,scale * 2f,scale * 8.5f);path.cubicTo(scale * 2f,scale * 5.42f, scale * 4.42f,scale * 3f, scale * 7.5f,scale * 3f);path.rCubicTo(scale * 1.74f,scale * 0f, scale * 3.41f,scale * 0.81f, scale * 4.5f,scale * 2.09f);path.cubicTo(scale * 13.09f,scale * 3.81f, scale * 14.76f,scale * 3f, scale * 16.5f,scale * 3f);path.cubicTo(scale * 19.58f,scale * 3f, scale * 22f,scale * 5.42f, scale * 22f,scale * 8.5f);path.rCubicTo(scale * 0f,scale * 3.78f, scale *  -3.4f,scale * 6.86f, scale *-8.55f,scale * 11.54f);path.lineTo(scale * 12f,scale * 21.35f);path.close();} else {}canvas.drawPath(path, paint);canvas.restore();}}

另外还需要重写一个重要的方法onTouchEvent,我们需要在这个方法中判断点击的位置,这样才能知道点中的是第几个星星,我们也可以在这个方法去设置点击的有效范围

 @Overridepublic boolean onTouchEvent(MotionEvent event) {float dx = event.getX();float dy = event.getY();int paddingLeft = getPaddingLeft();int paddingTop = getPaddingTop();int oldRating = rating;int newRating = rating;float dxx;boolean isClick = false;// 计算点击事件产生在哪个星星上if (dy > paddingTop && dy <= paddingTop + starSize) {for (int i = 1; i <= numStars; i++) {dxx = dx - paddingLeft - (starSize + starSpacing) * (i - 1);if (dxx > 0 && dxx < starSize) {newRating = i;isClick = true;break;}}}int action = event.getAction();switch (action) {case MotionEvent.ACTION_DOWN:if (isClick) {setRating(newRating);if (onRatingChangeListener != null) {onRatingChangeListener.onRatingChange(oldRating, newRating);}}break;case MotionEvent.ACTION_UP:performClick();break;}return true;}

最后再加上一个回调事件接口:

 public interface OnRatingChangeListener {void onRatingChange(int oldRating, int newRating);}

GitHub地址

该项目已经开源到GitHub上了,可以点击这里跳转

一个简单易用的RatingBar相关推荐

  1. idea log 不输出error_还在使用console.log()吗?Bunyan:一个简单易用的JS日志框架

    Bunyan是一个简单易用的JS日志框架,可以工作在多种环境下,这里以Nodejs为例说明Bunyan的基本用法.是时候替换console.log的写法了. 安装 npm install --save ...

  2. 基于ForkJoin构建一个简单易用的并发组件

    2019独角兽企业重金招聘Python工程师标准>>> 基于ForkJoin构建一个简单易用的并发组件 在实际的业务开发中,需要用到并发编程的知识,实际使用线程池来异步执行任务的场景 ...

  3. 开发一个简单易用的SDK的详细步骤(超详细,超适用)

    文章目录 开发一个简单易用的SDK的详细步骤 创建starter步骤 关键点 总结 开发一个简单易用的SDK的详细步骤 创建starter步骤 1.新建一个 spring boot 初始化项目 2.添 ...

  4. easyopen——一个简单易用的接口开放平台

    摘要: 一个简单易用的接口开放平台,平台封装了常用的参数校验.结果返回等功能,开发者只需实现业务代码即可. easyopen介绍 一个简单易用的接口开放平台,平台封装了常用的参数校验.结果返回等功能, ...

  5. d3.js 旋转图形_一个简单易用但功能强大的图形矢量化软件,扫描图片转换成CAD图的软件等等...

    背景简介 很多童鞋可能可能有想要把图片转成CAD能打开的格式,但是找不到软件,今天要分享的这个软件值得一试. 内容简介 AlgoLabR2VToolkit是一个将光栅图像转换为矢量图像的软件,转换后的 ...

  6. 如何选择一个简单易用的云桌面

    如何选择一个简单易用的企业级桌面云系统 废话少说,服务器虚拟化相信大家都熟悉了,也没啥难度了:但是桌面虚拟化就没那们简单.给你看一个Horizon View的系统架构,复杂不?反正一个新手,不折腾个把 ...

  7. python 消息队列 go_gmq: gmq是基于redis提供的特性,使用go语言开发的一个简单易用的消息队列;支持延迟任务,异步任务,超时任务,优先级任务...

    1. 概述 gmq是基于redis提供的特性,使用go语言开发的一个简单易用的队列;关于redis使用特性可以参考之前本人写过一篇很简陋的文章Redis 实现队列; gmq的灵感和设计是基于有赞延迟队 ...

  8. Qt怎么实现将bmp图片转换成Ascii_一个简单易用但功能强大的图形矢量化软件,扫描图片转换成CAD图的软件等等...

    背景简介 很多童鞋可能可能有想要把图片转成CAD能打开的格式,但是找不到软件,今天要分享的这个软件值得一试. 内容简介 AlgoLabR2VToolkit是一个将光栅图像转换为矢量图像的软件,转换后的 ...

  9. java通讯框架_gim: 一个简单易用,稳定高效的及时通讯框架(java、android)

    gim 一个简单易用,稳定高效的及时通讯框架(java) 简介: gim是基于高性能网络框架getty封装的,能简单上手,稳定高效的及时通讯框架 Getty:[https://github.com/g ...

最新文章

  1. Django celery6.4
  2. django 完整日志配置
  3. linux下命令行打开文件管理器
  4. 1-编程的基本条件和起步
  5. jQuery 事件用法详解
  6. golang panic的错误回收和简单的使用场景
  7. [Leedcode][JAVA][第9题][回文数][数学法]
  8. 【刷算法】字符串的全排列
  9. 安川机器人位置变量要素_安川机器人变量分配(维修)
  10. 区块链溯源系统架构---区块链工作笔记002
  11. oracle配置ipv6_配置 IPv6 接口
  12. jquery判断页面标签是否存在
  13. 没想到,这么简单的线程池用法,深藏这么多坑!
  14. vasp服务器中断,vasp优化结构没提示直接中断
  15. 动易cms5.0如何安装在远程服务器上,动易PowerEasy_SiteWeaver_CMS6.8安装教程
  16. Javascript回显图片
  17. java 其他文件转pdf_java 其他文件转成pdf java生成pdf
  18. 小米扫地机器人漏灰_#原创新人#一次失败的改装:MI 小米 扫地机器人 改装湿拖功能...
  19. linux下安装虚拟天文馆,【地理软件】虚拟天文馆——stellarium
  20. 输入一个四位数字x,分别求出x的个位数字、十位数字、百位数字和千位数字的值 ,并求个位、十位、百位、千位的和 -C语言

热门文章

  1. play(三) play实例项目开发Yabe 2前端页面
  2. JS代码在线优化工具
  3. 2019年牛市第一波技术指标选股神器组合源码
  4. qq修改实名认证已达上限_英雄联盟健康系统实名认证修订指引
  5. Tikmeta分享 |达人营销,你知道多少?
  6. CISA Cert Prep: 5 Information Asset Protection for IS Auditors CISA证书准备:5 IS审计员的信息资产保护 Lynda课程中文字幕
  7. WIN10不能安装S7-200的解决方法
  8. 【说说你和异性同桌干过的最浪漫的事 】 ----看到第176楼就突然沉默了....(节选自百度DotA吧)...
  9. VC++实现视频聊天:FFmpeg解码+SDL播放视频
  10. 【定义】矩阵初等变换和矩阵等价