少年乘勇气,百战过乌孙。这篇文章主要讲述Android Shader渲染以及实现水波纹霓虹文字雷达等效果相关的知识,希望能为你提供帮助。
Shader概述 Shader是绘图过程中的着色器,
实现绘制各种不同的效果,
比如镜像,
水波纹,
雷达等等,
Shader有以下五个子类:
- BitmapShader用于Bitmap图片的渲染
- ComposeShader用于混合渲染
- LinearGradient用于线性渲染
- RadialGradient用于环形渲染
- SweepGradient用于梯度渲染
Shader的三种模式TileMode
- CLAMP 当绘制的区域超过了原始的大小, 超出的区域就会用边缘的颜色进行拉伸
- REPEAT 重复水平或者竖直方向的图片
- MIRROR 用图片的镜像填充
BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY)
- bitmap: 需要着色的位图
- tileX: X方向的填充模式
- tileY: Y方向的填充模式
实例
文章图片
这是一个充值后的影魔, 直接看看代码的实现:
public class ShaderView extends View {
Bitmap mBitmap;
BitmapShader mBitmapShader;
Paint mPaint;
int mWidth;
int mHeight;
public ShaderView(Context context) {
super(context);
init();
}private void init() {
mPaint =
new Paint(Paint.ANTI_ALIAS_FLAG);
mBitmap =
BitmapFactory.decodeResource(getResources(), R.mipmap.test);
mBitmapShader =
new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.MIRROR);
}public ShaderView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}@
Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth =
getMeasuredWidth();
mHeight =
getMeasuredHeight();
}@
Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setShader(mBitmapShader);
canvas.drawRect(0, 0, mWidth, mHeight, mPaint);
}
}
这里的X轴采用的是CLAMP, 所以右边是拉伸边缘的像素点, Y轴采用的是MIRROR, 上下都是镜像的。
现在我们把drawRect注释点, 来绘制一个圆
//canvas.drawRect(0, 0, mWidth, mHeight, mPaint);
canvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2, mPaint);
文章图片
可以看到以上的效果, 所以可不可以这样理解呢, 这里的BitmapShader就是给绘制的内容加上我们所设置的Bitmap作为背景。
RadialGradient 主要用于在某一区域内实现环形的渐变效果, RadialGradient的意思是放射渐变, 即它会向一个放射源一样, 从一个点开始向外从一个颜色渐变成另一种颜色。
构造方法
RadialGradient(float centerX, float centerY, float radius,int centerColor, int edgeColor, TileMode tileMode)
- centerX: 渐变中心点X坐标
- centerY: 渐变中心点Y坐标
- radius: 渐变半径
- centerColor: 渐变中心的颜色, 取值类型必须是八位的0xAARRGGBB色值
- edgeColor: 渐变结束的颜色
- tileMode: 填充的模式RadialGradient(float centerX, float centerY, float radius,int colors[], float stops[], @ NonNull TileMode tileMode)
- int[] colors: 表示所需要的渐变颜色数组
- float[] stops: 表示每个渐变颜色所在的位置百分点, 取值0-1
文章图片
文章图片
代码
public class RadialGradientView extends View {
private RadialGradient mRadialGradient;
private Paint mPaint =
new Paint(Paint.ANTI_ALIAS_FLAG);
int mWidth;
int mHeight;
public RadialGradientView(Context context) {
super(context);
}public RadialGradientView(Context context, AttributeSet attrs) {
super(context, attrs);
}@
Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth =
getMeasuredWidth();
mHeight =
getMeasuredHeight();
//mRadialGradient =
new RadialGradient(mWidth / 2, mHeight / 2, mWidth / 2, 0xffff0000, 0xff00ff00, Shader.TileMode.CLAMP);
int[] colors =
new int[]{0xffff0000, 0xff00ff00, 0xff0000ff, 0xffffff00};
float[] stops =
new float[]{0f, 0.3f, 0.7f, 1f};
mRadialGradient =
new RadialGradient(mWidth / 2, mHeight / 2, mWidth / 2, colors, stops, Shader.TileMode.REPEAT);
mPaint.setShader(mRadialGradient);
}@
Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2, mPaint);
}
}
再次修改一下
@
Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth =
getMeasuredWidth();
mHeight =
getMeasuredHeight();
int[] colors =
new int[]{0xffff0000, 0xff00ff00, 0xff0000ff, 0xffffff00};
float[] stops =
new float[]{0f, 0.3f, 0.7f, 1f};
mRadialGradient =
new RadialGradient(mWidth / 2, mHeight / 2, 200, colors, stops, Shader.TileMode.REPEAT);
mPaint.setShader(mRadialGradient);
}@
Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(new Rect(0,0,mWidth,mHeight),mPaint);
}
效果如下
文章图片
水波纹效果实现
文章图片
代码如下, 就不多说了
public class RippleView extends TextView {
private int mX, mY;
private ObjectAnimator mAnimator;
private int DEFAULT_RADIUS =
50;
private int mCurRadius =
0;
private RadialGradient mRadialGradient;
private Paint mPaint =
new Paint();
public RippleView(Context context) {
super(context);
}public RippleView(Context context, AttributeSet attrs) {
super(context, attrs);
}@
Override
public boolean onTouchEvent(MotionEvent event) {
if (mX !=
event.getX() || mY !=
event.getY()) {
mX =
(int) event.getX();
mY =
(int) event.getY();
setRadius(DEFAULT_RADIUS);
}if (event.getAction() =
=
MotionEvent.ACTION_DOWN) {
//如果不返回true,
后续的事件收不到
return true;
} else if (event.getAction() =
=
MotionEvent.ACTION_UP) {
if (mAnimator !=
null &
&
mAnimator.isRunning()) {
mAnimator.cancel();
}
if (mAnimator =
=
null) {
//这里第一个对象传递当前对象,
在当前对象中设置了setRadius方法,
所以这里传递radius
//每当值变化时就会调用这个setRadius方法
mAnimator =
ObjectAnimator.ofInt(this, "
radius"
, DEFAULT_RADIUS, getWidth());
}
mAnimator.setInterpolator(new AccelerateInterpolator());
mAnimator.addListener(new Animator.AnimatorListener() {
@
Override
public void onAnimationStart(Animator animation) {
}@
Override
public void onAnimationEnd(Animator animation) {
setRadius(0);
}@
Override
public void onAnimationCancel(Animator animation) {
}@
Override
public void onAnimationRepeat(Animator animation) {
}
});
mAnimator.start();
}return super.onTouchEvent(event);
}//注意这里的方法名必须是setRadius
public void setRadius(final int radius) {
mCurRadius =
radius;
if (mCurRadius >
0) {
mRadialGradient =
new RadialGradient(mX, mY, mCurRadius, 0x00FFFFFF, 0xFF58FAAC, Shader.TileMode.CLAMP);
mPaint.setShader(mRadialGradient);
}
invalidate();
}@
Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(mX, mY, mCurRadius, mPaint);
}
}
LinearGradient 线性渲染, 对某一区域实现线性渐变效果。
构造函数
LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,TileMode tile)
- x0,y0是渐变的起点坐标
- x1,y1是渐变的终点坐标
- color0是开始颜色
- color1是结束颜色
LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],TileMode tile)
- colors和positions意义和之前的RadialGradient一样。
文章图片
public class LinearGradientView extends View {
Paint mPaint =
new Paint();
LinearGradient mLinearGradient;
int[] colors =
new int[]{
0xFFFF0000,
0xffFF7F00,
0xffFFFF00,
0xff00FF00,
0xff00FFFF,
0xff0000FF,
0xff8B00FF};
public LinearGradientView(Context context) {
super(context);
}public LinearGradientView(Context context, AttributeSet attrs) {
super(context, attrs);
}@
Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mLinearGradient =
=
null) {
mLinearGradient =
new LinearGradient(0, 0, 0, 400, colors, null, Shader.TileMode.CLAMP);
mPaint.setShader(mLinearGradient);
}
canvas.drawRect(new Rect(0,0,getWidth(),getHeight()),mPaint);
}
}
霓虹文字效果
文章图片
这里实现主要是继承TextView, 获得它绘制文字的Paint, 给这个Paint设置LinearGradient的Shader, 把这个Shader从左边开始向右移动, 实现霓虹效果。
public class LinearGradientText extends TextView {
Paint mPaint;
LinearGradient mLinearGradient;
private Matrix mMatrix;
private int mX;
public LinearGradientText(Context context) {
super(context);
init();
}public LinearGradientText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}private void init() {
//注意这里必须是TextView的Paint,
因为绘制文字就是用这个Paint
mPaint =
getPaint();
mMatrix =
new Matrix();
}private void initAnimtor(int width) {
ValueAnimator animator =
ValueAnimator.ofInt(0, width * 2);
//我们设置value的值为0-getMeasureWidth的3 倍
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@
Override
public void onAnimationUpdate(ValueAnimator animation) {
mX =
(Integer) animation.getAnimatedValue();
postInvalidate();
}
});
animator.setRepeatMode(ValueAnimator.RESTART);
//重新播放
animator.setRepeatCount(ValueAnimator.INFINITE);
//无限循环
animator.setDuration(2000);
animator.start();
}@
Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//设置LinearGradient,绘制的范围这里设置的是-w到w,
相当于两个宽度,
然后把Shader向右移动实现了效果
mLinearGradient =
new LinearGradient(-w, 0, w, 0, new int[]{getCurrentTextColor(), Color.RED, Color.YELLOW, Color.BLUE, getCurrentTextColor(),}
, null, Shader.TileMode.CLAMP);
mPaint.setShader(mLinearGradient);
initAnimtor(w);
}@
Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mMatrix.reset();
mMatrix.preTranslate(mX, 0);
mLinearGradient.setLocalMatrix(mMatrix);
}
}
SweepGradient 梯度渲染, 是指在某一中心以x轴正方向逆时针旋转一周而形成的扫描效果的渲染形式
构造函数
SweepGradient(float cx, float cy, int colors[], float positions[])
- cx,cy: 中心坐标点
- colors、positions同样和之前一样
SweepGradient(float cx, float cy, int color0, int color1)
- cx, cy中心坐标点
- color0、color1开始和结束的颜色
文章图片
@
Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width =
w;
height =
h;
mSweepGradient =
new SweepGradient(w / 2, h / 2, colors, null);
mPaint.setShader(mSweepGradient);
}@
Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(new Rect(0, 0, width, height), mPaint);
}
雷达扫描实现
文章图片
/**
* Created by lzy on 2017/4/13.
*/
public class RadarView extends View {private static final int MSG_WHAT =
10086;
private static final int DELAY_TIME =
20;
//设置默认宽高,
雷达一般都是圆形,
所以我们下面取宽高会去Math.min(宽,高)
private final int DEFAULT_WIDTH =
200;
private final int DEFAULT_HEIGHT =
200;
private int mRadarRadius;
//雷达的半径private Paint mRadarPaint;
//雷达画笔private Paint mRadarBg;
//雷达底色画笔private int radarCircleCount =
4;
//雷达圆圈的个数,
默认4个private int mRadarLineColor =
Color.WHITE;
//雷达线条的颜色,
默认为白色private int mRadarBgColor =
Color.BLACK;
//雷达圆圈背景色
private Shader radarShader;
//paintShader//雷达扫描时候的起始和终止颜色
private int startColor =
0x0000ff00;
private int endColor =
0xaa00ff00;
public RadarView(Context context) {
this(context, null);
}public RadarView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}public RadarView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
mRadarPaint =
new Paint(Paint.ANTI_ALIAS_FLAG);
//设置抗锯齿
mRadarPaint.setColor(mRadarLineColor);
//画笔颜色
mRadarPaint.setStyle(Paint.Style.STROKE);
//设置空心的画笔,
只画圆边
mRadarPaint.setStrokeWidth(2);
//画笔宽度mRadarBg =
new Paint(Paint.ANTI_ALIAS_FLAG);
//设置抗锯齿
mRadarBg.setColor(mRadarBgColor);
//画笔颜色
mRadarBg.setStyle(Paint.Style.FILL);
//设置空心的画笔,
只画圆边radarShader =
new SweepGradient(0, 0, startColor, endColor);
matrix =
new Matrix();
}//初始化,
拓展可设置参数供布局使用
private void init(Context context, AttributeSet attrs) {
if (attrs !=
null) {
TypedArray ta =
context.obtainStyledAttributes(attrs, R.styleable.RadarView);
startColor =
ta.getColor(R.styleable.RadarView_startColor, startColor);
endColor =
ta.getColor(R.styleable.RadarView_endColor, endColor);
mRadarBgColor =
ta.getColor(R.styleable.RadarView_bgColor, mRadarBgColor);
mRadarLineColor =
ta.getColor(R.styleable.RadarView_lineColor, mRadarLineColor);
radarCircleCount =
ta.getInteger(R.styleable.RadarView_circleCount, radarCircleCount);
ta.recycle();
}
}@
Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width =
measureSize(1, DEFAULT_WIDTH, widthMeasureSpec);
int height =
measureSize(0, DEFAULT_HEIGHT, heightMeasureSpec);
int measureSize =
Math.max(width, height);
//取最大的 宽|高
setMeasuredDimension(measureSize, measureSize);
}/**
* 测绘measure
*
* @
param specType1为宽,
其他为高
* @
param contentSize 默认值
*/
private int measureSize(int specType, int contentSize, int measureSpec) {
int result;
//获取测量的模式和Size
int specMode =
MeasureSpec.getMode(measureSpec);
int specSize =
MeasureSpec.getSize(measureSpec);
if (specMode =
=
MeasureSpec.EXACTLY) {
result =
Math.max(contentSize, specSize);
} else {
result =
contentSize;
if (specType =
=
1) {
// 根据传人方式计算宽
result +
=
(getPaddingLeft() +
getPaddingRight());
} else {
// 根据传人方式计算高
result +
=
(getPaddingTop() +
getPaddingBottom());
}
}return result;
}@
Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mRadarRadius =
Math.min(w / 2, h / 2);
}//旋转的角度
private int rotateAngel =
0;
@
Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(mRadarRadius, mRadarRadius);
//将画板移动到屏幕的中心点mRadarBg.setShader(null);
canvas.drawCircle(0, 0, mRadarRadius, mRadarBg);
//绘制底色(默认为黑色),
可以使雷达的线看起来更清晰for (int i =
1;
i <
=
radarCircleCount;
i+
+
) {//根据用户设定的圆个数进行绘制
canvas.drawCircle(0, 0, (float) (i * 1.0 / radarCircleCount * mRadarRadius), mRadarPaint);
//画圆圈
}canvas.drawLine(-mRadarRadius, 0, mRadarRadius, 0, mRadarPaint);
//绘制雷达基线 x轴
canvas.drawLine(0, mRadarRadius, 0, -mRadarRadius, mRadarPaint);
//绘制雷达基线 y轴//canvas.rotate(rotateAngel,0,0);
//设置颜色渐变从透明到不透明
mRadarBg.setShader(radarShader);
canvas.concat(matrix);
canvas.drawCircle(0, 0, mRadarRadius, mRadarBg);
}private Matrix matrix;
private Handler mHandler =
new Handler() {
@
Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
rotateAngel +
=
3;
postInvalidate();
matrix.reset();
matrix.preRotate(rotateAngel, 0, 0);
mHandler.sendEmptyMessageDelayed(MSG_WHAT, DELAY_TIME);
}
};
public void startScan() {
mHandler.removeMessages(MSG_WHAT);
mHandler.sendEmptyMessage(MSG_WHAT);
}public void stopScan() {
mHandler.removeMessages(MSG_WHAT);
}
}
ComposeShader 组合渲染
构造函数
ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)
- shaderA : 渲染器A
- shaderB : 渲染器B
- Xfermode : 两种渲染器组合的模式,Xfermode对象
ComposeShader(Shader shaderA, Shader shaderB, Mode mode)
- Mode : 两种渲染器组合的模式,ProterDuff.Mode对象
文章图片
这里是结合了BitmapShader和LinearGradient实现的效果
public class ComposeShaderView extends View { Bitmap mBitmap; BitmapShader mBitmapShader; Paint mPaint; LinearGradient mLinearGradient; ComposeShader mComposeShader; int mWidth; int mHeight; public ComposeShaderView(Context context) { super(context); init(); }public ComposeShaderView(Context context, AttributeSet attrs) { super(context, attrs); init(); }private void init() { mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test_3); mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); }@ Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = w; mHeight = h; mLinearGradient = new LinearGradient(0, 0, w, h, new int[] { Color.WHITE, Color.LTGRAY, Color.TRANSPARENT, Color.GREEN }, null, Shader.TileMode.CLAMP); mComposeShader = new ComposeShader(mBitmapShader, mLinearGradient, PorterDuff.Mode.MULTIPLY); mPaint.setShader(mComposeShader); }@ Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawOval(0, 0, mWidth, mHeight, mPaint); } }
【Android Shader渲染以及实现水波纹霓虹文字雷达等效果】关于Mode可以看看这里
Demo下载链接
推荐阅读
- Kotlin编程之AndroidStudio(包括3.0与2.x版本)配置与使用
- Android属性allowBackup安全风险浅析
- Android之自定义EditText光标和下划线颜色
- android studio执行 Information:Gradle tasks [:app:assembleDebug]失败处理
- 模拟图像处理与数字图像处理
- DB2验证和配置数据库详细步骤
- 最新ASP.Net常见的面试题和答案合集介绍
- 最新Spring常见面试题和答案合集(面试必备)
- 最新50+个AWS常见面试题和答案合集介绍