首先贴上源码地址:https://github.com/CB-ysx/CBRatingBar
最近需要做一个星星的评分控件(可以调整进度,进度颜色渐变)
如图:
image image一开始想到的是用系统自带的RatingBar做,但发现了一个问题,实现颜色渐变有点复杂,而且有好几个页面都用了这个,总不能都这样写吧。刚好这段时间看了HenCoder写的Android自定义view系列文章,于是就想自己尝试下实现一个评分控件,可以实现图案的替换,渐变颜色,进度背景,图案个数,大小等参数的自己控制,经过几天的折腾,终于完成了这个控件CBRatingBar
先上效果图:
imageimage
image
image
gif效果图:
image如何使用:
Gradle
- 在项目的build.gradle中添加如下代码:
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
- 在项目的build.gradle中引入该库:
dependencies {
compile 'com.github.CB-ysx:CBRatingBar:2.0.1'
}
使用方法
- xml
<com.cb.ratingbar.CBRatingBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<com.cb.ratingbar.CBRatingBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:starSize="20dp"
app:starCount="5"
app:starSpace="10dp"
app:starStrokeWidth="1dp"
app:starCanTouch="true"
app:starMaxProgress="120"
app:starProgress="60"
app:starShowStroke="true"
app:starUseGradient="true"
app:starStartColor="#0000ff"
app:starEndColor="#00ff00"
app:starCoverColor="#ff0000"
app:starFillColor="#666666"
app:starPointCount="5"
app:starStrokeColor="#0f0f0f"
app:pathData="@string/bird"
app:pathDataId="@string/bird"/>
- java
cbRatingBar.setStarSize(20) //大小
.setStarCount(5) //数量
.setStarSpace(10) //间距
.setStarPointCount(5) //角数(n角星)
.setShowStroke(true) //是否显示边框
.setStarStrokeColor(Color.parseColor("#00ff00")) //边框颜色
.setStarStrokeWidth(5) //边框大小
.setStarFillColor(Color.parseColor("#00ff00")) //填充的背景颜色
.setStarCoverColor(Color.parseColor("#00ff00")) //填充的进度颜色
.setStarMaxProgress(120) //最大进度
.setStarProgress(50) //当前显示的进度
.setUseGradient(true) //是否使用渐变填充(如果使用则coverColor无效)
.setStartColor(Color.parseColor("#000000")) //渐变的起点颜色
.setEndColor(Color.parseColor("#ffffff")) //渐变的终点颜色
.setCanTouch(true) //是否可以点击
.setPathData(getResources().getString(R.string.pig))//传入path的数据
.setPathDataId(R.string.pig)//传入path数据id
.setDefaultPath()//设置使用默认path
.setPath(path)//传入path
.setOnStarTouchListener(new CBRatingBar.OnStarTouchListener() { //点击监听
@Override
public void onStarTouch(int touchCount) {
Toast.makeText(MainActivity.this, "点击第" + touchCount + "个星星", Toast.LENGTH_SHORT).show();
}
});
说明
pathData为svg文件中的path数据,如下:
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns:xlink="http://www.w3.org/1999/xlink" style="" class="icon" height="16" p-id="2384" t="1506306007922"
version="1.1" viewBox="0 0 1137 1024" width="17.765625" xmlns="http://www.w3.org/2000/svg">
<defs>
<style type="text/css"></style>
</defs>
<path
d="M800.653373 60.04085c-92.533023 0-174.90483 42.957261-228.601405 109.913074-53.696576-66.955813-136.068383-109.913074-228.601406-109.913074-161.838292 0-293.037297 131.199004-293.037297 293.037297 0 34.485875 6.284982 67.452386 17.216998 98.202847 82.149461 249.939217 470.047002 495.868793 504.429116 517.265896 34.374702-21.397103 422.272244-267.326679 504.421705-517.265896 10.932015-30.750461 17.216997-63.716972 17.216997-98.202847-0.007412-161.838292-131.206416-293.037297-293.044708-293.037297z"
fill="#E24B44" p-id="2385">
</path>
</svg>
以上path中"M800.653373 ... -293.037297z"这部分数据就是要提交给控件的pathData。
如何实现
为了有更好的扩展性,这我用了path数据来绘制图案,而非单独实现绘制星星,当然,一开始确实只是实现了绘制星星,而且是没有圆角效果的星星(设计图有圆角,先凑合着用吧,哈哈)。于是在网上找到了星星的绘制方法:
/**
* 获取星星的path
*
* @return
*/
private Path getStarPath(int dx) {
Path path = new Path();
float radius;
if (starPointCount % 2 == 0) {
radius = starSize * (cos(360.0 / starPointCount / 2) / 2 - sin(360.0 / starPointCount / 2) * sin(90 -
360.0 / starPointCount) / cos(90 - 360.0 / starPointCount));
} else {
radius = starSize * sin(360.0 / starPointCount / 4) / 2 / sin(180 - 360.0 / starPointCount / 2 - 360.0 /
starPointCount / 4);
}
for (int i = 0; i < starPointCount; ++i) {
if (i == 0) {
path.moveTo(starSize * cos(360 / starPointCount * i) / 2, starSize * sin(360 / starPointCount * i) /
2 + dx);
} else {
path.lineTo(starSize * cos(360.0 / starPointCount * i) / 2, starSize * sin(360.0 / starPointCount *
i) / 2 + dx);
}
path.lineTo(radius * cos(360.0 / starPointCount * i + 360.0 / starPointCount / 2), radius * sin(360.0 /
starPointCount * i + 360.0 / starPointCount / 2) + dx);
}
path.close();
return path;
}
其中dx可以先不用管,这是后面需要绘制多个星星用到的,这段代码就可以得到星星的path,至于为什么是这样计算,这我就没有去探究了(赶项目要紧),这段代码绘制出来的图案如下:
没错,只能绘制一个星星,那要如何绘制多个星星呢,这里就用到了dx了,dx是星星的偏移量(星星宽度+两个星星之间的间距),通过这个偏移量获取不同位置的星星path,再绘制到画布上就能实现多个星星了,代码如下:
canvas.translate(dx, dy);
canvas.rotate(-90);
int x = 0;
for (int i = 0; i < starCount; ++i) {
Path path = getStarPath(x);
canvas.drawPath(patpaint);
x += (starSize + starSpace);
}
canvas.rotate(90);
canvas.translate(-dx, -dy);
starCount就是星星的个数
starSize就是星星的大小
starSpace就是两个星星之间的间距
此时绘制出来的星星是填充了颜色的星星,但是还没有进度条,更没有渐变的进度条效果。
绘制效果如图:
接下来就是实现进度条,先实现纯色的进度条,本来绘制进度很简单的,但是由于星星属于不太规则的图案(虽然已经很规则了,但相对于矩形、圆形这些来说还是不规则的,哈哈,不要吐槽我),加上考虑到扩展性(可能使用的是其他真正不规则的图案),这里用了另一种方法来填充不规则图形。
代码如下:
//将星星绘制到star上
Bitmap star = Bitmap.createBitmap(width, starSizeBitmap.Config.ARGB_8888);
Canvas starCanvas = neCanvas(star);
drawStar(starCanvas, starFillPaint);
//在star上填充颜色
Bitmap finalStar = Bitmap.createBitmap(width, starSizeBitmap.Config.ARGB_8888);
Canvas canvas = neCanvas(finalStar);
canvas.save();
canvas.clipRect(0, 0, dx, starSize);
canvas.drawRect(0, 0, widthstarSize, starCoverPaint);
canvas.restore();
canvas.drawBitmap(star, 0, 0, starPaint);
先创建一个宽为width(由星星个数及间距计算得出)高为starSize(单个星星的大小)的Bitmap,然后把星星以未选中时的背景色绘制到Bitmap上,这时得到的就是没有任何进度的图。接下来是绘制进度,我们需要再创建一个Bitmap,然后使用clipRect方法裁剪高度为starSize,宽度为dx(进度)的矩形局域,然后填充进度的颜色(可以是纯色也可以是渐变色),最后把绘制了星星的bitmap绘制到这个画布上,采用PorterDuffXfermode的画笔,即可得到填充了进度的星星效果,如图:
设置画笔:
starPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
关于画笔的设置,可参考HenCoder的这篇文章HenCoder Android 开发进阶: 自定义 View 1-2 Paint 详解,不得不说,这几篇文章都写得很不错,对我来说,收获真的很多。
这样就完成了CBRatingBar的第一个版本。用在app上,又方便效果也还不错,不足的是只有一个图案,没办法自定义,于是就有了第二个版本,开发2.0.0版本虽然只是加多了自定义path数据,但是这一过程也是挺坎坷的。
研究png图片提取path数据---无果;
研究svg图片中的path数据---有点希望;
于是开始学习svg语法,自己实现将svg中的pathData转为Android绘图中的path数据,到了画弧线这些命令就卡住了,还有网上看了一些svg数据,发现其实挺坑的,有的用‘,’分割,有的用空格,感觉我的算法并不能很好地识别,又纠结了一段时间。最后在github发现了RichPath这个库,发现了该库中有实现从svg提取path的算法,于是就拿过来用了,在此感谢tarek360提供的算法。
有了这个剩下的就简单多了,加多几个方法,传入pathData数据,将之前获取星星的path方法,改为可以从pathData中提取,这样就实现了可以自定义图案,具体代码如下:
/**
* 初始化path
*
* @return
*/
private void initPath() {
if (pathData != null && !"".equals(pathData.trim().replace(" ", ""))) {
mPath = PathParserCompat.createPathFromPathData(pathData);
isSelfPath = true;
} else if (pathDataId != -1) {
mPath = PathParserCompat.createPathFromPathData(getResources().getString(pathDataId));
isSelfPath = true;
} else {
isSelfPath = false;
}
if (isSelfPath) {
resizePath(mPath, starSize, starSize);
}
}
其中的pathData就是原始数据,PathParserCompat就是用来将数据转为path的。由于提取出来的path是svg本来的大小,所以需要将它缩减为我们设置的大小,也就是用resizePath这个方法:
public void resizePath(Path path, float width, float height) {
RectF bounds = new RectF(0, 0, width, height);
RectF src = new RectF();
path.computeBounds(src, true);
Matrix resizeMatrix = new Matrix();
resizeMatrix.setRectToRect(src, bounds, Matrix.ScaleToFit.FILL);
path.transform(resizeMatrix);
}
这样我们就可以很方便地使用我们自己的图案来绘制了。
最后再贴上源码地址:https://github.com/CB-ysx/CBRatingBar
欢迎star~
关于svg语法,可查看W3C School
网友评论