仿华为天气刻度圆盘

————作为一个自学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,

@Override
protected 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为画笔设置颜色渐变效果,代码如下:

@Override
public 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ㄒ)/~~
  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值