Android – 自定义多色渐变背景板
前言:
Android 自带的 xml 文件内 gradient 设置渐变最多只有三种颜色,使用方便但范围受限,不能很好满足各种需求;
本款多色渐变背景板应运而生:
* 1. 支持圆角模式,矩形模式;
* 2. 支持线性、扫描和环形渐变,线性渐变支持0-180角度设置;
* 3. 支持多种颜色设置;(E.g: #ff0000 #00ff00 #0000ff)
使用:
//三种效果:
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_centerInParent="true">
<com.nepalese.virgocomponent.view.VirgoGradientView
android:layout_width="160dp"
android:layout_height="120dp"
app:vgMode="mode_rect"
app:vgRoundRadius="60dp"
app:vgColors="#3f51b1 #5a55ae #7b5fac #8f6aae #a86aa4 #cc6b8e #f18271 #f3a469 #f7c978"
app:vgGradientMode="gradient_linear"
app:vgLinearAngle="15" />
<com.nepalese.virgocomponent.view.VirgoGradientView
android:layout_marginStart="20dp"
android:layout_width="160dp"
android:layout_height="120dp"
app:vgMode="mode_round"
app:vgRoundRadius="15dp"
app:vgColors="#3f51b1 #5a55ae #7b5fac #8f6aae #a86aa4 #cc6b8e #f18271 #f3a469 #f7c978"
app:vgGradientMode="gradient_linear"
app:vgLinearAngle="30" />
<com.nepalese.virgocomponent.view.VirgoGradientView
android:layout_marginStart="20dp"
android:layout_width="120dp"
android:layout_height="120dp"
app:vgMode="mode_round"
app:vgRoundRadius="60dp"
app:vgColors="#3f51b1 #5a55ae #7b5fac #8f6aae #a86aa4 #cc6b8e #f18271 #f3a469 #f7c978"
app:vgGradientMode="gradient_linear"
app:vgLinearAngle="45" />
</LinearLayout>
码源:
1. attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="VirgoGradientView">
<attr name="vgMode" format="integer">
<enum name="mode_round" value="1"/>
<enum name="mode_rect" value="2"/>
</attr>
<attr name="vgGradientMode" format="integer">
<enum name="gradient_linear" value="1"/>
<enum name="gradient_sweep" value="2"/>
<enum name="gradient_radial" value="3"/>
</attr>
<attr name="vgLinearAngle" format="integer"/>
<attr name="vgRoundRadius" format="dimension|reference"/>
<attr name="vgColors" format="string"/>
</declare-styleable>
</resources>
2. VirgoGradientView.java
package com.nepalese.virgocomponent.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.annotation.Nullable;
import com.nepalese.virgocomponent.R;
/**
* @author nepalese on 2024/10/10 10:10
* @usage 多色渐变调板:
* 1. 支持圆角模式,矩形模式;
* 2. 支持线性、扫描和环形渐变,线性渐变支持0-180角度设置;
* 3. 支持多种颜色设置;(E.g: #ff0000 #00ff00 #0000ff)
*/
public class VirgoGradientView extends View {
private static final String TAG = "VirgoGradientView";
public static final int MODE_ROUND = 1;//圆角模式
public static final int MODE_RECT = 2;//矩形模式(默认)
public static final int GRADIENT_LINEAR = 1;//线性渐变
public static final int GRADIENT_SWEEP = 2;//扫描渐变
public static final int GRADIENT_RADIAL = 3;//环形渐变
private Paint mPaint;//画笔
private RectF mRectF;//画布矩形
private Shader mShader;//渐变渲染
private int mWidth, mHeight;//宽高
private int mMode;//图形模式
private int mGradientMode;//渐变模式
private int mAngle;//线性渐变角度(从左->右:0-180)
private int mRoundRadius;//圆角半径(仅圆角模式)
private int[] mColors;//颜色组
public VirgoGradientView(Context context) {
this(context, null);
}
public VirgoGradientView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public VirgoGradientView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
private void init(AttributeSet attrs) {
String strColors;//颜色按顺序,以空格间开
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.VirgoGradientView);
mMode = typedArray.getInt(R.styleable.VirgoGradientView_vgMode, MODE_RECT);
mAngle = typedArray.getInt(R.styleable.VirgoGradientView_vgLinearAngle, 0);
mGradientMode = typedArray.getInt(R.styleable.VirgoGradientView_vgGradientMode, GRADIENT_LINEAR);
mRoundRadius = typedArray.getDimensionPixelSize(R.styleable.VirgoGradientView_vgRoundRadius, 20);
strColors = typedArray.getString(R.styleable.VirgoGradientView_vgColors);
typedArray.recycle();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.FILL);
//初始化默认颜色
initColor(strColors);
}
private void initColor(String strColors) {
if (TextUtils.isEmpty(strColors)) {
strColors = "#69EACB #EACCF8 #6654F1";//默认
}
if (strColors.contains(" ")) {
String[] colors = strColors.split(" ");
mColors = new int[colors.length];
for (int i = 0; i < colors.length; i++) {
try {
mColors[i] = Color.parseColor(colors[i]);
} catch (Exception e) {
mColors[i] = Color.WHITE;
}
}
} else {
//单色
mColors = new int[1];
try {
mColors[0] = Color.parseColor(strColors);
} catch (Exception e) {
//格式异常
Log.e(TAG, "格式异常!");
mColors[0] = Color.WHITE;
}
}
// //彩虹色
// mColors = new int[7];
// mColors[0] = Color.rgb(255,0,0);
// mColors[1] = Color.rgb(255,165,0);
// mColors[2] = Color.rgb(255,255,0);
// mColors[3] = Color.rgb(0,255,0);
// mColors[4] = Color.rgb(0,127,255);
// mColors[5] = Color.rgb(0,0,255);
// mColors[6] = Color.rgb(139,0,255);
}
private void setShader() {
switch (mGradientMode) {
case GRADIENT_LINEAR:
//x0,y0,x1,y1是起始位置和渐变的结束位置
//positions指定颜色数组的相对位置: [0…1], 如果传null,渐变就线性变化
//角度正切值
double tan = Math.tan(Math.PI * mAngle / 180);
if (45 > mAngle && mAngle >= 0) {
//[0,45)
mShader = new LinearGradient(0, (float) ((mHeight - tan * mWidth) / 2), mWidth, (float) ((mHeight + tan * mWidth) / 2), mColors, null, Shader.TileMode.CLAMP);
} else if (135 >= mAngle && mAngle >= 45) {
//[45,135]
if (mAngle == 90) {
mShader = new LinearGradient(mWidth / 2f, 0, mWidth / 2f, mHeight, mColors, null, Shader.TileMode.CLAMP);
} else {
mShader = new LinearGradient((float) ((mWidth - mHeight / tan) / 2), 0, (float) ((mWidth + mHeight / tan) / 2), mHeight, mColors, null, Shader.TileMode.CLAMP);
}
} else if (180 >= mAngle && mAngle > 135) {
mShader = new LinearGradient(mWidth, (float) ((mHeight + tan * mWidth) / 2), 0, (float) ((mHeight - tan * mWidth) / 2), mColors, null, Shader.TileMode.CLAMP);
} else {
//默认左 -> 右
mShader = new LinearGradient(0, 0, mWidth, 0, mColors, null, Shader.TileMode.CLAMP);
}
break;
case GRADIENT_SWEEP:
//cx,cy,圆的中心坐标
mShader = new SweepGradient(mWidth / 2f, mHeight / 2f, mColors, null);
break;
case GRADIENT_RADIAL:
//cx,cy,中心坐标
int max = Math.max(mWidth, mHeight);
mShader = new RadialGradient(mWidth / 2f, mHeight / 2f, max / 2f, mColors, null, Shader.TileMode.CLAMP);
break;
}
mPaint.setShader(mShader);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//在measure之后, layout之前
mRectF = new RectF(0, 0, w, h);
mWidth = w;
mHeight = h;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
setShader();
if (mMode == MODE_RECT) {
//矩形
canvas.drawRect(mRectF, mPaint);
} else {
//圆角
canvas.drawRoundRect(mRectF, mRoundRadius, mRoundRadius, mPaint);
}
}
//api//
public void setmMode(int mMode) {
this.mMode = mMode;
}
public void setmGradientMode(int mGradientMode) {
this.mGradientMode = mGradientMode;
}
public void setmAngle(int mAngle) {
this.mAngle = mAngle % 180;
}
public void setmRoundRadius(int mRoundRadius) {
this.mRoundRadius = mRoundRadius;
}
//颜色种类不少于2
public void setmColors(int[] mColors) {
if (mColors.length < 2) {
return;
}
this.mColors = mColors;
}
}
渐变色网站:
https://webgradients.com/