Android精选AndroidAndroid开发经验谈

android多维效果图

2018-02-24  本文已影响223人  天地人qrx

新的一年开始了,一直没发表过文章,感觉还是要弄些总结出来有助于自己的提高,刚好这几天比较有空,之前有写个小控件,稍微优化了下,先上效果图吧。

                                                                        五维效果图

                                                                    六维效果图

看效果图也不难就是三个图形叠加起来的,那思路就是一个图形一个图形的画上去,先上代码吧


import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Paint;

import android.graphics.Path;

import android.graphics.PointF;

import android.util.AttributeSet;

import android.view.View;

import android.view.animation.Animation;

import android.view.animation.DecelerateInterpolator;

import android.view.animation.Transformation;

import java.util.LinkedHashMap;

import java.util.Map;

/**

* @author qrx

* @date 2016/3/30

* @time 15:08

* @description

*/

public class MultidimensionalViewextends View {

/**

    * 背景画笔

    */

    private PaintmBgPaint;

    /**

    * 线条画笔

    */

    private PaintmLinePaint;

    /**

    * 标题画笔

    */

    private PaintmTitlePaint;

    /**

    * 粗白色线画笔

    */

    private PaintmBoldLinePaint;

    /**

    * 背景路径

    */

    private PathmBgPath;

    /**

    * 阴影路径

    */

    private PathmShadowPath;

    /**

    * 阴影画笔

    */

    private PaintmShadowPaint;

    /**

    * 高

    */

    private int height;

    /**

    * 宽

    */

    private int width;

    /**

    * 内边长

    */

    private int inSideLength;

    /**

    * 背景上下边距

    */

    private int paddingTopAndBottom =0;

    /**

    * 背景各顶点数组

    */

    private PointF[]mBgPoints;

    /**

    * 背景各顶点数组

    */

    private PointF[]mDeepBgPoints;

    /**

    * 小标题数组

    */

    private PointF[]mTitlePoints;

    /**

    * 小白点的半径

    */

    private float circleRadius;

    /**

    * 顶点个数

    */

    private int mPointCounts =5;

    /**

    * 中心坐标

    */

    private PointFcenterPoint;

    /**

    * 小标题

    */

  private Stringtitle[];

    /**

    * 数据 key标题  value值

    */

    private Mapdata;

    /**

    * 各数据坐标

    */

    private PointF[]mDataPoints;

    /**

    * 用于测量文本大小

    */

    private Paint.FontMetricsfm;

    /**

    * 动画

    */

    private AnimationmAnimation;

    private int mPaintAlpha;

    public MultidimensionalView(Context context) {

this(context, null);

    }

public MultidimensionalView(Context context, AttributeSet attrs) {

this(context, attrs, 0);

    }

public MultidimensionalView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

        init();

    }

@Override

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int widthMode = MeasureSpec.getMode(widthMeasureSpec);

        width = MeasureSpec.getSize(widthMeasureSpec);

        if (widthMode != MeasureSpec.UNSPECIFIED) {

heightMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);

        }

height = MeasureSpec.getSize(heightMeasureSpec);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    }

@Override

    protected void onSizeChanged(int w, int h, int oldw, int oldh) {

super.onSizeChanged(w, h, oldw, oldh);

        paddingTopAndBottom = Math.max(getPaddingTop(),getPaddingBottom());

        if(paddingTopAndBottom ==0){

paddingTopAndBottom = (int) ScreenUtil.dp2px(getContext(), 60);

        }

inSideLength =height /2 -paddingTopAndBottom;

        circleRadius = ScreenUtil.dp2px(getContext(), 4);

        centerPoint =new PointF(w /2, inSideLength +paddingTopAndBottom);

    }

/**设置数据 key-->顶点标题  value --> 数值[0,100]

    * @param data 数据map

*/

    public void bindData(LinkedHashMap data) {

if (data !=null) {

this.data = data;

            mPointCounts = data.size();

            title =new String[mPointCounts];

            data.keySet().toArray(title);

        }

initAnimation();

    }

/**

    * 动画

    */

    private void initAnimation() {

if (mAnimation ==null) {

mAnimation =new Animation() {

@Override

                protected void applyTransformation(float interpolatedTime, Transformation t) {

mPaintAlpha = (int) (interpolatedTime *255);

                    invalidate();

                }

};

            mAnimation.setInterpolator(new DecelerateInterpolator(3.2f));

        }

if (!mAnimation.hasStarted()) {

mAnimation.cancel();

        }

mAnimation.setDuration(10000);

        startAnimation(mAnimation);

    }

/**

    * 度数转换成弧度

    *

    * @param degrees

    * @return

    */

    private double getRadians(int degrees) {

return degrees * Math.PI /180;

    }

/**

    * 初始化

    */

    private void init() {

mBgPaint =new Paint(Paint.ANTI_ALIAS_FLAG);

        mBgPaint.setStyle(Paint.Style.FILL);

        mBgPath =new Path();

        mLinePaint =new Paint(Paint.ANTI_ALIAS_FLAG);

        mTitlePaint =new Paint(Paint.ANTI_ALIAS_FLAG);

        mTitlePaint.setTextSize(ScreenUtil.dp2px(getContext(), 14));

        fm =mTitlePaint.getFontMetrics();

        mShadowPath =new Path();

        mShadowPaint =new Paint(Paint.ANTI_ALIAS_FLAG);

        mShadowPaint.setStyle(Paint.Style.FILL);

        mShadowPaint.setColor(getContext().getResources().getColor(R.color.white));

        mShadowPaint.setAlpha(100);

        mBoldLinePaint =new Paint(Paint.ANTI_ALIAS_FLAG);

        mBoldLinePaint.setStyle(Paint.Style.STROKE);

        mBoldLinePaint.setColor(getContext().getResources().getColor(R.color.white));

        mBoldLinePaint.setStrokeWidth(ScreenUtil.dp2px(getContext(), 2));

    }

/**

    * 画背景

    */

    private void drawBackground(Canvas canvas) {

initBgPoints();

        mBgPath.reset();

        mBgPath.moveTo(mBgPoints[0].x, mBgPoints[0].y);//第一个点 顶点

        for (int i =1; i

mBgPath.lineTo(mBgPoints[i].x, mBgPoints[i].y);

        }

mBgPath.close();

        mBgPaint.setColor(getContext().getResources().getColor(R.color.bg_blue_color));

        mBgPaint.setAlpha(mPaintAlpha);

        canvas.drawPath(mBgPath, mBgPaint);

    }

/**

    * 画背景

    */

    private void drawDeepBackground(Canvas canvas) {

initDeepBgPoints();

        mBgPath.reset();

        mBgPath.moveTo(mDeepBgPoints[0].x, mDeepBgPoints[0].y);//第一个点 顶点

        for (int i =1; i

mBgPath.lineTo(mDeepBgPoints[i].x, mDeepBgPoints[i].y);

        }

mBgPath.close();

        mBgPaint.setColor(getContext().getResources().getColor(R.color.bg_blue_deep_color));

        mBgPaint.setAlpha(mPaintAlpha);

        canvas.drawPath(mBgPath, mBgPaint);

    }

/**

    * 初始化深色背景各顶点

    */

    private void initDeepBgPoints() {

initBgPoints();

        if (mDeepBgPoints ==null) {

mDeepBgPoints =new PointF[mPointCounts];

            for (int i =0; i

mDeepBgPoints[i] = getMiddlePointFromCentre(mBgPoints[i]);

            }

}

}

/**

    * 画网

    *

    * @param canvas

    */

    private void drawMesh(Canvas canvas) {

mLinePaint.setColor(getContext().getResources().getColor(R.color.white));

        mLinePaint.setStrokeWidth(1);

        mLinePaint.setAlpha(mPaintAlpha);

        for (int i =0; i

canvas.drawLine(centerPoint.x, centerPoint.y, mBgPoints[i].x, mBgPoints[i].y, mLinePaint);

        }

}

/**

    * 获取两个点的中点

    *

    * @param p1 第一个点

    * @param p2 第二个点

    * @return

    */

    private PointFgetMiddlePoint(PointF p1, PointF p2) {

return new PointF((p1.x + p2.x) /2, (p1.y + p2.y) /2);

    }

/**

    * 获取两个点的中点

    *

    * @param p1 第一个点

    * @return

    */

    private PointFgetMiddlePointFromCentre(PointF p1) {

return new PointF((p1.x +centerPoint.x) /2, (p1.y +centerPoint.y) /2);

    }

/**

    * 初始化各顶点 利用中心坐标求出各顶点的坐标

    */

    private void initBgPoints() {

if (mBgPoints ==null) {

mBgPoints =new PointF[mPointCounts];

            double degrees = getRadians(360/mPointCounts);

            for (int i =0; i

mBgPoints[i] =new PointF((float) (centerPoint.x +inSideLength * Math.sin(degrees * i)), (float) (centerPoint.y -inSideLength * Math.cos(degrees * i)));

            }

}

}

@Override

    protected void onDraw(Canvas canvas) {

//画背景

        drawBackground(canvas);

        //画深色背景

        drawDeepBackground(canvas);

        //画网格 中心到各顶点线

        drawMesh(canvas);

        //画标题

        drawTitle(canvas);

        //画坐标点

        drawCoordinate(canvas);

        //画每个坐标点连接起来的面积区域

        drawShadow(canvas);

        //连接每个坐标

        drawBoldLine(canvas);

    }

/**

    * 画粗线

    *

    * @param canvas

    */

    private void drawBoldLine(Canvas canvas) {

mBoldLinePaint.setAlpha(mPaintAlpha);

        for (int i =0; i

if (mDataPoints[i].y !=centerPoint.y

                    &&mDataPoints[(i +1) %mPointCounts].y !=centerPoint.y) {

canvas.drawLine(mDataPoints[i].x, mDataPoints[i].y,

                        mDataPoints[(i +1) %mPointCounts].x,

                        mDataPoints[(i +1) %mPointCounts].y,

                        mBoldLinePaint);

            }

}

}

/**

    * 画阴影

    *

    * @param canvas

    */

    private void drawShadow(Canvas canvas) {

mShadowPaint.setAlpha(mPaintAlpha*100/255);

        mShadowPath.moveTo(mDataPoints[0].x, mDataPoints[0].y);

        for (int i =1; i

mShadowPath.lineTo(mDataPoints[i].x, mDataPoints[i].y);

        }

mShadowPath.close();

        canvas.drawPath(mShadowPath, mShadowPaint);

    }

/**

    * 画具体坐标

    *

    * @param canvas

    */

    private void drawCoordinate(Canvas canvas) {

initDataPoints();

        mLinePaint.setAlpha(mPaintAlpha);

        for (int i =0; i

if (mDataPoints[i].y !=centerPoint.y) {

//排除数据为0的情况

                //画圆

                canvas.drawCircle(mDataPoints[i].x, mDataPoints[i].y, circleRadius, mLinePaint);

            }

}

}

/**

    * 初始化坐标

    */

    private void initDataPoints() {

mDataPoints =new PointF[mPointCounts];

        for (int i =0; i

mDataPoints[i] = getDataPoint(mBgPoints[i], data.get(title[i]));

        }

}

/**

    * 获取指定数值的点,根据相应的百分比

    *

    * @param p    顶点

    * @param value 数值

    * @return

    */

    private PointFgetDataPoint(PointF p, int value) {

PointF pointF =new PointF();

        pointF.x = (float) ((value *1.0 /100 *inSideLength) /inSideLength * (p.x -centerPoint.x) +centerPoint.x);

        pointF.y = (float) ((value *1.0 /100 *inSideLength) /inSideLength * (p.y -centerPoint.y) +centerPoint.y);

        return pointF;

    }

/**

    * 获取两点之间的距离

    *

    * @param p1 第一点

    * @param p2 第二点

    * @return

    */

    private float getDistance(PointF p1, PointF p2) {

return (float) Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));

    }

/**

    * 画小标题

    *

    * @param canvas

    */

    private void drawTitle(Canvas canvas) {

initTitlePoints();

        //小标题

        mTitlePaint.setColor(getContext().getResources().getColor(R.color.text_color_black));

        mTitlePaint.setAlpha(mPaintAlpha);

        for (int i =0; i

canvas.drawText(title[i], mTitlePoints[i].x -mTitlePaint.measureText(title[i]) /2, mTitlePoints[i].y, mTitlePaint);

        }

//蓝色数值

        mTitlePaint.setColor(getContext().getResources().getColor(R.color.text_color_blue));

        mTitlePaint.setAlpha(mPaintAlpha);

        for (int i =0; i

String text;

            if (data.get(title[i]) >0) {

text = String.valueOf(data.get(title[i]));

            }else {

text ="N/A";

            }

canvas.drawText(text, mTitlePoints[i].x -mTitlePaint.measureText(text) /2, mTitlePoints[i].y + (fm.bottom -fm.top), mTitlePaint);

        }

}

/**

    * 初始化各小标题位置

    */

    private void initTitlePoints() {

if (mTitlePoints ==null) {

mTitlePoints =new PointF[mPointCounts];

            for (int i =0; i

mTitlePoints[i] = getTitlePoint(mBgPoints[i]);

            }

}

}

/**

    * 通过传入的顶点坐标  获取小标题坐标

    *

    * @param pointF

    * @return

    */

    private PointFgetTitlePoint(PointF pointF) {

return getDataPoint(pointF, 125);

    }

}


首先来看看onMeasure()方法 ,这边主要是把整个视图弄成是正方形的,我们画的图形都是正多边形,视图弄成正方形的画方便下手;

再看下onSizeChanged()方法,这边主要是确定图形中心的坐标以及中心坐标到各顶点的距离,方便后面的计算;

bindData()和initAnimation()就后面再说了,init()中都是初始化一些画笔啥的;

那就来看看最重要的onDraw()方法吧,drawBackground()就是画整个背景图形用的,在该方法中的initBgPoints()中有初始化每个顶点的坐标,这个顶点的坐标计算思路就是先确定最顶部的顶点,然后这个顶点以中心坐标进行旋转,旋转角度就是360除以点的个数了,顺时针还是逆时针旋转这个就是修改加减三角函数的问题了,我这边是顺时针的。初始化各顶点后就只需要连接各个顶点就好了,当然这边的画笔的风格要设置成FILL。

接下来的方法也都比较类似,drawDeepBackground(),是画深色部分的图形,比原来图形的小一半,坐标也很好算,一样减半就好了。

//画网格 中心到各顶点线

drawMesh(canvas);

//画标题

drawTitle(canvas);

//画坐标点

drawCoordinate(canvas);

//画每个坐标点连接起来的面积区域

drawShadow(canvas);

//连接每个坐标

drawBoldLine(canvas);

这些方法就都是大同小异了,算出要画图形顶点的位置,然后连接起来。

那现在我们回来看看bindData()和initAnimation() 这两个方法,bindData传递的是一个有向的hashmap,这样能够保证每次画的图形是一样的,key为标题,value为值,这个值有效的情况下应该在0到100,initAnimation()中有用到加速的插值器,动画时间为1秒,在这一秒内更新了每个画笔的透明值,然后刷新重画,这样整个效果就做完了。

设置数据:

LinkedHashMap data =new LinkedHashMap<>();

data.put("结交人脉", 70);

data.put("维护人脉", 50);

data.put("时间投入", 30);

data.put("忙碌程度", 40);

data.put("活跃", 50);

data.put("信用", 80);

mMultidimensionalView.bindData(data);

整个控件做的还是有点粗糙,像画笔的颜色、粗细的值、动画时间可能放在自定义属性上会更好,后面再优化优化吧,还有很多改进的地方,欢迎各位指正。


项目地址:https://github.com/qiurunxing/multidimensionalView

上一篇下一篇

猜你喜欢

热点阅读