android 中漸變的實現和SweepGradient 圓形漸變重點注意
Android 的自定義View神通廣大,可以實現各種複雜的樣式,漸變圓弧就是其中的一種。
1 shape 實現漸變
這個比較簡單就是定義一個漸變的shape。
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="0"
android:endColor="#FFC54E"
android:startColor ="#FF9326"
android:type="linear" />
<corners
android:bottomLeftRadius="25dp"
android:bottomRightRadius="25dp"
android:topLeftRadius="25dp"
android:topRightRadius="25dp" />
</shape>
圖片右側的黃色色塊就是漸變,使用時直接設定背景。
2 LinearGradient
2.1 第一種
public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.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
*/
x0,y0,x1,y1是起始位置和漸變的結束位置,color0,color1是漸變顏色,最後一個引數表示繪製模式:
Shader.TileMode有3種引數可供選擇,分別為CLAMP、REPEAT和MIRROR:
[1] CLAMP的作用是如果渲染器超出原始邊界範圍,則會複製邊緣顏色對超出範圍的區域進行著色
[2] REPEAT的作用是在橫向和縱向上以平鋪的形式重複渲染點陣圖
[3] MIRROR的作用是在橫向和縱向上以映象的方式重複渲染點陣圖
2.2 第二種
public LinearGradient (float x0, float y0, float x1, float y1, int[] colors, float[] positions, Shader.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 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
*/
x0,y0,x1,y1 引數和上面一樣,tile和上面一樣。
colors表示漸變的顏色陣列;
positions指定顏色陣列的相對位置
package com.example.hpuzz.myapplication;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
public class ViewDemo extends View {
public ViewDemo(Context context) {
super(context);
}
public ViewDemo(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public ViewDemo(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.GREEN);
LinearGradient linearGradient = new LinearGradient(0, 0, 0, getMeasuredHeight(),new int[]{Color.RED, Color.BLUE}, null, LinearGradient.TileMode.REPEAT);
paint.setShader(linearGradient);
canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),paint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension((int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, getResources().getDisplayMetrics()), (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, getResources().getDisplayMetrics()));
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.GREEN);
float[] position = new float[3];
position[0] = 0.0f;
position[1] = 0.8f;
position[2] = 1.0f;
LinearGradient linearGradient = new LinearGradient(0, 0, 0, getMeasuredHeight(),new int[]{Color.RED,Color.GREEN, Color.BLUE}, position, LinearGradient.TileMode.REPEAT);
paint.setShader(linearGradient);
canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),paint);
}
可以看到position的取值範圍[0,1],作用是指定某個位置的顏色值,如果傳null,漸變就線性變化。
3 SweepGradient
/**
* A Shader that draws a sweep gradient around a center point.
*
* @param cx The x-coordinate of the center
* @param cy The y-coordinate of the center
* @param colors The colors to be distributed between around the center.
* There must be at least 2 colors in the array.
* @param positions May be NULL. The relative position of
* each corresponding color in the colors array, beginning
* with 0 and ending with 1.0. If the values are not
* monotonic, the drawing may produce unexpected results.
* If positions is NULL, then the colors are automatically
* spaced evenly.
*/
public SweepGradient(float cx, float cy,
@NonNull @ColorInt int colors[], @Nullable float positions[]) {
if (colors.length < 2) {
throw new IllegalArgumentException("needs >= 2 number of colors");
}
if (positions != null && colors.length != positions.length) {
throw new IllegalArgumentException(
"color and position arrays must be of equal length");
}
mType = TYPE_COLORS_AND_POSITIONS;
mCx = cx;
mCy = cy;
mColors = colors.clone();
mPositions = positions != null ? positions.clone() : null;
}
/**
* A Shader that draws a sweep gradient around a center point.
*
* @param cx The x-coordinate of the center
* @param cy The y-coordinate of the center
* @param color0 The color to use at the start of the sweep
* @param color1 The color to use at the end of the sweep
*/
public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1) {
mType = TYPE_COLOR_START_AND_COLOR_END;
mCx = cx;
mCy = cy;
mColor0 = color0;
mColor1 = color1;
mColors = null;
mPositions = null;
}
cx,cy,圓的中心座標。
color0,color1漸變顏色
colors漸變顏色陣列
positions 顏色位置,範圍[0,1]
package com.example.hpuzz.myapplication;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SweepGradient;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
public class ViewDemo2 extends View {
public ViewDemo2(Context context) {
super(context);
}
public ViewDemo2(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public ViewDemo2(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.GREEN);
paint.setStrokeWidth(15);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
/* float[] position = new float[3];
position[0] = 0.0f;
position[1] = 0.8f;
position[2] = 1.0f;*/
// SweepGradient linearGradient = new SweepGradient(getMeasuredWidth()/2,getMeasuredHeight()/2,new int[]{Color.RED,Color.GREEN, Color.BLUE}, position);
SweepGradient linearGradient = new SweepGradient((getMeasuredWidth() - 40)/2,(getMeasuredHeight() - 40)/2,new int[]{Color.RED,Color.GREEN, Color.BLUE}, null);
Matrix matrix = new Matrix();
matrix.setRotate(180, getMeasuredWidth()/2, getMeasuredHeight()/2);
linearGradient.setLocalMatrix(matrix);
paint.setShader(linearGradient);
RectF rect = new RectF(20, 20, getMeasuredWidth() - 20, getMeasuredHeight() - 20);
canvas.drawArc(rect,0,360,false,paint);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension((int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 240, getResources().getDisplayMetrics()), (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 240, getResources().getDisplayMetrics()));
}
}
改變開始漸變的角度:
Matrix matrix = new Matrix();
matrix.setRotate(180, (getMeasuredWidth()-40)/2, (getMeasuredHeight()-40)/2);
linearGradient.setLocalMatrix(matrix);
positions為null表示線性漸變,如果不為空可以改變漸變中某些顏色的位置。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.GREEN);
paint.setStrokeWidth(15);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
float[] position = new float[3];
position[0] = 0.0f;
position[1] = 0.8f;
position[2] = 1.0f;
SweepGradient linearGradient = new SweepGradient(getMeasuredWidth()/2,getMeasuredHeight()/2,new int[]{Color.RED,Color.GREEN, Color.BLUE}, position);
// SweepGradient linearGradient = new SweepGradient((getMeasuredWidth() - 40)/2,(getMeasuredHeight() - 40)/2,new int[]{Color.RED,Color.GREEN, Color.BLUE}, null);
Matrix matrix = new Matrix();
matrix.setRotate(180, getMeasuredWidth()/2, getMeasuredHeight()/2);
linearGradient.setLocalMatrix(matrix);
paint.setShader(linearGradient);
RectF rect = new RectF(20, 20, getMeasuredWidth() - 20, getMeasuredHeight() - 20);
canvas.drawArc(rect,0,360,false,paint);
}
可以看到藍色被壓縮的只剩了一點。
positions的特殊應用,SweepGradient 無法指定顏色從某個角度到某個角度,可以利用positons指定顏色位置實現相應弧度顯示相應顏色。
類似只畫半個圓,想讓顏色從0度劃過90度分佈,在90度的位置顯示綠色,如果不設定positions則顯示效果為:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.GREEN);
paint.setStrokeWidth(15);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
float[] position = new float[3];
position[0] = 0.0f;
position[1] = 0.25f;
position[2] = 1.0f;
SweepGradient linearGradient = new SweepGradient(getMeasuredWidth()/2,getMeasuredHeight()/2,new int[]{Color.RED,Color.GREEN, Color.BLUE}, position);
// SweepGradient linearGradient = new SweepGradient((getMeasuredWidth() - 40)/2,(getMeasuredHeight() - 40)/2,new int[]{Color.RED,Color.GREEN, Color.BLUE}, null);
Matrix matrix = new Matrix();
matrix.setRotate(180, getMeasuredWidth()/2, getMeasuredHeight()/2);
linearGradient.setLocalMatrix(matrix);
paint.setShader(linearGradient);
RectF rect = new RectF(20, 20, getMeasuredWidth() - 20, getMeasuredHeight() - 20);
canvas.drawArc(rect,180,90,false,paint);
}
基於上面的知識,60度,30度都可以實現。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.GREEN);
paint.setStrokeWidth(15);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
float[] position = new float[3];
position[0] = 0.0f;
position[1] = 0.17f;
position[2] = 1.0f;
SweepGradient linearGradient = new SweepGradient(getMeasuredWidth()/2,getMeasuredHeight()/2,new int[]{Color.RED