Android仿支付宝上芝麻信用分雷达图
程序员文章站
2024-03-04 12:04:17
一、首先看下支付宝上芝麻信用分的效果图:
二、思路
1、确定雷达图中心点坐标
&nb...
一、首先看下支付宝上芝麻信用分的效果图:
二、思路
1、确定雷达图中心点坐标
2、绘制多边形及连接线
3、根据维度值绘制覆盖区域
4、绘制分数
5、绘制每个维度的标题文字和图标
三、实现
获取布局的中心坐标
在onsizechanged(int w, int h, int oldw, int oldh)
方法里面,根据view的长宽,计算出雷达图的半径(这里取布局宽高最小值的四分之一,可以自定义),获取整个布局的中心坐标。
public class creditscoreview extends view { //数据个数 private int datacount = 5; //每个角的弧度 private float radian = (float) (math.pi * 2 / datacount); //雷达图半径 private float radius; //中心x坐标 private int centerx; //中心y坐标 private int centery; //各维度标题 private string[] titles = {"履约能力", "信用历史", "人脉关系", "行为偏好", "身份特质"}; //各维度图标 private int[] icons = {r.mipmap.ic_performance, r.mipmap.ic_history, r.mipmap.ic_contacts, r.mipmap.ic_predilection, r.mipmap.ic_identity}; //各维度分值 private float[] data = {170, 180, 160, 170, 180}; //数据最大值 private float maxvalue = 190; //雷达图与标题的间距 private int radarmargin = densityutils.dp2px(getcontext(), 15); //雷达区画笔 private paint mainpaint; //数据区画笔 private paint valuepaint; //分数画笔 private paint scorepaint; //标题画笔 private paint titlepaint; //图标画笔 private paint iconpaint; //分数大小 private int scoresize = densityutils.dp2px(getcontext(), 28); //标题文字大小 private int titlesize = densityutils.dp2px(getcontext(), 13); ... @override protected void onsizechanged(int w, int h, int oldw, int oldh) { //雷达图半径 radius = math.min(h, w) / 2 * 0.5f; //中心坐标 centerx = w / 2; centery = h / 2; postinvalidate(); super.onsizechanged(w, h, oldw, oldh); } ... }
绘制多边形和连接线
主要看下getpoint
方法,此方法封装了获取雷达图上各个点坐标的计算逻辑。
/** * 绘制多边形 * * @param canvas 画布 */ private void drawpolygon(canvas canvas) { path path = new path(); for (int i = 0; i < datacount; i++) { if (i == 0) { path.moveto(getpoint(i).x, getpoint(i).y); } else { path.lineto(getpoint(i).x, getpoint(i).y); } } //闭合路径 path.close(); canvas.drawpath(path, mainpaint); } /** * 绘制连接线 * * @param canvas 画布 */ private void drawlines(canvas canvas) { path path = new path(); for (int i = 0; i < datacount; i++) { path.reset(); path.moveto(centerx, centery); path.lineto(getpoint(i).x, getpoint(i).y); canvas.drawpath(path, mainpaint); } }
getpoint
方法,参数radarmargin
与percent
在此步骤赋予默认值。
/** * 获取雷达图上各个点的坐标 * * @param position 坐标位置(右上角为0,顺时针递增) * @return 坐标 */ private point getpoint(int position) { return getpoint(position, 0, 1); } /** * 获取雷达图上各个点的坐标(包括维度标题与图标的坐标) * * @param position 坐标位置 * @param radarmargin 雷达图与维度标题的间距 * @param percent 覆盖区的的百分比 * @return 坐标 */ private point getpoint(int position, int radarmargin, float percent) { int x = 0; int y = 0; if (position == 0) { x = (int) (centerx + (radius + radarmargin) * math.sin(radian) * percent); y = (int) (centery - (radius + radarmargin) * math.cos(radian) * percent); } else if (position == 1) { x = (int) (centerx + (radius + radarmargin) * math.sin(radian / 2) * percent); y = (int) (centery + (radius + radarmargin) * math.cos(radian / 2) * percent); } else if (position == 2) { x = (int) (centerx - (radius + radarmargin) * math.sin(radian / 2) * percent); y = (int) (centery + (radius + radarmargin) * math.cos(radian / 2) * percent); } else if (position == 3) { x = (int) (centerx - (radius + radarmargin) * math.sin(radian) * percent); y = (int) (centery - (radius + radarmargin) * math.cos(radian) * percent); } else if (position == 4) { x = centerx; y = (int) (centery - (radius + radarmargin) * percent); } return new point(x, y); }
多边形和连接线
绘制覆盖区域
/** * 绘制覆盖区域 * * @param canvas 画布 */ private void drawregion(canvas canvas) { path path = new path(); for (int i = 0; i < datacount; i++) { //计算百分比 float percent = data[i] / maxvalue; int x = getpoint(i, 0, percent).x; int y = getpoint(i, 0, percent).y; if (i == 0) { path.moveto(x, y); } else { path.lineto(x, y); } } //绘制填充区域的边界 path.close(); valuepaint.setstyle(paint.style.stroke); canvas.drawpath(path, valuepaint); //绘制填充区域 valuepaint.setstyle(paint.style.fill_and_stroke); canvas.drawpath(path, valuepaint); }
覆盖区域
绘制分数
/** * 绘制分数 * * @param canvas 画布 */ private void drawscore(canvas canvas) { int score = 0; //计算总分 for (int i = 0; i < datacount; i++) { score += data[i]; } canvas.drawtext(score + "", centerx, centery + scoresize / 2, scorepaint); }
分数
绘制标题
/** * 绘制标题 * * @param canvas 画布 */ private void drawtitle(canvas canvas) { for (int i = 0; i < datacount; i++) { int x = getpoint(i, radarmargin, 1).x; int y = getpoint(i, radarmargin, 1).y; bitmap bitmap = bitmapfactory.decoderesource(getresources(), icons[i]); int iconheight = bitmap.getheight(); float titlewidth = titlepaint.measuretext(titles[i]); //底下两个角的坐标需要向下移动半个图片的位置(1、2) if (i == 1) { y += (iconheight / 2); } else if (i == 2) { x -= titlewidth; y += (iconheight / 2); } else if (i == 3) { x -= titlewidth; } else if (i == 4) { x -= titlewidth / 2; } canvas.drawtext(titles[i], x, y, titlepaint); } }
标题
绘制图标
/** * 绘制图标 * * @param canvas 画布 */ private void drawicon(canvas canvas) { for (int i = 0; i < datacount; i++) { int x = getpoint(i, radarmargin, 1).x; int y = getpoint(i, radarmargin, 1).y; bitmap bitmap = bitmapfactory.decoderesource(getresources(), icons[i]); int iconwidth = bitmap.getwidth(); int iconheight = bitmap.getheight(); float titlewidth = titlepaint.measuretext(titles[i]); //上面获取到的x、y坐标是标题左下角的坐标 //需要将图标移动到标题上方居中位置 if (i == 0) { x += (titlewidth - iconwidth) / 2; y -= (iconheight + gettextheight(titlepaint)); } else if (i == 1) { x += (titlewidth - iconwidth) / 2; y -= (iconheight / 2 + gettextheight(titlepaint)); } else if (i == 2) { x -= (iconwidth + (titlewidth - iconwidth) / 2); y -= (iconheight / 2 + gettextheight(titlepaint)); } else if (i == 3) { x -= (iconwidth + (titlewidth - iconwidth) / 2); y -= (iconheight + gettextheight(titlepaint)); } else if (i == 4) { x -= iconwidth / 2; y -= (iconheight + gettextheight(titlepaint)); } canvas.drawbitmap(bitmap, x, y, titlepaint); } } /** * 获取文本的高度 * * @param paint 文本绘制的画笔 * @return 文本高度 */ private int gettextheight(paint paint) { paint.fontmetrics fontmetrics = paint.getfontmetrics(); return (int) (fontmetrics.descent - fontmetrics.ascent); }
图标
总结
好了,到这里主要的绘制工作就完成了,有些图标实在找不到,就用相似的代替了。希望这篇文章的内容对各位android开发者们能有所帮助,如果有疑问大家可以留言交流。