最近在drrible上看到一个超酷炫的效果,立体圆柱缓慢上升:https://dribbble.com/shots/7077455-Spending-analytics
然后准备实现一波,做之前在网上找了很久,并没有相似的效果,所以自己做了一个,已经上传到我的代码库里:
https://github.com/jiangzhengnan/NguiLib
欢迎小伙伴们的start或者requests
下面简单说一下实现过程:
1.首先要讲传入的数据数组进行排序,因为是2d平面模拟3d,所以弧形的绘制要由内到外才不会出现错位的情况:
-
//计算出绘制顺序
-
private void computationOrder() {
-
ArrayList<Entry> tempOrderList =
new ArrayList<>();
-
leftAngle =
0;
-
rightAngle =
0;
-
for (
int i =
0; i < max; i++) {
-
-
Collections.sort(mEntries,
new MyCompare());
-
Entry tempEntry = mEntries.get(
0);
-
float halfAngle = tempEntry.percent /
2;
-
/*
-
1.算法,先绘制最大的一块,居中
-
2.剩下的 优先记录左边和右边的坐标值
-
3.优先添加不会溢出的
-
*/
-
if (i ==
0) {
-
leftAngle =
270f - halfAngle;
-
rightAngle =
270f + halfAngle;
-
if (rightAngle >=
360) {
-
rightAngle -=
360f;
-
}
-
tempEntry.tag = CENTER;
-
tempEntry.startAngle = leftAngle;
-
mEntries.remove(
0);
-
}
else {
-
//1.找到当前小的边
-
if (getDistanceToCenter(leftAngle, LEFT) > getDistanceToCenter(rightAngle, RIGHT)) {
-
//左边比右边大,取右边
-
tempEntry = getNextAngle(getDistanceToCenter(rightAngle, RIGHT));
-
tempEntry.tag = RIGHT;
-
-
rightAngle += tempEntry.percent;
-
if (rightAngle >=
360) {
-
rightAngle -=
360f;
-
}
-
tempEntry.startAngle = rightAngle;
-
}
else {
-
//右边比左边大,取左边
-
tempEntry = getNextAngle(getDistanceToCenter(leftAngle, LEFT));
-
tempEntry.tag = LEFT;
-
-
leftAngle -= tempEntry.percent;
-
tempEntry.startAngle = leftAngle;
-
}
-
}
-
tempOrderList.add(tempEntry);
-
}
-
mEntrySourceList = tempOrderList;
-
}
2.然后是绘制部分,首先根据排列的顺序进行绘制,依次绘制各个弧形段,然后画轮廓线:
-
private void drawCylinder(Canvas canvas, Entry tempEntry, int thickness,boolean ifChangeThick) {
-
mainPaint.setStyle(Paint.Style.FILL);
-
//绘制各个弧度
-
int perThickness =ifChangeThick? (
int) ((tempEntry.percent /
360f) * thickness * (max *
0.5f)) : thickness;
-
float drawTempStartAngle =
0f;
-
RectF tempRectF;
-
float lineStartX =
0f;
-
float lineStartY =
0f;
-
float lineEndX =
0f;
-
float lineEndY =
0f;
-
float oX = centerX;
-
float oY = (area2DHeight + area3DHight) /
2;
-
float R = centerX;
-
//y轴2d3d缩放比例
-
float bilv = ((
float) (area3DHight - area2DHeight)) / ((
float) area2DHeight);
-
for (
int j =
0; j <= perThickness; j++) {
-
tempRectF =
new RectF(
0, area2DHeight - j, area2DWidth, area3DHight - j);
-
switch (tempEntry.tag) {
-
case CENTER:
-
drawTempStartAngle = tempEntry.startAngle;
-
lineStartX = oX;
-
lineEndX = oX;
-
lineStartY = (area2DHeight + area3DHight) /
2;
-
lineEndY = oY - j;
-
break;
-
case LEFT:
-
drawTempStartAngle = tempEntry.startAngle;
-
/*
-
左边夹角tempAngle 0< tempAngle < 180
-
90 <drawTempStartAngle <270
-
*/
-
if (drawTempStartAngle <=
180) {
-
//startAngle 90-180
-
lineStartX = oX - (
float) (R * Math.sin(Math.toRadians(drawTempStartAngle -
90f)));
-
lineEndX = lineStartX;
-
lineStartY = oY + (
float) (bilv * R * Math.cos(Math.toRadians(drawTempStartAngle -
90f)));
-
lineEndY = lineStartY - j;
-
-
}
else {
-
//startAngle 180-270
-
lineStartX = oX - (
float) (R * Math.cos(Math.toRadians(drawTempStartAngle -
180f)));
-
lineEndX = lineStartX;
-
lineStartY = oY - (
float) (bilv * R * Math.sin(Math.toRadians(drawTempStartAngle -
180f)));
-
lineEndY = lineStartY - j;
-
}
-
break;
-
case RIGHT:
-
drawTempStartAngle = tempEntry.startAngle - tempEntry.percent;
-
/*
-
右边夹角tempAngle 0< tempAngle < 180
-
90 <drawTempStartAngle <270
-
*/
-
if (drawTempStartAngle <=
360) {
-
//startAngle 270-360
-
lineStartX = oX + (
float) (R * Math.cos(Math.toRadians(
360f - drawTempStartAngle - tempEntry.percent)));
-
lineEndX = lineStartX;
-
lineStartY = oY - (
float) (bilv * R * Math.sin(Math.toRadians(
360f - drawTempStartAngle - tempEntry.percent)));
-
lineEndY = lineStartY - j;
-
-
}
else {
-
//startAngle 0-90
-
lineStartX = oX + (
float) (R * Math.cos(Math.toRadians(drawTempStartAngle)));
-
lineEndX = lineStartX;
-
lineStartY = oY - (
float) (bilv * R * Math.sin(Math.toRadians(drawTempStartAngle)));
-
lineEndY = lineStartY - j;
-
}
-
break;
-
}
-
-
//弧形
-
mainPaint.setColor(tempEntry.color);
-
-
-
canvas.drawArc(tempRectF, drawTempStartAngle, tempEntry.percent,
true, mainPaint);
-
if (j == perThickness) {
-
drawTopLine(canvas, tempRectF, drawTempStartAngle, tempEntry.percent);
-
}
-
//竖直线
-
mainPaint.setColor(Color.WHITE);
-
if (tempEntry.tag == CENTER) {
-
canvas.drawLine(lineStartX, lineStartY,
-
lineEndX, lineEndY,
-
mainPaint);
-
//还需要画左右两边的竖直线
-
float xOffset = (
float) (R * Math.sin(Math.toRadians(tempEntry.percent /
2)));
-
float yOffset = (
float) (bilv * R * Math.cos(Math.toRadians(tempEntry.percent /
2)));
-
canvas.drawLine(
-
oX - xOffset,
-
oY - yOffset,
-
oX - xOffset,
-
oY - yOffset - j,
-
mainPaint);
-
canvas.drawLine(
-
oX + xOffset,
-
oY - yOffset,
-
oX + xOffset,
-
oY - yOffset - j,
-
mainPaint);
-
}
else {
-
canvas.drawLine(lineStartX, lineStartY,
-
lineEndX, lineEndY,
-
mainPaint);
-
}
-
}
-
}
3.两种不同的动画效果在ondraw里面分别进行判断:
-
@SuppressLint(
"DrawAllocation")
-
@Override
-
protected void onDraw(Canvas canvas) {
-
super.onDraw(canvas);
-
switch (ANIM_STATE) {
-
case ANIM_STATE_ALL:
-
for (
int i =
0; i < mEntrySourceList.size(); i++) {
-
Entry tempEntry = mEntrySourceList.get(i);
-
drawCylinder(canvas, tempEntry, thickness,
true);
-
}
-
break;
-
case ANIM_STATE_SINGLE:
-
for (
int i =
0; i < mEntrySourceList.size(); i++) {
-
Entry tempEntry = mEntrySourceList.get(i);
-
drawCylinder(canvas, tempEntry,
1,
false);
-
}
-
-
for (
int i =
0; i < singleAnimIndex; i++) {
-
if (i < mEntrySourceList.size()) {
-
-
Entry tempEntry = mEntrySourceList.get(i);
-
int tempThickNess = singleAnimValue - (i +
1) *
100;
-
LogUtils.INSTANCE.d(
"tempThickNess: " + tempThickNess);
-
-
tempThickNess = (
int) ((tempEntry.percent /
360f) * tempThickNess * (max *
0.33f));
-
-
drawCylinder(canvas, tempEntry, tempThickNess,
false);
-
}
-
}
-
-
break;
-
case ANIM_STATE_CHANGGE:
-
-
break;
-
}
-
}
至此就介绍得差不多了,具体的代码里面都有注释,希望喜欢的小伙伴们点个赞,有问题可以留言。
转载:https://blog.csdn.net/qq_22770457/article/details/103655337
查看评论