Android-自定义能力分布图

一、上图镇楼:

自定义能力分布图
在医生的微信群里面看到医生发的能力分布图,想实现一下,于是花了两个小时写了出来,由于gif录制原因,所以棱角效果不明显,请自行运行至手机查看实际效果。话不多说,上源码。

二、源代码

package com.lxh.custom.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Shader;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

/**
 * Created by lxh on 2017/7/14.
 * QQ-632671653
 */

public class AbilityMapView extends View {

    //能力层级
    private int LAYER = 3;
    //能力数组
    private String[] ability;
    //各能力分值
    private int[] abilityScore;
    //各能力满分分值
    private int abilityFullMark = 100;
    //View宽度
    private int viewWidth;
    //View高度
    private int viewHeight;
    //中心x坐标
    private int centerX;
    //中心Y坐标
    private int centerY;
    //padding值
    private int padding;
    //大圆半径
    private int Radius;
    //各项能力颜色值
    private int[] colors = {Color.BLUE,Color.BLACK,Color.CYAN,Color.GREEN,Color.MAGENTA};
    //程序上下文
    private Context context;
    public AbilityMapView(Context context) {
        super(context);
        this.context = context;
    }

    public AbilityMapView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        viewWidth = MeasureSpec.getSize(widthMeasureSpec);
        viewHeight = MeasureSpec.getSize(heightMeasureSpec);
        padding = dip2px(70);
        if (viewWidth<viewHeight){
            viewHeight = viewWidth;
        }else {
            viewWidth = viewHeight;
        }
        setMeasuredDimension(viewWidth,viewHeight);
        centerX = viewWidth / 2;
        centerY = viewWidth / 2;
        Radius = viewWidth / 2 - padding;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint = new Paint();
        drawLayer(canvas,paint,LAYER);
        if (ability.length>0){
            drawAbilityLine(canvas,paint,ability.length);
            if (ability.length != colors.length){
                Log.e("AbilityMapView","色值数量与能力数量不匹配!");
                return;
            }
            if (ability.length != abilityScore.length){
                Log.e("AbilityMapView","分数值数量与能力数量不匹配!");
                return;
            }
            drawAbilityMap(canvas,paint);
        }

    }

    /**
     * 绘制文字
     * @param canvas
     * @param x
     * @param y
     * @param text
     * @param color
     * @param paint
     */
    private void drawText(Canvas canvas,float x,float y,String text,int color,Paint paint){
        paint.reset();
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
        paint.setColor(color);
        //绘制圆点标记
        canvas.drawCircle(x,y,dip2px(3),paint);
        paint.setTextSize(dip2px(10));
        Rect rect = new Rect();
        paint.getTextBounds(text, 0, text.length(), rect);
        int w = rect.width(); //获取宽度
        int h = rect.height();//获取高度
        float textX = 0;
        float textY = 0;
        //坐标点位于右上
        if (x>=centerX&&y<centerY){
            textX = x+dip2px(5);
            textY = y;
        }
        //坐标点位于右下
        if (x>=centerX&&y>=centerY){
            textX = x;
            textY = y+h+dip2px(5);
        }
        //坐标点位于左下
        if (x<centerX&&y>centerY){
            textX = x-w-dip2px(5);
            textY = y+h+dip2px(5);
        }
        //坐标点位于左上
        if (x<centerX&&y<=centerY){
            textX = x-w-dip2px(5);
            textY = y-h;
        }
        canvas.drawText(text,textX,textY,paint);
    }

    /**
     * 绘制能力分布
     * @param canvas
     * @param paint
     */
    private void drawAbilityMap(Canvas canvas,Paint paint){
        int angle = 360 / ability.length;//两种能力虚线之间的夹角
        paint.setColor(Color.parseColor("#FF435AD7"));
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
        for (int i = 0;i<ability.length;i++){
            Path path = new Path();
            path.moveTo(centerX,centerY);
            float radius = (abilityScore[i]*Radius)/abilityFullMark;//计算每个分值在大圆半径上的长度
            //计算分值在能力虚线上的坐标
            float tempX  = (float) (centerX + radius * Math.cos(2 * Math.PI * (- 90+angle*i) / 360));
            float tempY = (float) (centerY + radius * Math.sin(2 * Math.PI * (- 90+angle*i) / 360));
            path.lineTo(tempX,tempY);
            int index = i+1;
            if (index>=abilityScore.length){
                index = 0;
            }
            float endRadius = (abilityScore[index]*Radius)/abilityFullMark;//计算每个分值在大圆半径上的长度
            //计算分值在能力虚线上的坐标
            float endX  = (float) (centerX + endRadius * Math.cos(2 * Math.PI * (- 90+angle*(index)) / 360));
            float endY = (float) (centerY + endRadius * Math.sin(2 * Math.PI * (- 90+angle*(index)) / 360));
            //构造线性渐变
            LinearGradient lg=new LinearGradient(tempX,tempY,endX,endY,Color.parseColor("#FFDF9C28"),
                    Color.parseColor("#FFECBB67"), Shader.TileMode.MIRROR);
            paint.setShader(lg);
            path.lineTo(endX,endY);
            path.close();
            canvas.drawPath(path,paint);
            //计算标记点坐标
            float flagX = (float) (centerX + (Radius+30) * Math.cos(2 * Math.PI * (- 90+angle*(i)) / 360));
            float flagY = (float) (centerY + (Radius+30) * Math.sin(2 * Math.PI * (- 90+angle*(i)) / 360));
            drawText(canvas,flagX,flagY,ability[i]+" "+abilityScore[i],colors[i],paint);
        }
    }

    /**
     * 绘制层级圆
     * @param canvas
     * @param paint
     * @param layer
     */
    private void drawLayer(Canvas canvas, Paint paint,int layer){
        int radius;
        paint.setColor(Color.parseColor("#FFcccccc"));
        paint.setStrokeWidth(dip2px(1));
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        for(int i = 1;i<=layer;i++){
            radius = (Radius/layer)*i;
            canvas.drawCircle(centerX,centerY,radius,paint);
        }
    }

    /**
     * 绘制能力虚线
     * @param canvas
     * @param paint
     * @param abilityNumber
     */
    private void drawAbilityLine(Canvas canvas,Paint paint,int abilityNumber){
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(2);
        paint.setColor(Color.parseColor("#FFcccccc"));
        paint.setAntiAlias(true);//抗锯齿
        paint.setPathEffect(new DashPathEffect(new float[] {15, 15}, 0));//设置虚线15sp实线,15sp虚线
        int angle = 360 / abilityNumber;//两种能力虚线之间的夹角
        float X1;//能力虚线在大圆上的X坐标
        float Y1;//能力虚线在大圆上的Y坐标
        for(int i = 0;i<abilityNumber;i++){
            X1 = (float) (centerX + Radius * Math.cos(2 * Math.PI * (- 90+angle*i) / 360));
            Y1 = (float) (centerY + Radius * Math.sin(2 * Math.PI * (- 90+angle*i) / 360));
            Path mPath = new Path();
            mPath.reset();
            mPath.moveTo(centerX, centerY);//设置中心点为虚线路径起点
            mPath.lineTo(X1, Y1);//设置圆上坐标点为虚线路径终点
            canvas.drawPath(mPath, paint);
        }
    }

    //设置能力分级
    public void setLayer(int layer){
        this.LAYER = layer;
        postInvalidate();
    }

    public void setAbility(String[] ability) {
        this.ability = ability;
        postInvalidate();
    }

    public void setAbilityScore(int[] abilityScore) {
        this.abilityScore = abilityScore;
        postInvalidate();
    }

    public void setAbilityFullMark(int abilityFullMark) {
        this.abilityFullMark = abilityFullMark;
        postInvalidate();
    }

    public void setColors(int[] colors) {
        this.colors = colors;
        postInvalidate();
    }

    /**
     * 将dip或dp值转换为px值,保证尺寸大小不变
     *
     * @param dipValue
     *            (DisplayMetrics类中属性density)
     * @return
     */
    public int dip2px(float dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }
}

在XML中使用

<com.lxh.custom.view.AbilityMapView
        android:id="@+id/abilityView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

在Activity中进行初始化

        s = new int[]{90, 50, 60, 40, 35};
        colors = new int[]{Color.BLUE, Color.BLACK, Color.CYAN, Color.GREEN, Color.MAGENTA};
        abilityMapView = (AbilityMapView) findViewById(R.id.abilityView);
        String [] t = {"攻击","防御","生存","控制","辅助"};
        abilityMapView.setAbility(t);//设置能力类型
        abilityMapView.setAbilityScore(s);//设置各项能力值
        abilityMapView.setAbilityFullMark(100);//设置最大能力值
        abilityMapView.setLayer(3);//设置圆圈层级
        abilityMapView.setColors(colors);//设置各项能力色值

代码不复杂,而且都带有注释,项目完整地址:https://github.com/lxh632671653/custom
技术交流请加QQ:632671653

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值