画渐变圆弧

最近要完成这样的一个效果图:

lizi.png

要求是中间圆环进度条的颜色是渐变的,75%的#FFFFFF渐变到100%,宽度6pt,两端都是圆的,并且有一个20% #000000、8pt的外发光。

看着这要求,很是头疼,还外发光。。

主要的难点有几个:

  1. 背景颜色的渐变
  2. 圆弧的渐变
  3. 让文字画在圆形的中间,图片也画在中间

首先背景颜色的渐变,可以定义一个drawable解决:

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <gradient
        android:angle="315"
        android:endColor="#00A1F8"
        android:startColor="#84CFFB" />
</shape>

注意的是 angle 属性角度必须是45度的倍数,至于什么度数的渐变方位在哪,那就自己试一下就行,315度就是在左上角。

第二个,圆弧的渐变

  • 让弧线的两端是圆滑的,需要给Pain设置一个属性:
currentPaint.setStrokeCap(Paint.Cap.ROUND); //让弧线两边是圆滑的
  • 线性渐变 LinearGradient

LinearGradient有两个构造方法,分别是:

    /** Create a shader that draws a linear gradient along a line.
        @param x0           The x-coordinate for the start of the gradient line
        @param y0           The y-coordinate for the start of the gradient line
        @param x1           The x-coordinate for the end of the gradient line
        @param y1           The y-coordinate for the end of the gradient line
        @param  colors      The colors to be distributed along the gradient line
        @param  positions   May be null. The relative positions [0..1] of
                            each corresponding color in the colors array. If this is null,
                            the the colors are distributed evenly along the gradient line.
        @param  tile        The Shader tiling mode
    */
    public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],
            TileMode tile) {
    }

    /** Create a shader that draws a linear gradient along a line.
        @param x0       The x-coordinate for the start of the gradient line
        @param y0       The y-coordinate for the start of the gradient line
        @param x1       The x-coordinate for the end of the gradient line
        @param y1       The y-coordinate for the end of the gradient line
        @param  color0  The color at the start of the gradient line.
        @param  color1  The color at the end of the gradient line.
        @param  tile    The Shader tiling mode
    */
    public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,
            TileMode tile) {
    }

前面 4 个 参数都是起始坐标和结束坐标,第一个colors数组是沿渐变线分布的颜色,positions数组是 相对位置[0..1]的的颜色阵列中的每个相应的颜色。如果这是空,该颜色是沿着渐变线均匀分布。(其实就是对注释的翻译,我翻译不好)
第二个构造方法后面直接就是开始渐变的颜色和结束渐变的颜色。最后一个参数都传 Shader.TileMode.MIRROR 就好。

其实这里比较想不通的是渐变如果是沿着一条直线还好,怎么才能让它沿着一条弧线来渐变呢,就是说 float x1, float y1 这两个坐标不知道怎么传。

先看怎么画弧线:

mOval = new RectF(
      mStrokeWidth + dp(4),
      mStrokeWidth + dp(4),
      getWidth() - mStrokeWidth - dp(4),
      getHeight() - mStrokeWidth - dp(4)
);

float sweepAngle = ((float) currentValue / (float) totalValue) * 360f;
canvas.drawArc(mOval, mStartAngle, sweepAngle, false, currentPaint);

首先定义一个范围,再画出来。
至于刚刚的问题,参考了一个开源控件的写法,原来这样设置 LinearGradient 的参数就可以达到效果:

mShader = new LinearGradient(
        mOval.left,
        mOval.top,
        mOval.left,
        mOval.bottom,
        mFgColorStart,
        mFgColorEnd,
        Shader.TileMode.MIRROR
);

第三个,让文字画在圆形的中间,图片也画在中间

当你想把文字绘制在中间时,你可能第一时间会想到坐标是这样的:

int textX = getWidth() / 2;
int textY = getHeight() / 2;

可是这样是不行的,你必须要考虑加上文字的宽度和高度才能算正确,
获取文字宽度的方法:

textPaint.measureText(text + ""); //测量文字,得到宽度

因为文字也是数字,也是可以测量的。

得到文字高度,必须理解基准线这东西,这里有个文章介绍。

再贴个图:

120628055573351.png

可以看到,得到高度,就相当于 ascent 减去 descent:

float textHeight = textPaint.ascent() + textPaint.descent(); //根据基准线得到文字的高

所有这样的坐标才能画在正中间:

int textX = getWidth() / 2;
int textY = getHeight() / 2;

textX = textX - textWidth / 2;
textY = textY - textHeight / 2;

图片的话,Bitmap 都有相应的 get 方法,就不说了。

==================分割线=2016.6.3==========================

更正一个错误,沿着弧线渐变用线性渐变是不行的,如果用以上方法它会有一种效果就是从浅到深后又会由深到浅。

正确的方法是使用梯度渐变 SweepGradient 和 Matrix结合起来才有效果。
首先先看下 SweepGradient 的大致效果如下:

1344993412_1866.png

这样的效果就不会出现线性渐变的问题了。

代码如下:

private void init() {
    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(50);  
    mPaint.setStrokeCap(Paint.Cap.ROUND);
    mPaint.setColor(Color.WHITE);
    mMatrix = new Matrix();
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    int x = getWidth() / 2;
    int y = getHeight() / 2;
    float startAngle = 270;
    float sweepAngle = 340;

    mMatrix.setRotate(-150, x, y);
    mShader = new SweepGradient(startAngle, sweepAngle, new int[]{0x4dffffff, 0xffffffff}, null);
    mShader.setLocalMatrix(mMatrix);

    mPaint.setShader(mShader);
    RectF mOval = new RectF(25, 25, getWidth() - 25, getHeight() - 25);

    canvas.drawArc(mOval, startAngle, sweepAngle, false, mPaint);
}

其中,核心代码是:

mMatrix.setRotate(-150, x, y);
mShader.setLocalMatrix(mMatrix);

没有这两句,怎么写都是白搭。

其中 x 和 y 是圆心坐标,-150的意思是 将扫描起始的地方逆时针选择90°后,圆角画笔的地方还有半个圆没有遮盖。
因为我是从12点方向开始画的,而扫描开始的方向是3点钟,所以我填的是 -90 ,剩下那 -60 度是微调,这个需要按实际情况而定,看下这两个图或自己试一下就知道什么意思了:
填 -90 度的时候效果:

a.png

加上 -60 度的微调后的效果:

b.png

这样,应该能够明白什么意思了。

不过有一个bug,当结束角度接近360度的时候,会显示成这样:


c.png

暂时没有解决办法,哪位大神看到有办法的,请支招。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,427评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,551评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,747评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,939评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,955评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,737评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,448评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,352评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,834评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,992评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,133评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,815评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,477评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,022评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,147评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,398评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,077评论 2 355

推荐阅读更多精彩内容