Android绘图Canvas十八般武器之Shader详解及实战篇(下)
来源:互联网 发布:致远软件合肥 编辑:程序博客网 时间:2024/06/05 22:49
前言
上一篇《Android绘图Canvas十八般武器之Shader篇(上)》 我们知道了Bitmap的用法,及TileMode的详细情况。接下来,这一篇作为整个知识体系的下半部要讲的是Shader的其它几个子类。
首先声明,网上很多称之为渲染,如图形渲染,线性渲染等,而在这里我更喜欢称为渐变。
LinearGradient 线性渐变渲染器
LinearGradient中文翻译过来就是线性渐变的意思。线性渐变通俗来讲就是给起点设置一个颜色值如#faf84d,终点设置一个颜色值如#CC423C,然后在一个区域内绘图,这个图像的颜色将呈现非常美妙的效果,颜色会从起点颜色到终点颜色过渡。给一张图,大家直观感受一下
我们看LinearGradient的API,发现它只有两个构造方法,非常简单。
LinearGradient (float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile)//x0 和y0是颜色渐变的起点坐标。//x1和y1是颜色渐变的终点坐标。//color0是起点颜色值 //color0是终点颜色值。//tile 就是TileMode类型参数,这个我们上一篇已经讲过了。
LinearGradient的用法。
//1 创建LinearGradient对象,并设置它的起点坐标,终点坐标,起点颜色值,终点颜色值,然后设置TileModemShader = new LinearGradient(0,0,w,0,Color.parseColor("#faf84d"), Color.parseColor("#CC423C"), Shader.TileMode.CLAMP); //2 将Shader赋值给Paint对象。mPaint.setShader(mShader); //3 绘制图形canvas.drawRect(0,0,w,h/2,mPaint);
用法非常简单。
LinearGradient还有一个构造方法。
LinearGradient (float x0, float y0, float x1, float y1, int[] colors, float[] positions, Shader.TileMode tile)
需要注意的是,这里有一个int[] colors
和float[] positions
它们代表什么意思呢?
实际上LinearGradient除了可以指定起点颜色值和终点颜色值外,还有可以指定许多中间颜色值。就如彩虹一般。而colors[]
数组存放的就是这样的颜色值组合。大家看看代码和图片效果就可能直观感受到。
//渐变的是一个颜色序列(#faf84d,#003449,#808080,#cc423c)mShader = new LinearGradient(0,0,w,0,new int[]{Color.parseColor("#faf84d"),Color.parseColor("#003449"), Color.parseColor("#808080"), Color.parseColor("#CC423C")},null,Shader.TileMode.CLAMP);mPaint.setShader(mShader);canvas.drawRect(0,0,w,h/2,mPaint);
颜色很丰富是不是?颜色从一个颜色过渡到另外一个颜色直到过渡到终点颜色。
大家有没有注意到,我将上面代码中的float[] positon
置为null
,而它代表了什么呢?它其实与colors
数组对应,代表了各个颜色值在位置,positions
数组中的值大小范围从0.0到1.0,0.0代表起点位置,1.0代表终点位置。如果这个数组被置为空的话,颜色就会平均分配。 ,如果这个数组不为空呢?我们结合代码效果来讲解。
mShader = new LinearGradient(0,0,w,0,new int[]{Color.parseColor("#faf84d"),Color.parseColor("#003449"), Color.parseColor("#808080"), Color.parseColor("#CC423C")},new float[]{0.0f,0.6f,0.8f,1.0f},Shader.TileMode.CLAMP);mPaint.setShader(mShader);canvas.drawRect(0,0,w,h/2,mPaint);
代码中colors[]
并没有改变,只是多了positon[]
,效果却不一样了。
new int[]{Color.parseColor("#faf84d"),Color.parseColor("#003449"), Color.parseColor("#808080"), Color.parseColor("#CC423C")}new float[]{0.0f,0.6f,0.8f,1.0f}// #faf84d对应的position值是0.0 所以为起点位置。// #003449对应0.6 所以这个颜色位置起点到终点中间0.6比率的地方。// #808080对应0.8 这个颜色在0.8比率的地方// #cc423c对应1.0 这个颜色为终点处的颜色
需要注意的是,position[]
数组中的数组最好是由小到大,这是为什么呢?它不支持0.8 然后再到0.6之类。大家看代码。
mShader = new LinearGradient(0,0,w,0,new int[]{Color.parseColor("#faf84d"),Color.parseColor("#003449"), Color.parseColor("#808080"), Color.parseColor("#CC423C")},new float[]{0.6f,0.8f,0.2f,0.0f},Shader.TileMode.CLAMP);
可以看到颜色可以从0.6的位置过渡到0.8,后面的就不起作用了。
RadialGradient 环行渲染器
我喜欢称它为径向渐变,因为PHOTOSHOP中就对应有径向渐变的概念。
径向渐变,所谓径向就是辐射状,由中心向四周辐射。
径向渐变也只有两个构造方法,基本用法跟线性渐变差不多。
RadialGradient (float centerX, float centerY, float radius, int centerColor, int edgeColor, Shader.TileMode tileMode)//centerX 圆心的X坐标//centerY 圆心的Y坐标//radius 圆的半径//centerColor 中心颜色//edgeColor 边缘颜色//tileMode 这个不用介绍了吧?
上代码。
mShader = new RadialGradient(w/2,h/2,w/2,Color.parseColor("#faf84d"), Color.parseColor("#CC423C"), Shader.TileMode.CLAMP);mPaint.setShader(mShader);canvas.drawRect(0,0,w,h,mPaint);
效果:
RadialGradient (float centerX, float centerY, float radius, int[] colors, float[] stops, Shader.TileMode tileMode)
同LinearGradient一样,这里也有一个颜色数组和位置数组,意义也是一样的,stop[]
也可以为null,如果为null的话,color[]
数组的颜色就会平均分配在区域之中。否则,它对应的颜色就会按照比例填充。
mShader = new RadialGradient(w/2,h/2,w/2,new int[]{Color.parseColor("#00aa00"),Color.parseColor("#880033"), Color.parseColor("#F8795A"), Color.parseColor("#CC423C")},new float[]{0.0f,0.2f,0.8f,1.0f}, Shader.TileMode.CLAMP);mPaint.setShader(mShader);canvas.drawRect(0,0,w,h,mPaint);
SweepGradient 梯度渐变渲染器
梯度渐变,或者叫做扫描渐变。我觉得扫描更适合吧,它是指从x轴出发,以逆时钟为方向,以扫描360度形成的区域进行颜色的变换。
SweepGradient (float cx, float cy, int color0, int color1)//color0是起始颜色//color1是终止颜色
代码示例:
mShader = new SweepGradient(w/2,h/2,Color.RED,Color.BLUE);mPaint.setShader(mShader);canvas.drawRect(0,0,w,h,mPaint);
效果:
SweepGradient (float cx, float cy, int[] colors, float[] positions)
大家应该也明白这个方法中每个参数的含义。
mShader = new SweepGradient(w/2,h/2,new int[]{Color.RED,Color.CYAN,Color.YELLOW, Color.GREEN,Color.MAGENTA,Color.BLUE},new float[]{0.0f,0.2f,0.3f,0.4f,0.8f,1.0f});mPaint.setShader(mShader);canvas.drawRect(0,0,w,h,mPaint);
我们把颜色丰富点,本来想弄成赤橙黄绿青蓝紫,结果因为懒,就随便弄了点,效果如下:
ComposeShader 组合渲染器
混合渲染,在这里我又开始称Shader为渲染了,因为ComposeShader不仅仅用于颜色,它能将两个Shader对象参考Xfermode规则进行颜色混合。
网上的一张图:
这张图详细的解释了混合模式的组合效果,我机会我也写一篇相关博文。
再看ComposeShader的两个构造方法。
ComposeShader (Shader shaderA, Shader shaderB, Xfermode mode)ComposeShader (Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
接下来,我们写代码验证一下。
实战1
- 编写1个BitmapShader.
- 编写1个RadiasGradient。
- 将它们进行混合产生新的Shader.
- 以新的Shader绘制一个圆。
public class CircleView extends View { private Paint mPaint; private Shader mShader; public CircleView(Context context) { this(context,null); } public CircleView(Context context, AttributeSet attrs) { this(context, attrs,0); } public CircleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPaint = new Paint(); mPaint.setAntiAlias(true); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //这里为了方便演示,将尺寸固定为400*400 setMeasuredDimension(400,400); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int w = getWidth(); int h = getHeight(); int radius = w <= h ? w/2 : h/2; Bitmap bmp = BitmapFactory.decodeResource(getResources(),R.drawable.repeat); Bitmap result = Bitmap.createScaledBitmap(bmp,w,h,false); //1. 编写1个BitmapShader. BitmapShader bitmapShader = new BitmapShader(result, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); //2. 编写1个RadiasGradient。 RadialGradient radialGradient = new RadialGradient(radius,radius,radius,Color.BLACK,Color.TRANSPARENT, Shader.TileMode.CLAMP); //3. 将它们进行混合产生新的Shader. ComposeShader composeShader = new ComposeShader(bitmapShader,radialGradient,new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); mPaint.setShader(composeShader); //4. 以新的Shader绘制一个圆。 canvas.drawCircle(w/2,h/2,radius,mPaint); }}
我们来看看混合后的效果是怎么样的。
哇,好梦幻的狗狗。
实战2 倒影功能
以前刚开始学Android的时候,项目里面要用到倒影,当时的自己是写不出来的,好在网上有现成的代码可以copy。现在我们可以运用ComposeShader来实现这么一个View。
需求分析
- 倒影与原图比例为1:4。
- 倒影与原图之间有5px的间隙。
- 倒影的下边缘不能太平整了,要尽量跟真实的一致。
好了为了节省篇幅,我只粘贴onDraw()
中的代码。
protected void onDraw(Canvas canvas) { super.onDraw(canvas); //定义各种宽高 int bmpWidth = 200; int bmpHeight = 200; int gap = 5; int reflectionHeight = bmpHeight / 4; //绘制原图 Bitmap bmp = BitmapFactory.decodeResource(getResources(),R.drawable.repeat); Bitmap result = Bitmap.createScaledBitmap(bmp,bmpWidth,bmpHeight,false); canvas.drawBitmap(result,0,0,null); canvas.save(); //向下移动准备在原图下方绘制倒影 canvas.translate(0,bmpHeight+gap); Matrix m = new Matrix(); m.postScale(-1f,1f); m.postRotate(-180); //将原图水平翻转 Bitmap texture = Bitmap.createBitmap(result,0,0,result.getWidth(),result.getHeight(),m,false); //创建BitmapShader和LinearShader。 BitmapShader bitmapShader = new BitmapShader(texture, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); LinearGradient linearGradient = new LinearGradient(0,0,0,reflectionHeight,Color.BLACK,Color.TRANSPARENT, Shader.TileMode.CLAMP); ComposeShader composeShader = new ComposeShader(bitmapShader,linearGradient,new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); mPaint.setShader(composeShader); //以混合模式绘制矩形区域,可以获得倒影效果。 canvas.drawRect(0,0,bmpWidth,reflectionHeight,mPaint); canvas.restore();}
效果:
倒影出来了。
好吧,关于Canvas
中Shader
部分就写完了。Canvas
还有许多有趣的类和API,有时间我再写。
- Android绘图Canvas十八般武器之Shader详解及实战篇(下)
- 绘图Canvas十八般武器之Shader详解及实战
- Android绘图Canvas十八般武器之Shader详解及实战篇(上)
- Android绘图Canvas十八般武器之Shader详解及实战篇(上)
- Android中Canvas绘图之Shader使用图文详解
- Android中Canvas绘图之Shader使用图文详解
- Android 中 Canvas 绘图之 Shader 使用图文详解
- Android中Canvas绘图之Shader使用图文详解
- Android中Canvas绘图之Shader使用图文详解
- Android中Canvas绘图之Shader使用图文详解---转载
- Android基础:十八般武器之TabHost
- Android基础:十八般武器之Button
- Android基础:十八般武器之TextView
- 【canvas】Android Canvas绘图详解
- Android Canvas绘图详解
- Android Canvas绘图详解
- Android Canvas绘图详解
- Android Canvas绘图详解
- javaAPI之Math类与Random类
- hsf开源dubbo学习-2-配置
- 浙江大学ZOJ 1002题 详解
- Java Math的 floor,ceil和round函数的简单介绍
- iOS App上架流程(2016详细版)
- Android绘图Canvas十八般武器之Shader详解及实战篇(下)
- [KinectWPF程序]1深度图像,使用WriteableBitmap对象改进Kinect图像显示&复杂的Kinect初始化方法
- 特征工程小案例
- Findstr
- MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk.
- Set
- angular的指令
- 疑难杂症(3) -- 【java.lang.UnsupportedClassVersionError】版本不一致出错
- 用excel设计带条形码的报价单