最近在drrible上看到一个超酷炫的效果,立体圆柱缓慢上升:https://dribbble.com/shots/7077455-Spending-analytics
然后准备实现一波,做之前在网上找了很久,并没有相似的效果,所以自己做了一个,已经上传到我的代码库里:
https://github.com/jiangzhengnan/NguiLib
欢迎小伙伴们的start或者requests
下面简单说一下实现过程:
1.首先要讲传入的数据数组进行排序,因为是2d平面模拟3d,所以弧形的绘制要由内到外才不会出现错位的情况:
//计算出绘制顺序
private void computationOrder() {
ArrayList 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
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;
}
}
至此就介绍得差不多了,具体的代码里面都有注释,希望喜欢的小伙伴们点个赞,有问题可以留言。