Android自定义控件之Shader(着色器/渲染器)

Android自定义控件之Shader(着色器/渲染器)

在Paint有一个方法

mPaint.setShader(Shader shader);

Shader有五个子类:

Shader五个子类

BitmapShader,
ComposeShader,
LinearGradient,
RadialGradient,
SweepGradient

在这几种子类的构造中可能会用到Shader.TileMode

Shader.TileMode有三种模式:

  1. Shader.TileMode.CLAMP 最后一个像素进行拉伸填充
  2. Shader.TileMode.MIRROR 镜像
  3. Shader.TileMode.REPEAT 重复
1.BitmapShader : 用一张图片进行着色

它只有一个构造方法:

/**
 * @param bitmap    着色中使用的位图
 * @param tileX     X方向上绘图模式
 * @param tileY     Y方向上绘图模式
 */
BitmapShader (Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)

学习了这些知识点可以做一个小练习,望远镜效果

public class TelescopeView extends View {

    //画笔:一个绘制圆的画笔,一个绘制边框的画笔
    private Paint mPaint,strokePaint;
    //圆心
    private float posX, posY;
    //半径
    private int radios = 200;
    //是否显示望远镜头
    private boolean isShow;

    public TelescopeView(Context context) {
        this(context, null);
    }

    public TelescopeView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TelescopeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    //初始化
    private void init() {
        //创建望远镜画笔
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //设置图片着色器
        mPaint.setShader(new BitmapShader(BitmapFactory.decodeResource(getResources(), R.mipmap.one_bg), Shader.TileMode.REPEAT, Shader.TileMode.MIRROR));

        //创建边框画笔
        strokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        strokePaint.setStyle(Paint.Style.STROKE);
        strokePaint.setColor(Color.BLACK);
        strokePaint.setStrokeWidth(10);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.GRAY);
        if (isShow) {
            canvas.drawCircle(posX, posY, radios, mPaint);
            canvas.drawCircle(posX,posY, radios,strokePaint);
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                isShow = true;
                posX = event.getX();
                posY = event.getY();
                postInvalidate();
                break;
            case MotionEvent.ACTION_MOVE:
                posX = event.getX();
                posY = event.getY();
                postInvalidate();
                break;
            case MotionEvent.ACTION_UP:
                isShow = false;
                postInvalidate();
                break;
        }

        return true;
    }
}

望远镜

2.LinearGradient 线性着色器

构造:

/** 
    @param x0       开始的X坐标
    @param y0       开始的Y坐标
    @param x1       结束的X坐标
    @param y1       结束的Y坐标
    @param  color0  起始的颜色
    @param  color1  结束的颜色
    @param  tile    TileMode模式
    */
    public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,
            TileMode tile)
/** Create a shader that draws a linear gradient along a line.
        @param x0           开始的X坐标
        @param y0           开始的Y坐标
        @param x1           结束的X坐标
        @param y1           结束的Y坐标
        @param  colors      颜色组
        @param  positions   这个参数可以为null,
                            如果为null则所有颜色均匀分布,
                            如果不为空则数组个数必须与颜色的个数相等,
                            并且元素大小在0-1之间,代表从百分之多少开始对应的颜色
        @param  tile        TileMode模式
    */
    public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],
            TileMode tile)

了解了构造就可以来简单实现以下了

构造 1:

public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,TileMode tile)
mPaint.setShader(new LinearGradient(0,0,getWidth(),getHeight(), Color.RED,Color.GREEN, Shader.TileMode.CLAMP));

线性1

因为在这行代码中我设置的是自定义控件整个的大小,所以Shader.TileMode改变了并看不出来什么影响。

如果我设置成这样就可以看出效果了

mPaint.setShader(new LinearGradient(0,0,getWidth()/2,getHeight()/2, Color.RED,Color.GREEN, Shader.TileMode.REPEAT));

线性2

变成了这个样子,其他效果可以自己来做一做

构造 2:

public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],TileMode tile)
mPaint.setShader(new LinearGradient(0,0,getWidth(),getHeight(),new int[]{Color.BLUE,Color.CYAN,Color.RED,Color.YELLOW,Color.GREEN},new float[]{0,0.3f,0.7f,0.8f,0.9f}, Shader.TileMode.MIRROR));

效果:

线性3

用这个着色器可以做一个小练习,图片的倒影效果,附上自定义控件代码

public class ReflectionsView extends View {

    //倒影和源图的间距
    private static final int JULI = 10;
    //画笔
    private Paint mPaint;
    //源图和倒影图
    private Bitmap srcBit,daoBit;
    //图片的起始X,Y
    private int bitX,bitY;
    //倒影图片的起始和宽高
    float startX,startY, daoWid, daoHei;

    public ReflectionsView(Context context) {
        this(context,null);
    }

    public ReflectionsView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public ReflectionsView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    private void init(Context context) {
        //获取源图
        srcBit = BitmapFactory.decodeResource(getResources(), R.mipmap.one_bg);
        //通过矩阵翻转源图生成倒影
        Matrix matrix = new Matrix();
        matrix.setScale(1f,-1f);
        //生成倒影图
        daoBit = Bitmap.createBitmap(srcBit,0,srcBit.getHeight()-srcBit.getHeight()/3,srcBit.getWidth(),srcBit.getHeight()/3,matrix,true);
        //获取屏幕宽高
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(dm);
        int sWid = dm.widthPixels;
        int sHei = dm.heightPixels;
        bitX = sWid/2-srcBit.getWidth()/2;
        bitY = sHei/2-srcBit.getHeight()/2;

        startX = bitX+srcBit.getWidth()/2;
        startY = bitY+ JULI +srcBit.getHeight();
        daoWid = startX;
        daoHei = startY + srcBit.getHeight()/3;
        //设置线性着色器
        LinearGradient linearGradient = new LinearGradient(startX,startY, daoWid, daoHei,0x55000000, Color.TRANSPARENT, Shader.TileMode.CLAMP);
        //创建画笔
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //设置着色器
        mPaint.setShader(linearGradient);
        //设置混合模式Xfermode;
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制源图
        canvas.drawBitmap(srcBit,bitX,bitY,null);
        //保存画布,用于处理Xfermode
        int sl = canvas.saveLayer(bitX,startY,bitX+srcBit.getWidth(), daoHei,null,Canvas.ALL_SAVE_FLAG);
        //绘制源图
        canvas.drawBitmap(daoBit,bitX,bitY+ JULI +srcBit.getHeight(),null);
        //绘制倒影的矩形
        canvas.drawRect(0,0,getWidth(),getHeight(),mPaint);
        //还原
        canvas.restoreToCount(sl);

    }
}

倒影

3.SweepGradient 角度渐变着色器

构造:

    /**
    * @param cx       该中心的X坐标
    * @param cy       该中心的Y坐标
    * @param color0   起始颜色
    * @param color1   结束颜色
    */
    public SweepGradient(float cx, float cy, int color0, int color1)
/**
     * A subclass of Shader that draws a sweep gradient around a center point.
     *
     * @param cx        该中心的X坐标
     * @param cy        该中心的Y坐标
     * @param colors    颜色组
     * @param positions 这个参数可以为null,
                        如果为null则所有颜色均匀分布,
                        如果不为空则数组个数必须与颜色的个数相等,
                        并且元素大小在0-1之间,代表从百分之多少开始对应的颜色
     */
public SweepGradient(float cx, float cy,
                         int colors[], float positions[])

这个类使用是蛮简单的,类似LinearGradient,就不累赘了

效果是这个样子

角度1

角度2

4.RadialGradient 径向渐变着色器

构造:

/** 
    @param centerX     圆心的X坐标
    @param centerY     圆心的Y坐标
    @param radius      径向渐变半径
    @param centerColor 中心起始颜色
    @param edgeColor   结束颜色
    @param tileMode    TileMode模式
    */
public RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, TileMode tileMode)
/** Create a shader that draws a radial gradient given the center and radius.
        @param centerX  圆心的X坐标
        @param centerY  圆心的Y坐标
        @param radius   径向渐变半径
        @param colors   颜色组
        @param stops    这个参数可以为null,
                        如果为null则所有颜色均匀分布,
                        如果不为空则数组个数必须与颜色的个数相等,
                        并且元素大小在0-1之间,代表从百分之多少开始对应的颜色
        @param tileMode TileMode模式
    */
    public RadialGradient(float centerX, float centerY, float radius,
               int colors[], float stops[], TileMode tileMode)

例 1:

mPaint.setShader(new RadialGradient(getWidth()/2,getHeight()/2,getWidth(), Color.YELLOW,Color.RED, Shader.TileMode.CLAMP));

效果:

径向1

例 2:

mPaint.setShader(new RadialGradient(getWidth()/2,getHeight()/2,getWidth(), new int[]{Color.BLUE,Color.CYAN,Color.RED,Color.YELLOW,Color.GREEN},null, Shader.TileMode.CLAMP));

径向2

这个有点吓人了,噗噗噗,将就看吧,嘿嘿

5.ComposeShader 着色器组合

构造:

/** 
    @param shaderA  渲染器A,Shader及其子类对象
    @param shaderB  渲染器B,Shader及其子类对象
    @param mode     两种渲染器组合的模式,ProterDuff.Mode对象
*/
    public ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
/** 
    @param shaderA  渲染器A,Shader及其子类对象
    @param shaderB  渲染器B,Shader及其子类对象
    @param mode     两种渲染器组合的模式,Xfermode对象
    */
    public ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)

使用方法也很简单

linearGradient = new LinearGradient(0,0,200,200,new int[]{Color.RED,Color.YELLOW,Color.WHITE,Color.BLACK,Color.CYAN},null, Shader.TileMode.REPEAT);
radialGradient = new RadialGradient(getWidth()/2,getHeight()/2,60,new int[]{Color.RED,Color.YELLOW,Color.WHITE,Color.BLACK,Color.CYAN},null, Shader.TileMode.MIRROR);
composeShader = new ComposeShader(linearGradient, radialGradient, PorterDuff.Mode.ADD);

效果:

混合

注:混合着色器需要关闭硬件加速,否则无法显示。

setLayerType(LAYER_TYPE_SOFTWARE,null);

本文在之后会更新对应着色器的实例,向望远镜那样的。
我是个走在android“不归路”上的小白~~希望可以和大神们学习到更多东西


最后附上aigestudio大神的非常好的学习系列文章:http://blog.csdn.net/column/details/androidcustomview.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值