Android Shader渲染以及实现水波纹霓虹文字雷达等效果

来源:互联网 发布:java 字节码 汇编 编辑:程序博客网 时间:2024/04/30 06:02

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的意思是放射渐变,即它会向一个放射源一样,从一个点开始向外从一个颜色渐变成另一种颜色。

构造方法

  1. RadialGradient(float centerX, float centerY, float radius,int centerColor, int edgeColor, TileMode tileMode)
    - centerX:渐变中心点X坐标
    - centerY:渐变中心点Y坐标
    - radius:渐变半径
    - centerColor:渐变中心的颜色,取值类型必须是八位的0xAARRGGBB色值
    - edgeColor:渐变结束的颜色
    - tileMode:填充的模式
  2. 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

线性渲染,对某一区域实现线性渐变效果。

构造函数

  1. LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,TileMode tile)
    • x0,y0是渐变的起点坐标
    • x1,y1是渐变的终点坐标
    • color0是开始颜色
    • color1是结束颜色
  2. 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轴正方向逆时针旋转一周而形成的扫描效果的渲染形式

构造函数

  1. SweepGradient(float cx, float cy, int colors[], float positions[])
    • cx,cy:中心坐标点
    • colors、positions同样和之前一样
  2. 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

组合渲染

构造函数

  1. ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)
    • shaderA :渲染器A
    • shaderB :渲染器B
    • Xfermode :两种渲染器组合的模式,Xfermode对象
  2. 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下载链接

3 0
原创粉丝点击