首页 > 代码库 > Android Shader渲染以及实现水波纹霓虹文字雷达等效果
Android Shader渲染以及实现水波纹霓虹文字雷达等效果
Shader概述
Shader是绘图过程中的着色器,实现绘制各种不同的效果,比如镜像,水波纹,雷达等等,Shader有以下五个子类:
- BitmapShader用于Bitmap图片的渲染
- ComposeShader用于混合渲染
- LinearGradient用于线性渲染
- RadialGradient用于环形渲染
- SweepGradient用于梯度渲染
Shader的三种模式TileMode
- CLAMP 当绘制的区域超过了原始的大小,超出的区域就会用边缘的颜色进行拉伸
- REPEAT 重复水平或者竖直方向的图片
- MIRROR 用图片的镜像填充
BitmapShader
构造方法
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 specType 1为宽, 其他为高 * @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);}@Overrideprotected 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);}@Overrideprotected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawOval(0, 0, mWidth, mHeight, mPaint);}}
关于Mode可以看看这里
Demo下载链接
Android Shader渲染以及实现水波纹霓虹文字雷达等效果