当前位置 博文首页 > 启舰:自定义控件三部曲之绘图篇(二十)——RadialGradient与水波

    启舰:自定义控件三部曲之绘图篇(二十)——RadialGradient与水波

    作者:[db:作者] 时间:2021-06-30 15:31

    前言:每当感叹自己的失败时,那我就问你,如果让你重新来一次,你会不会成功?如果会,那说明并没有拼尽全力。

    系列文章:

    Android自定义控件三部曲文章索引:http://blog.csdn.net/harvic880925/article/details/50995268

    最近博主实在是太忙了,博客更新实在是太慢了,真是有愧大家。

    这篇将是Shader的最后一篇,下部分,我们将讲述Canvas变换的知识。在讲完Canvas变换以后,就正式进入第三部曲啦,是不是有点小激动呢……

    今天给大家讲的效果是使用RadialGradient来实现水波纹按钮效果,水波纹效果是Android L平台上自带的效果,这里我们就看看它是如何实现的,本篇的最终效果图如下
    在这里插入图片描述

    ###一、RadialGradient详解
    RadialGradient的意思是放射渐变,即它会向一个放射源一样,从一个点开始向外从一个颜色渐变成另一种颜色;

    一、构造函数

    RadialGradient有两个构造函数

    //两色渐变
    RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, Shader.TileMode tileMode)
    //多色渐变
    RadialGradient(float centerX, float centerY, float radius, int[] colors, float[] stops, Shader.TileMode tileMode)
    

    (1)、两色渐变构造函数使用实例
    下面我们来看一下两色渐变构造函数的使用方法。

    RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, Shader.TileMode tileMode)
    

    这个两色渐变的构造函数的各项参数意义如下:

    • centerX:渐变中心点X坐标
    • centerY:渐变中心点Y坐标
    • radius:渐变半径
    • centerColor:渐变的起始颜色,即渐变中心点的颜色,取值类型必须是八位的0xAARRGGBB色值!透明底Alpha值不能省略,不然不会显示出颜色。
    • edgeColor:渐变结束时的颜色,即渐变圆边缘的颜色,同样,取值类型必须是八位的0xAARRGGBB色值!
    • TileMode:与我们前面讲的各个Shader一样,用于指定当控件区域大于指定的渐变区域时,空白区域的颜色填充方式。

    下面我们举个例子来看下用法:

    public class DoubleColorRadialGradientView extends View {
        private Paint mPaint;
        private RadialGradient mRadialGradient;
        private int mRadius = 100;
        public DoubleColorRadialGradientView(Context context) {
            super(context);
            init();
        }
    
        public DoubleColorRadialGradientView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public DoubleColorRadialGradientView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init();
        }
    
        private void init(){
    	    setLayerType(LAYER_TYPE_SOFTWARE,null);
            mPaint = new Paint();
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
    
            mRadialGradient = new RadialGradient(w/2,h/2,mRadius,0xffff0000,0xff00ff00, Shader.TileMode.REPEAT);
            mPaint.setShader(mRadialGradient);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            canvas.drawCircle(getWidth()/2,getHeight()/2,mRadius,mPaint);
        }
    }
    

    代码量不大,这里首先在onSizeChange中,创建RadialGradient实例。onSizeChange会在布局发生改变时调用,onSizeChanged(int w, int h, int oldw, int oldh)传过来四个参数,前两个参数就代表当前控件所应显示的宽和高。有关onSizeChange的具体意义,我们会在第三部曲讲解回调函数流程中具体讲到,这里大家就先理解到这吧。
    在onSizeChange中,我们创建了一个RadialGradient,以控件的中心点为圆点,创建一个半径为mRadius的,从0xffff0000到0xff00ff00的放射渐变。我们这里指定的空白填充方式为Shader.TileMode.REPEAT。
    然后在绘图的时候:

    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(getWidth()/2,getHeight()/2,mRadius,mPaint);
    }
    

    在绘图时,依然是以控件中心点为圆心,画一个半径为mRadius的圆;注意我们画的圆的大小与所构造的放射渐变的大小是一样的,所以不存在空白区域的填充问题。
    效果图如下:
    在这里插入图片描述

    (2)、多色渐变构造函数使用实例
    多色渐变的构造函数如下:

    RadialGradient(float centerX, float centerY, float radius, int[] colors, float[] stops, Shader.TileMode tileMode)
    

    这里与两色渐变不同的是两个函数:

    • int[] colors:表示所需要的渐变颜色数组
    • float[] stops:表示每个渐变颜色所在的位置百分点,取值0-1,数量必须与colors数组保持一致,不然直接crash,一般第一个数值取0,最后一个数值取1;如果第一个数值和最后一个数值并没有取0和1,比如我们这里取一个位置数组:{0.2,0.5,0.8},起始点是0.2百分比位置,结束点是0.8百分比位置,而0-0.2百分比位置和0.8-1.0百分比的位置都是没有指定颜色的。而这些位置的颜色就是根据我们指定的TileMode空白区域填充模式来自行填充!!!有时效果我们是不可控的。所以为了方便起见,建议大家stop数组的起始和终止数值设为0和1.

    下面我们举个例子来看下用法:

    public class MultiColorRadialGradientView extends View {
        private Paint mPaint;
        private RadialGradient mRadialGradient;
        private int mRadius = 100;
        public MultiColorRadialGradientView(Context context) {
            super(context);
            init();
        }
    
        public MultiColorRadialGradientView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public MultiColorRadialGradientView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init();
        }
    
        private void init(){
            setLayerType(LAYER_TYPE_SOFTWARE,null);
            mPaint = new Paint();
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
    
            int[]   colors = new int[]{0xffff0000,0xff00ff00,0xff0000ff,0xffffff00};
            float[] stops  = new float[]{0f,0.2f,0.5f,1f};
            mRadialGradient = new RadialGradient(w/2,h/2,mRadius,colors,stops, Shader.TileMode.REPEAT);
            mPaint.setShader(mRadialGradient);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            canvas.drawCircle(getWidth()/2,getHeight()/2,mRadius,mPaint);
        }
    }
    

    这里主要看下多色渐变的构造方法:

    int[]   colors = new int[]{0xffff0000,0xff00ff00,0xff0000ff,0xffffff00};
    float[] stops  = new float[]{0f,0.2f,0.5f,1f};
    mRadialGradient = new RadialGradient(w/2,h/2,mRadius,colors,stops, Shader.TileMode.REPEAT);
    

    这里构造了一个四色颜色数组,渐变位置对应{0f,0.2f,0.5f,1f},然后创建RadialGradient实例。没什么难度。
    然后在绘画的时候,同样以控件中心为半径,以放射渐变的半径为半径画圆。由于画的圆半径与放射渐变的大小相同,所以不存在空白位置填充的问题,所以TileMode.REPEAT并没有用到。
    效果图如下:
    在这里插入图片描述

    ###二、TileMode重复方式
    TileMode的问题,已经重复讲了几篇文章了,其实虽然每种Shader所表现出来的效果不一样,但是形成原理都是相同的。下面我们再来看一下RadialGradient在不同的TileMode下的具体表现。

    (1)、X、Y轴共用填充参数

    与LinearGradient一样,从构造函数中,可以明显看出RadialGradient只有一个填充模式:

    //两色渐变
    RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, Shader.TileMode tileMode)
    //多色渐变
    RadialGradient(float centerX, float centerY, float radius, int[] colors, float[] stops, Shader.TileMode tileMode)
    

    这就说明了,当填充空白区域时,X轴和Y轴使用同一种填充模式。而不能像BitmapShader那样分别指定X轴与Y轴的填充参数。

    (2)、TileMode.CLAMP——边缘填充

    我们依然使用双色渐变的示例来看下效果,为了显示填充效果,我们这次画一个屏幕大小的矩形:

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    
        mRadialGradient = new RadialGradient(w/2,h/2