仿华为天气刻度圆盘

来源:互联网 发布:妙趣横生的算法 pdf 编辑:程序博客网 时间:2024/04/29 20:08

————作为一个自学Android不久的渣渣,一直在各大博客网站看大神前辈们的技术文章,真的学到很多。最近自己防了华为天气的刻度圆盘UI,特意写了这篇文章,算是记录吧。

首先,本文参考http://m.blog.csdn.net/article/details?id=51111125的绘制,但是加入了更多的逻辑判断, 这里感谢作者。

先来看看最终效果:

可以看出这个UI就是一个自定义的View,由 刻度盘(白色线条、渐变色线条)、中间温度文字、周边温度文字等部分组成。我们先来看刻度盘的绘制。

刻度盘的绘制

绘制这个刻度盘其实很简单,我们有几种方式可以绘制出来,一是旋转画布来达到效果,但是由于后面绘制周边最低温度和最高温度文字显示时,需要一个确切的坐标才能正确显示文字的位置,所以这里不考虑使用。二就是确定一个圆心,以及半径,然后遍历整个圆的度数,计算出每增加某个角度后顶点的X、Y坐标,然后依次绘制出线条。

由于需要根据角度计算坐标长度,因此需要用到一点勾股定理,所以先确定两个方法:

/** * 根据半径和角度计算x坐标 */private float calculateX(float r, double angle) {    angle = angle * ((2 * Math.PI) / 360);    double x = r * Math.sin(angle);    double xFinal = centerX + x;    return (float) xFinal;}/** * 根据半径和角度计算y坐标 */private float calculateY(float r, double angle) {    angle = angle * ((2 * Math.PI) / 360);    double y = r * Math.cos(angle);    double yFinal = centerY - y;    return (float) yFinal;}

先把角度换算成弧度,然后根据弧度计算X 、Y坐标。

现在遍历角度,我们把整个圆分为120份,每条线间隔3°,根据半径和此时的角度计算出坐标,线条的长度为l,

@Overrideprotected void onDraw(Canvas canvas) {    canvas.save();    for (double angle = 0; angle <= 360d; angle += 3.0d) {        float xStart = calculateX(r, angle);        float xStop = calculateX(r - l, angle);        float yStart = calculateY(r, angle);        float yStop = calculateY(r - l, angle);        //绘制起始角度和终止角度的着色线条        //根据起始角度和终止角度所在位置,大致可以分为三种情况        if (((startAngle>=0&&startAngle<180) && (stopAngle>=0&&stopAngle<180)) ||                ((startAngle>180&&startAngle<360) && (stopAngle>180&&stopAngle<360))){            //当起始角度和终止角度都在左边半圆 或者都在右边半圆时            if (angle <= stopAngle && angle >= startAngle) {                linePaint.setShader(mShader);            } else                linePaint.setShader(mWhiteShader);        } else if ((startAngle>180&&startAngle<=360) && (stopAngle>=0&&stopAngle<180)) {            //当起始角度在左边半圆  终止角度在右边半圆时            if ((angle>=0&&angle<=stopAngle) || (angle>=startAngle&&angle<=360)) {                linePaint.setShader(mShader);            } else {                linePaint.setShader(mWhiteShader);            }        }        //刻度盘大致为两种长度和宽度的线条        if (angle == 207 || angle == 153) {            //绘制边界位置的两条线条            linePaint.setStrokeWidth(2);            float xStartL = calculateX(r * 1.05f, angle);            float xStopL = calculateX((r - l), angle);            float yStartL = calculateY(r * 1.05f, angle);            float yStopL = calculateY((r - l), angle);            canvas.drawLine(xStartL, yStartL, xStopL, yStopL, linePaint);       //底部两条较长的线        } else if (!(angle < 207 && angle > 153)) {            //绘制其他位置的线条            linePaint.setStrokeWidth(3);            canvas.drawLine(xStart, yStart, xStop, yStop, linePaint);       //  画短线        }    }    //绘制最低温度、最高温度、中心实时温度    drawCenterTem(canvas);    drawStartTem(canvas);    drawStopTem(canvas);    canvas.restore();}

由于在最低温度和最高温度中间的线条都要着色,因此加入判断。还有刻度盘边界的两条线段长度不同,也需加上判断,注释已经写的很详细了。另外记得在Draw()方法中,里面的绘制逻辑都应被包含在canvas.save()、canvas.restore()之间,避免当数据改变时,视图没有做出变动。

剩下的绘制温度文字部分已经很明朗了,

/** * 画中心位置温度 * * @param canvas */private void drawCenterTem(Canvas canvas) {    mTextPaint.setTextSize(r * 0.6f);    mTextPaint.setAntiAlias(true);    mTextPaint.setTextAlign(Paint.Align.CENTER);    float textY = centerY - (mTextPaint.descent() + mTextPaint.ascent()) / 2;    canvas.drawText(centerTemper+"°", centerX, textY, mTextPaint);}/** * 画起始温度 */private void drawStartTem(Canvas canvas) {    mTextPaint.setTextSize(r * 0.1f);    canvas.drawText(startTem + "°",  calculateX(r * 1.1f, startAngle),            calculateY(r * 1.1f, startAngle), mTextPaint);}/** * 画截至温度 */private void drawStopTem(Canvas canvas) {    mTextPaint.setTextSize(r * 0.1f);    canvas.drawText(stopTem + "°", calculateX(r * 1.1f, stopAngle), calculateY(r * 1.1f, stopAngle), mTextPaint);}

另外在使用Draw()之前,我们在onWindowFocusChanged()方法中获取诸如手机屏幕的高和宽,继而计算出圆心、半径、线条长度等数据,以及使用Shader为画笔设置颜色渐变效果,代码如下:

@Overridepublic void onWindowFocusChanged(boolean hasWindowFocus) {    int viewWidth = getWidth();    int viewHeight = getHeight();    centerX = viewWidth / 2f;    centerY = viewHeight / 2f;    r = viewWidth * 0.4f;    l = viewWidth * 0.05f;    //设置渐变色    mShader = new SweepGradient(centerX, centerY, new int[] {                    Color.parseColor("#FB8B13"),                    Color.parseColor("#FB1414"),                    Color.parseColor("#1488FB"),                    Color.parseColor("#13FBE0"),                    Color.parseColor("#8BFB13"),                    Color.parseColor("#FB8B13")}, null);    mWhiteShader = new SweepGradient(centerX, centerY, new int[] {                    Color.WHITE,                    Color.WHITE }, null);    linePaint.setShader(mShader);    invalidate();}

这里我们使用的是梯度渲染来达到想要的效果,由于梯度渲染开始渲染的位置默认为90°,会导致颜色显示很突兀,因为我们第一个颜色和最后一个颜色设置成一样的,这样就首尾相连了。

最后,文章到此结束,其实绘制还是比较容易的,只是在最低温度和最高温度的显示上,根据温度,计算出起始和终止角度的逻辑上有点坑。我采用了比较傻的方法,如果有人有更好的方法,希望不惜赐教。

作者邮箱:13642948820@163.com

QQ号码:497078141

项目地址:https://github.com/yinyiliang/WindCloud

作者另一个项目:https://github.com/yinyiliang/YunGirl

最后,作者正在找工作,坐标广州,有没有哪位大大能介绍个工作呀!!/(ㄒoㄒ)/~~
2 0
原创粉丝点击