Android Paint系列基础+着色器渲染器(实现霓虹灯文字+实现圆形头像+放大镜+倒影+雷达扫描效果)

本文主要是介绍Android Paint系列基础+着色器渲染器(实现霓虹灯文字+实现圆形头像+放大镜+倒影+雷达扫描效果),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  • Paint基础
    • Canvas和Paint的关系
    • Paint方法
      • 构造函数
      • 图形绘制、路径相关
      • Text相关
  • 渲染
    • LinearGradient线性渲染
      • 构造函数1:
      • 构造函数2:
      • Demo
      • 霓虹灯文字效果
    • BitmapShader位图图像着色器
      • 构造函数
      • 圆形头像
      • 放大镜效果
      • 倒影效果
    • ComposeShader组合渲染
      • 构造函数1
      • 构造函数2
      • Demo
    • RadialGradient 径向渲染
      • 构造函数1
      • 构造函数2
      • Demo
    • SweepGradient 扫描渲染
      • 构造函数1
      • 构造函数2
      • Demo
      • 雷达扫描
  • Demo地址

Paint基础

我们知道,view通过调用onDraw(Canvas canvas)来进行绘制,其实是canvas调用底层接口,让GPU进行绘制。

private static native void nDrawOval(long nativeCanvas, float left, float top, float right,float bottom, long nativePaint);private static native void nDrawCircle(long nativeCanvas, float cx, float cy, float radius,long nativePaint);private static native void nDrawArc(long nativeCanvas, float left, float top, float right,float bottom, float startAngle, float sweep, boolean useCenter, long nativePaint);private static native void nDrawRoundRect(long nativeCanvas, float left, float top, float right,float bottom, float rx, float ry, long nativePaint);

Canvas和Paint的关系

canvas在绘制中扮演的是一个中间者,上层调用canvas,设置所有要绘制的参数,然后交给底层,由底层进行绘制,
我们调用绘制的时候,通常都需要传递一个paint参数:
例如:

public void drawRect(@NonNull Rect r, @NonNull Paint paint) {super.drawRect(r, paint);
}  
public void drawRGB(int r, int g, int b) {super.drawRGB(r, g, b);
}public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {super.drawText(text, x, y, paint);
}

Paint类保存关于如何绘制几何图形,文本和位图的样式和颜色信息。而canvas则决定了图形的位置,形状,以及图层等信息。

Paint方法

构造函数

1.Paint():用默认设置创建一个新的paint

2.Paint(int flags):用指定的标志创建一个新的paint

3.Paint(Paint paint):创建一个新的paint,使用指定的paint参数中的属性进行初始化

图形绘制、路径相关

1.set(Paint src):将来自src的字段复制到此画图中

2.setARGB(int a, int r, int g, int b): 设置颜色值,范围[0..255]

3.setAlpha(int a): 设置透明值,范围[0..255]

4.setAntiAlias(boolean aa): 设置画笔是否抗锯齿

5.setColor(int color): 设置颜色值

6.setColorFilter(ColorFilter filter): 设置或清除涂料的颜色过滤器,返回参数

7.setDither(boolean dither): 设置是否使用抖动处理,会使绘制出来的图片颜色更加平滑和饱满、图像更加清晰。

8.setFilterBitmap (boolean filter): 是否对位图进行滤波处理

9.setFlags (int flags): 设置标志

10.setHinting (int mode): 设置提示模式。 可能是HINTING_OFF或HINTING_ON

11.setMaskFilter (MaskFilter maskfilter): 设置实过滤器,现滤镜的效果,如滤化,立体

12.setPathEffect(PathEffect effect): 设置绘制路径的效果,如点画线等

13.setShader (Shader shader): 设置图像着色效果,可以绘制出各种渐变效果

15.setShadowLayer (float radius, float dx, float dy, int shadowColor): 设置阴影层,绘制阴影效果,radius为阴影的角度,dx和dy为阴影在x轴和y轴上的距离,color为阴影的颜色

16.setStrokeCap (Paint.Cap cap): 设置线条样式,Cap.ROUND(圆形)、Cap.SQUARE(方形)、Paint.Cap.BUTT(笔画以路径结束,并且不会超出它)

17.setStrokeJoin (Paint.Join join): 设置线段连接处样式,Join.MITER(结合处为锐角)、Join.Round(结合处为圆弧)、Join.BEVEL(结合处为直线)

18.setStrokeMiter (float miter): 设置笔画的倾斜度

19.setStrokeWidth (float width): 设置画笔宽度

20.setStyle (Paint.Style style): 设置画笔样式,Paint.Style.FILL :填充内部,Paint.Style.FILL_AND_STROKE :填充内部和描边Paint.Style.STROKE :仅描边

19.setXfermode (Xfermode xfermode): 设置图像混合模式

Text相关

1. setElegantTextHeight(boolean elegant):设置油漆的高雅度量标志

2. setFakeBoldText(boolean fakeBoldText):是否设置仿粗体

3. setFontFeatureSettings (String settings):设置字体功能设置

4. setFontVariationSettings (String fontVariationSettings):设置字体变体设置

5. setLetterSpacing (float letterSpacing):设置字符间距

6. setLinearText (boolean linearText):设置是否开启线性文本标识,即设置是否开启文本缓存

7. setStrikeThruText (boolean strikeThruText):设置删除线文本标志

8. setSubpixelText (boolean subpixelText):设置子像素,如果该项为true,将有助于文本在LCD屏幕上的显示效果

9. setTextAlign (Paint.Align align):设置文本对齐方式

10. setTextLocale (Locale locale):设置地理位置,一般直接传入Locale.getDefault(),它用来设置文本的区域比如中文、英文等

11. setTextScaleX (float scaleX):设置文本沿X轴水平缩放值,默认值为1,当值大于1会沿X轴水平放大文本,当值小于1会沿X轴水平缩放文本

12. setTextSize (float textSize):设置文字大小

13. setTextSkewX (float skewX):设置文字倾斜

14. setTypeface (Typeface typeface):设置字体类型

15. setUnderlineText (boolean underlineText):设置是否显示下划线

渲染

Paint通过以下代码设置着色器,着色器是绘制过程中返回水平跨度颜色的对象的基础类,设置之后,用该paint绘制的任何对象(除位图之外)都将从着色器中获取它的颜色。

paint.setShader(Shader shader);

直接子类有:

LinearGradient线性渲染

构造函数1:

public LinearGradient (float x0, float y0, float x1, float y1, int[] colors,  float[] positions, 
                Shader.TileMode tile);x0: 渐变线开始处的x坐标
y0: 渐变线开始出的y坐标
x1: 渐变线结束处的x坐标
y1: 渐变线结束处的x坐标
colors:沿渐变线分布的颜色,该值不能为空。
positions:可能为null, 颜色数组中每个对应颜色的相对位置[0..1](比重)。 如果它为空,则颜色均匀分布。
tile:着色器平铺模式,该值不能为空。

构造函数2:

public LinearGradient (float x0, float y0, float x1, float y1, int color0,int color1, Shader.TileMode tile)x0: 渐变线开始处的x坐标
y0: 渐变线开始出的y坐标
x1: 渐变线结束处的x坐标
y1: 渐变线结束处的x坐标
color0:渐变线开始处的颜色
color1:渐变线结束处的颜色
tile:着色器平铺模式,该值不能为空。

TileMode:

Shader.TileMode CLAMP
边缘拉伸
Shader.TileMode MIRROR
以镜像的方式在水平和垂直两个方向上重复,相邻图像有间隙
Shader.TileMode REPEAT
在水平和垂直两个方向上重复,相邻图像没有间隙

Demo

代码:

public class LGView extends View{private Shader.TileMode tileMode = Shader.TileMode.CLAMP; //默认public LGView(Context context) {super(context);}public LGView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}public LGView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);Paint paint = new Paint();//x0 = 0, y0 = 0, x1 = width, y1 = 0 -> 从左到右渐变//x0 = 0, y0 = 0, x1 = 0, y1 = height -> 从上到下渐变//x0 = 0. y0 = 0, x1 = width, y1 = height -> 从左上角到右下角渐变,对角渐变//x = 0, y0 = 0, x1 = width / 2, y1 = 0时,只有左边的一部分进行了渲染,clamp模式下右半部分用绿色填充了,//而在REPEAT模式下则在水平和垂直两个方向上重复,相邻图像没有间隙,使用MIRROR模式则以镜像的方式在水平和垂直两个方向上重复,相邻图像有间隙LinearGradient linearGradient = new LinearGradient(0, 0, getMeasuredWidth() / 2, 0,new int[]{Color.RED, Color.WHITE, Color.GREEN},null, tileMode);paint.setShader(linearGradient);canvas.drawRect(0,0, getMeasuredWidth(), getMeasuredHeight(), paint);}public void setTileMode(Shader.TileMode tileMode){this.tileMode = tileMode;postInvalidate();}}

效果图:


这里写图片描述

霓虹灯文字效果

代码说明:

public class NeonLightTextView extends TextView {private TextPaint mPaint;private LinearGradient mLinearGradient ;private Matrix mMatrix;private float mTranslate;private float DELTA_VALUE = 20; //每次减少的值public NeonLightTextView(Context context) {super(context);}public NeonLightTextView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}public NeonLightTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);mPaint = getPaint(); //获取当前TextView的画笔String text = getText().toString();float textWith = mPaint.measureText(getText().toString());// 6个文字的宽度int gradientSize = (int) (textWith / text.length() * 6);//draw的时候会从给定的颜色数组里面取值,这里字的颜色为黑色,然后通过alpha透明值的不同来实现霓虹灯效果int[] colors = new int[]{0x22000000, 0xff000000, 0x22000000};//每次渲染的区域为 gradientSize,且横向渲染//我们设置初始从-gradientSize开始mLinearGradient = new LinearGradient(-gradientSize,0,0,0,colors,null,Shader.TileMode.CLAMP);mPaint.setShader(mLinearGradient);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);float textWidth = getPaint().measureText(getText().toString()); //首先获取要绘制的文字的宽度if(mTranslate >= textWidth){mTranslate = 0;}else{mTranslate += DELTA_VALUE;}mMatrix = new Matrix();mMatrix.setTranslate(mTranslate, 0);mLinearGradient.setLocalMatrix(mMatrix);postInvalidateDelayed(50); //实现动画效果}
}

效果图:


这里写图片描述

BitmapShader位图图像着色器

位图图像渲染,用bitMap对绘制的图形进行渲染着色,即用图片对图形进行贴图:

构造函数

调用它来创建一个新的着色器,该着色器将使用位图进行绘制

public BitmapShader (Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)bitmap:在着色器内使用的位图,该值不能为空
tileX Shader.TileModex绘制位图的平铺模式,该值不能为空
tileY Shader.TileModey绘制位图的平铺模式,该值不能为空

TileMode:

Shader.TileMode CLAMP
当所画图形的尺寸大于Bitmap的尺寸的时候,会用Bitmap四边的颜色填充剩余空间,小于的时候则会对Bitmap进行裁剪
Shader.TileMode MIRROR
当绘制的图形尺寸大于Bitmap尺寸时,MIRROR也会用Bitmap重复平铺整个绘图区域,与REPEAT不同的是,两个相邻的Bitmap互为镜像,即类似于倒影效果
Shader.TileMode REPEAT
我们绘制的图形尺寸大于Bitmap尺寸时,会用Bitmap重复平铺整个绘制的区域
setLocalMatrix (Matrix localM)
设置局部矩阵,传空则会充值,如果矩阵的比例值为0,绘图无效

这里顺便说一下BitmapShader和ShapeDrawable,ShapeDrawable是一个绘制原始形状的可绘制对象,而BitmapShader的作用是用图片对图形进行贴图,这两者相结合的作用就是用BitmapShader中的bitmap对ShapeDrawable进行贴图,同时ShapeDrawable中可获取Paint对象,因此可通过下面代码来设置bitmapShader对象。

getPaint().setShader(bitmapShader)

我们实现两个效果

圆形头像

代码+说明

public class BitmapShaderCircularView extends ImageView {private Paint paint; private int radius;  //圆形头像的半径private float mScale; public BitmapShaderCircularView(Context context) {super(context);}public BitmapShaderCircularView(Context context, AttributeSet attrs) {super(context, attrs);}public BitmapShaderCircularView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//首先在view调用测量的时候,我们根据宽高取得一个正方形//并初始化半径int minTarget = Math.min(getMeasuredWidth(), getMeasuredHeight());radius = minTarget / 2;//重新设置宽高setMeasuredDimension(minTarget, minTarget);}@Overrideprotected void onDraw(Canvas canvas) {paint = new Paint();Bitmap bitmap = Utils.drawable2Bitmap(getDrawable());if (bitmap == null){return;}//构建BitmapShader对象,设置为CLAMP模式,当所画图形的尺寸小于bitmap尺寸的时候,会对bitmap进行裁剪BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);//我们要对bitmap进行缩放处理//radius * 2为宽高,再取bitmap的宽高中值小的进行相除,取得bitmap缩放比例mScale = (radius * 2.0f) / Math.min(bitmap.getWidth(), bitmap.getHeight());//设置矩阵,针对bitmap作缩放处理Matrix matrix = new Matrix();matrix.setScale(mScale, mScale);bitmapShader.setLocalMatrix(matrix);paint.setShader(bitmapShader);//这里开始画一个圆形,并对paint设置的BitmapShader中的bitmap进行裁剪处理canvas.drawCircle(radius, radius, radius, paint);}
}

效果图


这里写图片描述

放大镜效果

先说一下放大镜的原理,我们先绘制一个原图,即原bitmap,然后再这个原bitmap的基础上放大n倍得到放大后的bitmap,即放大的bitmap,通过创建BitmapShader将放大的bitmap关联,再通过ShapeDrawable创建一个圆形图形,裁剪放大的bitmap,获取放大镜内容,然后再绘制到原图上面,便可实现放大镜效果。

代码+说明

public class BitmapShaderZoomView extends View {private static final int ENLARGE_FACTOR = 2; //放大因素,即放大镜的放大倍数private static final int RADIUS  = 100; //放大镜的半径private Bitmap bitmap; //原图private Bitmap enlargeBitmap; //放大 2 倍后的bitmap对象private ShapeDrawable shapeDrawable; //放大镜内容private Matrix mMatrix; //矩阵public BitmapShaderZoomView(Context context) {super(context);}public BitmapShaderZoomView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}public BitmapShaderZoomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);initBitmap(w, h);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//我们首先绘制原bitmap图canvas.drawBitmap(bitmap, 0 , 0 , null);//这里绘制放大镜的内容//原理是通过BitmapShader的位图处理,通过制定的CLAMP模式,当所画图形的尺寸大于Bitmap的尺寸的时候,会用Bitmap四边的颜色填充剩余空间,小于的时候则会对Bitmap进行裁剪//这里会对放大后的bitmap进行裁剪,裁剪的大小和形状由shapeDrawable决定shapeDrawable.draw(canvas);}/*** 初始化bitmap,BitmapShader,ShapeDrawable等对象** @param width 当前view的宽* @param height 当前view的高* */private void initBitmap(int width, int height){bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.head);bitmap = Bitmap.createScaledBitmap(bitmap, width, height,true); //首先将原图铺满屏幕//我们这里将原图放大两倍enlargeBitmap = Bitmap.createScaledBitmap(bitmap,bitmap.getWidth() * ENLARGE_FACTOR,bitmap.getHeight() * ENLARGE_FACTOR,true);//这里设置BitmapShader的bitmap为放大后的bitmapBitmapShader bitmapShader = new BitmapShader(enlargeBitmap, Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);//再创建一个ShapeDrawable图形对象shapeDrawable = new ShapeDrawable(new OvalShape());shapeDrawable.getPaint().setShader(bitmapShader);//设置初始放大镜位置和大小shapeDrawable.setBounds(0,0,RADIUS * 2,RADIUS * 2);mMatrix = new Matrix();}@Overridepublic boolean onTouchEvent(MotionEvent event) {int x = (int) event.getX();int y = (int) event.getY();//将放大的图片往相反的方向移动//这里的目的是通过移动放大的图,然后取得放大镜的内容,之所以是RADIUS - x * ENLARGE_FACTOR和RADIUS - y * ENLARGE_FACTOR)//是因为设置的放大镜的初始坐标为(RADIUS,RADIUS)mMatrix.setTranslate(RADIUS - x * ENLARGE_FACTOR  , RADIUS - y * ENLARGE_FACTOR);shapeDrawable.getPaint().getShader().setLocalMatrix(mMatrix);// 这里抠出对应的放大的图的 放大镜内容区域shapeDrawable.setBounds(x - RADIUS,y - RADIUS, x + RADIUS, y + RADIUS);invalidate();return true;}
}

原图

这里写图片描述

效果图

这里写图片描述

倒影效果

代码+说明

public class BitmapShaderInvertView extends View {private Bitmap bitmap;private BitmapShader bitmapShader;private ShapeDrawable shapeDrawable;public BitmapShaderInvertView(Context context) {super(context);init();}public BitmapShaderInvertView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}public BitmapShaderInvertView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init(){bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.head);bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.MIRROR);shapeDrawable = new ShapeDrawable(new OvalShape());shapeDrawable.getPaint().setShader(bitmapShader);
//        shapeDrawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight()); //原图大小shapeDrawable.setBounds(0, 0, bitmap.getWidth() * 2, bitmap.getHeight() * 2); //原图大小}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);shapeDrawable.draw(canvas);}
}

当设置

tileX = Shader.TileMode.CLAMP;
tileY = Shader.TileMode.CLAMP;

的时候,效果为:
这里写图片描述

当设置

tileX = Shader.TileMode.CLAMP;
tileY = Shader.TileMode.REPEAT;

的时候,效果为:
这里写图片描述

当设置

tileX = Shader.TileMode.CLAMP;
tileY = Shader.TileMode.MIRROR;

的时候,效果为:
这里写图片描述

当设置

tileX = Shader.TileMode.REPEAT;
tileY = Shader.TileMode.REPEAT;

的时候,效果为:
这里写图片描述

当设置

tileX = Shader.TileMode.MIRROR;
tileY = Shader.TileMode.MIRROR;

的时候,效果为:
这里写图片描述

ComposeShader组合渲染

对Xfermode不了解的可以看下我的文章:
Android Paint系列之Xfermode + 刮刮卡效果实现

构造函数1

public ComposeShader (Shader shaderA, Shader shaderB, Xfermode mode)

构造函数2

public ComposeShader (Shader shaderA, Shader shaderB, PorterDuff.Mode mode)

分别是两个shader和一个mode,这个mode的作用指定是混合图像的算法,即ComposeShader 的作用就是将两个shader进行一个混合。

Demo

代码+说明

public class ComposeShaderView extends View {private Bitmap bitmap;private ComposeShader composeShader;private Paint paint;public ComposeShaderView(Context context) {super(context);init();}public ComposeShaderView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}public ComposeShaderView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init(){bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.heart);//我们创建了一个位图图像渲染器,和一个线性渲染器,再通过ComposeShader将两者以 'mode' 这种混合模式进行混合BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);LinearGradient linearGradient = new LinearGradient(0, 0, bitmap.getWidth(), 0, new int[]{Color.RED, Color.WHITE, Color.YELLOW}, null, Shader.TileMode.CLAMP);composeShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY); //直接叠加paint = new Paint();paint.setShader(composeShader);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (bitmap == null|| composeShader == null){return;}canvas.drawRect(0, 0, bitmap.getWidth(), bitmap.getHeight(), paint);}
}

效果图:
这里写图片描述

RadialGradient 径向渲染

创建一个着色器,以给定中心和半径绘制径向渐变

构造函数1

public RadialGradient (float centerX, float centerY, float radius, int[] colors, float[] stops, Shader.TileMode tileMode)centerX float:半径中心的x坐标
centerY float:半径中心的y坐标
radius:必须是正数,此渐变圆半径
colors:必须是正数,此渐变圆半径颜色,要分布在圆的中心和边缘之间的颜色,该值不能为空。
stops:可能为空, 有效值在0.0f和1.0f之间。 颜色数组中每个对应颜色的相对位置, 如果为null,则颜色均匀分布在圆的中心和边缘之间
tileMode:着色器平铺模式,该值不能为空。

构造函数2

public RadialGradient (float centerX, float centerY, float radius, int centerColor, int edgeColor, Shader.TileMode tileMode)centerX:半径中心的x坐标
centerY:半径中心的y坐标
radius:必须是正数,此渐变圆半径
centerColor:圆形中心的颜色。
edgeColor:圆的边缘的颜色。
tileMode:着色器平铺模式
该值不能为空。

RadialGradient实现一个从圆心向外圆的颜色渐变效果,坐标(centerX,centerY)是圆心,即颜色起始中心(第一个构造函数中为colors[0],第二个构造函数则为centerColor),radius为圆的半径,在圆的半径处的颜色是edgeColor(第一个构造函数中为colors[colors.length - 1],第二个构造函数则为edgeColor),这样就确定了当位置从圆心移向圆的轮廓时,颜色逐渐从colors[0]渐变到colors[colors.length - 1],或者从centerColor渐变到edgeColor。RadialGradient也支持TileMode参数,有以下三个取值:CLAMP 、REPEAT 和 MIRROR。

CLAMP:颜色从圆心颜色向圆周边缘颜色渐变,在圆以外的空间用colors[colors.length - 1]或edgeColor蓝色填充
REPEAT:颜色以圆心颜色到圆周边缘颜色作为一个渐变周期从圆心向外扩散
MIRROR:周期性地交替变换从圆心向外扩散

Demo

代码+说明

public class RadialGradientView extends View {public RadialGradientView(Context context) {super(context);}public RadialGradientView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}public RadialGradientView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//创建一个圆心为(300, 300)半径为100的渐变圆RadialGradient radialGradient = new RadialGradient(300,300,100,new int[]{Color.RED, Color.BLUE, Color.GREEN, Color.YELLOW},null, Shader.TileMode.CLAMP);//只有两个渐变颜色,分别为圆心其实颜色 -> 边缘颜色
//        RadialGradient radialGradient = new RadialGradient(300,
//                300,
//                100,
//                Color.RED,
//                Color.WHITE, Shader.TileMode.CLAMP);Paint paint = new Paint();paint.setShader(radialGradient);canvas.drawCircle(300, 300, 300, paint);}
}

当设置tileMode为CLAMP时,效果为:
这里写图片描述

当设置tileMode为REPEAT时,效果为:
这里写图片描述

当设置tileMode为MIRROR时,效果为:
这里写图片描述

SweepGradient 扫描渲染

创建360度颜色旋转渐变效果,具体来说颜色是围绕中心点360度顺时针旋转的,起点位置是90°

构造函数1

public SweepGradient (float cx, float cy, int[] colors, float[] positions)cx:中心的x坐标
cy:中心的y坐标
colors:要在中心周围分配的颜色,阵列中必须至少有2种颜色,该值不能为空
positions:可能为NULL,颜色数组中每个对应颜色的相对位置,从0开始到1.0结束, 如果这些值不是单调的,则绘图可能会产生意想不到的结果,如果位置为NULL,则颜色自动均匀分布,该值可能为空

构造函数2

public SweepGradient (float cx, float cy, int color0, int color1)
cx:中心的x坐标
cy:中心的y坐标
color0:在扫描开始时使用的颜色
color1:在扫描结束时使用的颜色

Demo

代码

public class SweepGradientView extends View {public SweepGradientView(Context context) {super(context);}public SweepGradientView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}public SweepGradientView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);int canvasWidth = canvas.getWidth();int canvasHeight = canvas.getHeight();int radius = 300;SweepGradient sweepGradient = new SweepGradient(canvasWidth / 2f,canvasHeight / 2f,new int[]{Color.RED, Color.BLUE, Color.YELLOW, Color.GREEN},null);Paint paint = new Paint();paint.setShader(sweepGradient);canvas.drawCircle(canvasWidth / 2f, canvasHeight / 2f, radius, paint);}
}

效果图:
这里写图片描述

我们用canvas.drawCircle()方法画了一个圆形,将SweepGradient的中心点设置为圆形的中心,并且设置了四个颜色,起始颜色为红色,结束颜色绿色,我们可以看到,从顺时针90°开始,到360°颜色从红色到绿色渐变。

android会根据设置的colors数组,进行颜色渐变处理,同时可指定positions,该位置数组中每一个位置与颜色数组中每个颜色一一对应,position取值范围为[0,1.0],0和1分别表示90°和360°的话,0.25表示180°,0.5表示270°,0.75表示0°,若positions为null,那么colors将设置为等间距

雷达扫描

我们根据SweepGradient 扫描渲染的原理,来实现雷达的扫描效果,原理是:
通过SweepGradient的扫描式渲染,即颜色渐变的原理,可在底图上层,增加一层扫描层,该扫描层采取

sweepGradient = new SweepGradient(viewWidth / 2, viewHeight / 2, Color.TRANSPARENT,Color.GREEN);

即起始颜色为透明,结束颜色为绿色,进行颜色渐变,再通过不停的旋转扫描层,实现扫描层不断扫描的动画。

代码+说明

public class SweepGradientRadarView extends View {private int viewWidth, viewHeight; //view的宽高private float[] RADIUS_RATIO = {0.05f, 0.1f, 0.15f, 0.2f, 0.25f, 0.30f}; //六个圆的半径比例private SweepGradient sweepGradient; // 扫描渲染器private Matrix rotateMatrix = new Matrix(); //旋转需要的矩阵private int rotateDelta = 10; //每次旋转增加的角度,影响旋转速度private int rotateAngle; //扫描层旋转的角度private Paint circlePaint; //画圆的画笔private Paint radarPaint; //画雷达的画笔public SweepGradientRadarView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}public SweepGradientRadarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}public SweepGradientRadarView(Context context) {super(context);init();}private void init(){//画圆的画笔circlePaint = new Paint();circlePaint.setStyle(Paint.Style.STROKE); //描边circlePaint.setStrokeWidth(1); //描边宽度circlePaint.setAntiAlias(true); //抗锯齿circlePaint.setColor(Color.RED);//画雷达效果的画笔radarPaint = new Paint();radarPaint.setStyle(Paint.Style.FILL_AND_STROKE); //填充radarPaint.setAntiAlias(true); //抗锯齿//开始执行雷达动画任务post(task);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//先绘制六个圆for (int i = 0; i < RADIUS_RATIO.length; i++) {canvas.drawCircle(viewWidth / 2, viewHeight / 2, viewWidth * RADIUS_RATIO[i], circlePaint);}//画布旋转,需要先保存状态,转换完之后还原canvas.save();//这里定义的扫描渲染器,设置起始的颜色为透明值,结束的颜色值为绿色sweepGradient = new SweepGradient(viewWidth / 2, viewHeight / 2, Color.TRANSPARENT,Color.GREEN);radarPaint.setShader(sweepGradient); //设置着色器canvas.setMatrix(rotateMatrix); //设置旋转矩阵//直接画渲染最大圆canvas.drawCircle(viewWidth / 2, viewHeight / 2, viewWidth * RADIUS_RATIO[RADIUS_RATIO.length - 1], radarPaint);//还原画布状态canvas.restore();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);//初始化view的宽高,以将雷达放置到view中心位置viewWidth = getMeasuredWidth();viewHeight = getMeasuredHeight();}private Runnable task = new Runnable() {@Overridepublic void run() {//每次增加rotateDelta角度rotateAngle = (rotateAngle + rotateDelta) % 360; // 旋转角度 对360取余//设置旋转角度和旋转中心位置rotateMatrix.postRotate(rotateDelta, viewWidth / 2, viewHeight / 2);invalidate(); //重绘postDelayed(task, 50); //延迟50ms后,再次进行绘制}};
}

效果图
这里写图片描述

Demo地址

demo地址:https://github.com/samlss/PaintShader

这篇关于Android Paint系列基础+着色器渲染器(实现霓虹灯文字+实现圆形头像+放大镜+倒影+雷达扫描效果)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/293887

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

Python如何实现PDF隐私信息检测

《Python如何实现PDF隐私信息检测》随着越来越多的个人信息以电子形式存储和传输,确保这些信息的安全至关重要,本文将介绍如何使用Python检测PDF文件中的隐私信息,需要的可以参考下... 目录项目背景技术栈代码解析功能说明运行结php果在当今,数据隐私保护变得尤为重要。随着越来越多的个人信息以电子形

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

使用Python快速实现链接转word文档

《使用Python快速实现链接转word文档》这篇文章主要为大家详细介绍了如何使用Python快速实现链接转word文档功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 演示代码展示from newspaper import Articlefrom docx import