Android 的自定義View神通廣大也祠,可以實(shí)現(xiàn)各種復(fù)雜的樣式昙楚,漸變圓弧就是其中的一種。
1 shape 實(shí)現(xiàn)漸變
這個(gè)比較簡(jiǎn)單就是定義一個(gè)漸變的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>
圖片右側(cè)的黃色色塊就是漸變堪旧,使用時(shí)直接設(shè)置背景忽刽。
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是起始位置和漸變的結(jié)束位置诱贿,color0,color1是漸變顏色竣付,最后一個(gè)參數(shù)表示繪制模式:
Shader.TileMode有3種參數(shù)可供選擇琅坡,分別為CLAMP代虾、REPEAT和MIRROR:
[1] CLAMP的作用是如果渲染器超出原始邊界范圍撬碟,則會(huì)復(fù)制邊緣顏色對(duì)超出范圍的區(qū)域進(jìn)行著色
[2] REPEAT的作用是在橫向和縱向上以平鋪的形式重復(fù)渲染位圖
[3] MIRROR的作用是在橫向和縱向上以鏡像的方式重復(fù)渲染位圖
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 參數(shù)和上面一樣丽声,tile和上面一樣乎澄。
colors表示漸變的顏色數(shù)組李滴;
positions指定顏色數(shù)組的相對(duì)位置
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],作用是指定某個(gè)位置的顏色值螃宙,如果傳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,圓的中心坐標(biāo)谆扎。
color0,color1漸變顏色
colors漸變顏色數(shù)組
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);
}
可以看到藍(lán)色被壓縮的只剩了一點(diǎn)。
positions的特殊應(yīng)用状土,SweepGradient 無(wú)法指定顏色從某個(gè)角度到某個(gè)角度无蜂,可以利用positons指定顏色位置實(shí)現(xiàn)相應(yīng)弧度顯示相應(yīng)顏色。
類似只畫半個(gè)圓蒙谓,想讓顏色從0度劃過90度分布斥季,在90度的位置顯示綠色,如果不設(shè)置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);
}
基于上面的知識(shí)累驮,60度酣倾,30度都可以實(shí)現(xiàn)。
@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,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,60,false,paint);
}
一個(gè)簡(jiǎn)單的150度圓弧帶有漸變:
public class ViewDemo2 extends View {
public static final int VIEWHEIGHT = 150;
private int arcWidth = 25;
private float mProgress = 0f;
private float mMaxValue = 100;
private float mCurrentValue = 0;
private float startX;
private float startY;
private RectF showContent;
private float mCenterX;
private float mCenterY;
private Context mContext;
private float operationAngle = 150;
private float mOpenAngle = 150;
public ViewDemo2(Context context) {
this(context,null);
}
public ViewDemo2(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public ViewDemo2(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
}
public float getmProgress() {
return mProgress;
}
public void setmProgress(float mProgress) {
this.mProgress = mProgress;
requestLayout();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint getPaint = new Paint();
getPaint.setColor(Color.GREEN);
getPaint.setStrokeWidth(arcWidth);
getPaint.setAntiAlias(true);
getPaint.setStyle(Paint.Style.STROKE);
getPaint.setStrokeCap(Paint.Cap.ROUND);
Paint UnGetPaint = new Paint();
UnGetPaint.setColor(Color.BLACK);
UnGetPaint.setStrokeWidth(arcWidth);
UnGetPaint.setAntiAlias(true);
UnGetPaint.setStyle(Paint.Style.STROKE);
UnGetPaint.setStrokeCap(Paint.Cap.ROUND);
float[] position = new float[3];
mProgress = 0.8f;
position[0] = 0.0f;
position[1] = 0.5f*(150*1.0f)/180f * mProgress;
position[2] = 1.0f;
SweepGradient linearGradient = new SweepGradient(mCenterX,mCenterY,new int[]{Color.RED,Color.BLUE, Color.GREEN}, 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(190, mCenterX, mCenterY);
linearGradient.setLocalMatrix(matrix);
getPaint.setShader(linearGradient);
canvas.drawArc(showContent,195,150,false,UnGetPaint);
canvas.drawArc(showContent,195,150 * mProgress,false,getPaint);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setTextSize(34);
float radius = (mCenterY - startY)*1.0f;
try {
int cutNUm = 3;
if(mOpenAngle == operationAngle){
int xuanzhuan = (int) (mOpenAngle/(cutNUm -1));
int awardText = 0;
for(int i =0;i<cutNUm;i++ ){
int sweeppp = i * xuanzhuan;
double bcy = 0;
double acx = 0;
double posx = 0;
double posy = 0;
if(sweeppp == 0){
posx = arcWidth*2 + 15;
posy = (double) mCenterY - 100 - arcWidth;
awardText = 0;
}else if (sweeppp > 0 && sweeppp < 75){
bcy = (double) (radius * Math.sin(sweeppp*1.0/180*Math.PI));
acx = (double) (radius * Math.cos(sweeppp*1.0/180*Math.PI));
posx = (double) (mCenterX - acx);
posy = (double) (mCenterY - bcy);
awardText = (int) ((sweeppp / operationAngle) * mMaxValue);
Rect rect = new Rect();
String awardStr = awardText + "";
paint.getTextBounds(awardStr,0,awardStr.length(), rect);
int awardwidth = rect.width();
int awardheight = rect.height();
posy = posy + awardheight + arcWidth;
posx = posx + awardwidth + arcWidth;
}else if(sweeppp < operationAngle && sweeppp > 75){
int sweep2 = (int) (operationAngle - sweeppp);
bcy = (double) (radius * Math.sin(sweep2*1.0/180*Math.PI));
acx = (double) ((radius * Math.cos(sweep2*1.0/180*Math.PI)));
posx = (double) (mCenterX + acx);
posy = (double) (mCenterY - bcy);
awardText = (int) ((sweeppp / operationAngle) * mMaxValue);
Rect rect = new Rect();
String awardStr = awardText + "";
paint.getTextBounds(awardStr,0,awardStr.length(), rect);
int awardwidth = rect.width();
int awardheight = rect.height();
posy = posy + awardheight + arcWidth;
posx = posx - awardwidth -arcWidth;
}else if(sweeppp == operationAngle){
posx = (double)((mCenterX + radius)) ;
posy = (double) (mCenterY) - 100 - arcWidth;
awardText= (int) mMaxValue;
Rect rect = new Rect();
String awardStr = awardText + "";
paint.getTextBounds(awardStr,0,awardStr.length(), rect);
int awardwidth = rect.width();
int awardheight = rect.height();
posx = posx - awardwidth - arcWidth *2 -3;
}else if(sweeppp == 75){
posx = (double) mCenterX;
posy = (double) ((mCenterY - radius + 10)- arcWidth*2);
awardText= (int) (mMaxValue / 2);
Rect rect = new Rect();
String awardStr = awardText + "";
paint.getTextBounds(awardStr,0,awardStr.length(), rect);
int awardwidth = rect.width();
int awardheight = rect.height();
posy = posy + awardheight + arcWidth*2 + 10;
posx = posx - awardwidth/2;
}
canvas.drawText(awardText+"", (float) posx, (float) posy, paint);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension((int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, VIEWHEIGHT*2, getResources().getDisplayMetrics()), (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, VIEWHEIGHT, getResources().getDisplayMetrics()));
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
float edgeLengthWidth;
float edgeLengthHeight;
int fix = 0;
edgeLengthWidth = w - getPaddingRight() - getPaddingLeft() - arcWidth - arcWidth;
edgeLengthHeight = (h - getPaddingBottom() - getPaddingTop() - arcWidth ) * 2 ;
startX = getPaddingLeft() + arcWidth;
startY = getPaddingTop() + arcWidth;
// 得到顯示區(qū)域和中心的
showContent = new RectF(startX + fix, startY + fix, startX + edgeLengthWidth, startY + edgeLengthHeight);
mCenterX = showContent.centerX();
mCenterY = showContent.centerY();
}
}
android繪圖之Paint(1)
android繪圖之Canvas基礎(chǔ)(2)
Android繪圖之Path(3)
Android繪圖之drawText繪制文本相關(guān)(4)
Android繪圖之Canvas概念理解(5)
Android繪圖之Canvas變換(6)
Android繪圖之Canvas狀態(tài)保存和恢復(fù)(7)
Android繪圖之PathEffect (8)
Android繪圖之LinearGradient線性漸變(9)
Android繪圖之SweepGradient(10)
Android繪圖之RadialGradient 放射漸變(11)
Android繪制之BitmapShader(12)
Android繪圖之ComposeShader谤专,PorterDuff.mode及Xfermode(13)
Android繪圖之drawText,getTextBounds,measureText,FontMetrics,基線(14)
Android繪圖之貝塞爾曲線簡(jiǎn)介(15)
Android繪圖之PathMeasure(16)
Android 動(dòng)態(tài)修改漸變 GradientDrawable