Android自定义View(五)着色器Shader

介绍:

Shader is the based class for objects that return horizontal spans of colors
during drawing. A subclass of Shader is installed in a Paint calling
paint.setShader(shader). After that any object (other than a bitmap) that is
drawn with that paint will get its color(s) from the shader.

Shader共有五个子类:

  • BitmapShader 瓷砖渐变
  • LinearGradient 线性渐变
  • RadialGradient 光束渐变
  • SweepGradient 梯度渐变
  • ComposeShader 混合渐变

1.BitmapShader 瓷砖渐变

Shader used to draw a bitmap as a texture. The bitmap can be repeated or
mirrored by setting the tiling mode.

一个构造方法:

public BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) {}

参数:

  • bitmap:图片
  • tilex X轴瓷砖模式,共三种
  • tileY y轴瓷砖模式,共三种

3种模式:

  • Shader.TileMode.CLAMP 拉伸模式

CLAMP 模式就是在View的宽度或者高度大于图片的宽或高时,在绘制图片的最后一帧拉伸展示。

public class BitmapShader_CLAMP extends View{

    private Paint paint;

    public BitmapShader_CLAMP(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }

    private void initPaint() {
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.img);
        BitmapShader bitmapShader = new BitmapShader(bitmap, android.graphics.Shader.TileMode.CLAMP, android.graphics.Shader.TileMode.CLAMP);
        paint.setShader(bitmapShader);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        setLayerType(View.LAYER_TYPE_SOFTWARE,null); //去掉硬件加速
        int x = getWidth() / 2;
        int y = getHeight() / 2;
        int radius = Math.min(getWidth(),getHeight()) / 2;
        canvas.drawCircle(x,y,radius,paint);
    }

    private void mMeasure(int widthMeasureSpec, int heightMeasureSpec){
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        if(widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(720,500);
        }else if(widthMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(720,heightSize);
        }else if(heightMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(widthSize,500);
        }
    }
}
  • Shader.TileMode.MIRROR 镜像模式

MIRROR 模式当view的高度或者宽度大于图片的高或宽时,先倒立,再重复绘制。

BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
  • Shader.TileMode.REPEAT 重复模式

REPEAT  模式与MIRROR模式差不多,只是没有倒立显示。只有重复

BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, android.graphics.Shader.TileMode.REPEAT);

2.线性渐变 LinearGradient

共有两个构造方法:

    public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[], @Nullable float positions[], @NonNull TileMode tile) {}
    public LinearGradient(float x0, float y0, float x1, float y1,@ColorInt int color0, @ColorInt int color1, @NonNull TileMode tile) {}

区别:

  • colors可以指定多种颜色
  • positions指定渐变位置

参数:

  • x0: 开始绘制的x轴坐标
  • y0: 开始绘制的y轴坐标
  • x1: 结束绘制的x轴坐标
  • y1: 结束绘制的y轴坐标
  • color0: 渐变的颜色
  • color1: 渐变的颜色
  • tilemode: 瓷砖模式

构造方法:

  • 方法一

只能指定两种渐变颜色

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /**
         * 参数:
         * x0: 开始绘制的x轴坐标
         * y0: 开始绘制的y轴坐标
         * x1: 结束绘制的x轴坐标
         * y1: 结束绘制的y轴坐标
         * color0: 渐变的颜色
         * color1: 渐变的颜色
         * tilemode: 瓷砖模式
         */
        LinearGradient linearGradient = new LinearGradient(0,0,300,300, Color.RED,Color.BLUE, Shader.TileMode.REPEAT);
        paint.setShader(linearGradient);
        canvas.drawRect(0,0,Math.min(getWidth(),getHeight()),Math.min(getWidth(),getHeight()),paint);
    }
  • 方法二

可以指定多种渐变颜色,多个开始渐变的位置

        int[] colors = new int[]{Color.BLUE,Color.RED,Color.YELLOW};
        float[] positions = new float[]{0.0f,0.5f,1.0f};
        LinearGradient linearGradient = new LinearGradient(0,0,600,600, colors,positions, Shader.TileMode.REPEAT);

3.光束渐变

有两个构建方法:

public RadialGradient(float centerX, float centerY, float radius,@NonNull @ColorInt int colors[], @Nullable float stops[],@NonNull TileMode tileMode) {}
public RadialGradient(float centerX, float centerY, float radius,@ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode) {}

区别:

  • colors可以指定多种颜色
  • positions指定渐变位置

参数:

  • centerX 圆心的X轴坐标
  • centerY 圆点的Y轴坐标
  • radius 半径
  • centerColor 中心颜色
  • edgeColor 边缘颜色
  • tileMode 瓷砖模式

两种构造方法

  • 方法一

只能指定两种渐变颜色

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /**
         * centerX 圆心的X轴坐标
         * centerY 圆点的Y轴坐标
         * radius 半径
         * centerColor 中心颜色
         * edgeColor 边缘颜色
         * tileMode 瓷砖模式
         */
        RadialGradient radialGradient = new RadialGradient(getWidth()/2,getHeight()/2,Math.min(getWidth(),getHeight())/2,Color.RED,Color.BLACK, Shader.TileMode.CLAMP);
        paint.setShader(radialGradient);
        canvas.drawRect(0,0,getWidth(),Math.min(getWidth(),getHeight()),paint);
    }
  • 方法二

可以指定多种渐变颜色,多个开始渐变的位置

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        final int[] colors = new int[]{Color.BLACK,Color.RED ,Color.BLACK};
        final float[] positions = new float[]{0f, 0.5f ,1f};
        RadialGradient radialGradient = new RadialGradient(getWidth()/2,getHeight()/2,Math.min(getWidth(),getHeight())/2,colors,positions, Shader.TileMode.CLAMP);
        paint.setShader(radialGradient);
        canvas.drawRect(0,0,getWidth(),Math.min(getWidth(),getHeight()),paint);
    }

4.梯度渐变

同样有两个构造方法

public SweepGradient(float cx, float cy,@NonNull @ColorInt int colors[], @Nullable float positions[]) {}
public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1) {}

区别:

  • colors可以指定多种颜色
  • positions指定渐变位置

参数:

  • cx 中心点的X轴坐标
  • cy 中心点的Y轴坐标
  • color0 开始渐变的颜色
  • color1 结束渐变的颜色

两个构建方法:

  • 方法一

只能指定两种渐变颜色

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /**
         * cx 中心点的X轴坐标
         * cy 中心点的Y轴坐标
         * color0 开始渐变的颜色
         * color1 结束渐变的颜色
         */
        SweepGradient sweepGradient = new SweepGradient(Math.min(getWidth(),getHeight())/2,Math.min(getWidth(),getHeight())/2,Color.RED,Color.BLUE);
        paint.setShader(sweepGradient);
        canvas.drawRect(0,0,Math.min(getWidth(),getHeight()),Math.min(getWidth(),getHeight()),paint);
    }
  • 方法二

可以指定多种渐变颜色,多个开始渐变的位置

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        final int[] colors = new int[]{Color.MAGENTA, Color.CYAN,Color.YELLOW,Color.BLUE ,Color.RED};
        final float[] positions = new float[]{0f, 0.25f,0.5f ,0.75f,1f};
        SweepGradient sweepGradient = new SweepGradient(Math.min(getWidth(),getHeight())/2,Math.min(getWidth(),getHeight())/2,colors,positions);
        paint.setShader(sweepGradient);
        canvas.drawRect(0,0,Math.min(getWidth(),getHeight()),Math.min(getWidth(),getHeight()),paint);
    }

5.混合渐变

A subclass of shader that returns the composition of two other shaders, combined by
an {@link android.graphics.Xfermode} subclass.

顾名思义,就是将多种渐变模式混合在一起。

两个构建方法:

public ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, @NonNull Xfermode mode) {}
public ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB,@NonNull PorterDuff.Mode mode) {}

区别:

  • 可以指定PorterDuff
  • 设置Xfermode

参数:

  • shaderA 渐变模式
  • shaderB 渐变模式
  • mode 混合模式

两个构建方法:

  • 方法一

可以指定一种混合模式,梯度渐变 + 光束渐变,要关闭硬件加速

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        setLayerType(LAYER_TYPE_SOFTWARE,null);

        SweepGradient sweepGradient = new SweepGradient(getWidth()/2,getHeight()/2,Color.RED,Color.BLUE);
        RadialGradient radialGradient = new RadialGradient(getWidth()/2,getHeight()/2,Math.min(getWidth(),getHeight())/2,Color.RED,Color.BLACK, Shader.TileMode.CLAMP);
        ComposeShader composeShader = new ComposeShader(sweepGradient,radialGradient, PorterDuff.Mode.SCREEN);
        paint.setShader(composeShader);
        canvas.drawRect(0,0,getWidth(),getHeight(),paint);
    }
  • 方法二

可以指定一种Xfermode

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        setLayerType(LAYER_TYPE_SOFTWARE,null);

        SweepGradient sweepGradient = new SweepGradient(getWidth()/2,getHeight()/2,Color.RED,Color.BLUE);
        RadialGradient radialGradient = new RadialGradient(getWidth()/2,getHeight()/2,Math.min(getWidth(),getHeight())/2,Color.RED,Color.BLACK, Shader.TileMode.CLAMP);
        ComposeShader composeShader = new ComposeShader(sweepGradient,radialGradient,new PorterDuffXfermode(PorterDuff.Mode.ADD));
        paint.setShader(composeShader);
        canvas.drawRect(0,0,getWidth(),getHeight(),paint);
    }

Demo

public class ReflectView extends View {
    private Paint mPaint;
    private Bitmap dstBitmap, srcBitmap;
    private PorterDuffXfermode xfermode;
    private int x, y;

    public ReflectView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        //原图Bitmap
        dstBitmap = decodeBitmapFormRes(getResources(), R.drawable.timg,540, 960);
        //垂直翻转
        Matrix matrix = new Matrix();
        matrix.setScale(1f, -1f);
        //图片向右倾斜
        matrix.preSkew(-0.5f, 0);
        //倒影Bitmap
        srcBitmap = Bitmap.createBitmap(dstBitmap, 0, 0, dstBitmap.getWidth(), dstBitmap.getHeight(), matrix, true);
        //初始化画笔
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
        //屏幕宽度
        int screenW = getResources().getDisplayMetrics().widthPixels;
        //起始点
        x = screenW / 2 - dstBitmap.getWidth() / 2;
        y = 0;
        //设置渐变矩形
        mPaint.setShader(new LinearGradient(x, dstBitmap.getHeight(), x, dstBitmap.getHeight() + dstBitmap.getHeight() / 2, 0xDD000000, Color.TRANSPARENT, Shader.TileMode.CLAMP));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        setLayerType(LAYER_TYPE_SOFTWARE,null);
        //绘制背景
        canvas.drawColor(Color.BLACK);
        //绘制原图
        canvas.drawBitmap(dstBitmap, x, y, null);
        //绘制倒影图片
        canvas.drawBitmap(srcBitmap, x, dstBitmap.getHeight(), null);
        mPaint.setXfermode(xfermode);
        //绘制渐变层
        canvas.drawRect(x, dstBitmap.getHeight(), x + dstBitmap.getWidth()+x , dstBitmap.getHeight() * 2, mPaint);
        mPaint.setXfermode(null);
    }

    /**
     * 测量
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int wSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int wSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int hSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int hSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        if (wSpecMode == MeasureSpec.AT_MOST && hSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(300, 900);
        } else if (wSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(300, hSpecSize);
        } else if (hSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(wSpecSize, 900);
        }
    }

    /**
     * 图片的缩放
     */
    private Bitmap decodeBitmapFormRes(Resources resources, int resId, int targetWidth, int targetHeight) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        options.inJustDecodeBounds = false;
        BitmapFactory.decodeResource(resources, resId, options);
        int inSample = calculateInSample(options, targetWidth, targetHeight);
        options.inSampleSize = inSample;
        return BitmapFactory.decodeResource(resources, resId, options);
    }

    private int calculateInSample(BitmapFactory.Options options, int targetWidth, int targetHeight) {
        if (targetWidth <= 0 || targetHeight <= 0) {
            return 1;
        }
        int inSample = 1;
        final int rawWidth = options.outWidth;
        final int rawHeight = options.outHeight;
        if (rawWidth > targetWidth || rawHeight > targetHeight) {
            final int halfWidth = rawWidth / 2;
            final int halfHeight = rawHeight / 2;
            while ((halfWidth / inSample >= targetWidth) && (halfHeight / inSample >= targetHeight)) {
                inSample *= 2;
            }
        }
        return inSample;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值