赞
踩
转From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige
前几天身子骨出了点小毛病不爽,再加上CSDN抽风也木有更新,现在补上之前漏掉的1/3
上一节结尾的时候我们说到,Paint类中我们还有一个方法没讲
Shader类呢也是个灰常灰常简单的类,它有五个子类,像PathEffect一样每个子类都实现了一种Shader,Shader在三维软件中我们称之为着色器,其作用嘛就像它的名字一样是来给图像着色的或者更通俗的说法是上色!这么说该懂了吧!再不懂去厕所哭去!这五个Shader里最异类的是BitmapShader,因为只有它是允许我们载入一张图片来给图像着色,那我们还是先来看看这个怪胎吧
BitmapShader
只有一个含参的构造方法BitmapShader (Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)而其他的四个兄弟姐妹呢都有两个!它只有一个蛋,又一魂谈!那好吧,我们来看看它是什么个效果,顺便呢也学习一下Shader的用法先,来看我们熟悉的代码:
我靠!这什么玩意!罪过罪过!真是看不懂!别急,Shader.TileMode里有三种模式:CLAMP、MIRROR和REPETA,我们看看其他两种模式是什么效果呢:
大家可以看到图像分为两部分左边呢Y轴镜像了,而右边像是被拉伸了一样怪怪的!其实CLAMP的意思就是边缘拉伸的意思,比如上图中左边Y轴镜像了,而右边会紧挨着左边将图像边缘上的第一个像素沿X轴复制!产生一种被拉伸的效果!就像扯蛋,不过这里扯的不是蛋而是图像边缘的第一个像素,就是这么简单。但是!作为一个严谨的男人必须要又一个严谨的态度!这时我就会想,如果两种模式互换会怎样呢?比如:
这…………好像跟我们想象中的不大一样唉……是我们做错了吗?不是的,结合上一个例子大家有没有注意BitmapShader是先应用了Y轴的模式而X轴是后应用的!所以着色是先在Y轴拉伸了然后再沿着X轴重复对吧(阴笑ing……)?!
要善于发现生活规律!我曾经说过:存在必定合理,那么这么一个玩意有什么用处呢?给大家看一个非常有趣的东西:
这玩意是不是感觉跟以前那种小霸王某个游戏的开场动画很类似?我们就是利用BitmapShader来实现的,而且实现方法也异常简单:
线性渐变,顾名思义这锤子玩意就是来画渐变的,实际上Shader的五个子类中除了上面我们说的那个怪胎,还有个变形金刚ComposeShader外其余三个都是渐变只是效果不同而已,而这个LinearGradient线性渐变一说大家估计都懂,先来看张效果图:
是不是秒懂了!恩,说明你头脑简单,这个实现也很简单,具体代码跟上面的BitmapShader一样只是把BitmapShader换成了LinearGradient而已:
仅仅两种颜色的渐变根本无法满足我们身体的欲望,太单调乏味!我们是不是可以定义多种颜色渐变呢?答案是必须的,LinearGradient的另一个构造方法
前面四个参数也是定义坐标的不扯了colors是一个int型数组,我们用来定义所有渐变的颜色,positions表示的是渐变的相对区域,其取值只有0到1,上面的代码中我们定义了一个[0, 0.1F, 0.5F, 0.7F, 0.8F],意思就是红色到黄色的渐变起点坐标在整个渐变区域(left, top, right, bottom定义了渐变的区域)的起点,而终点则在渐变区域长度 * 10%的地方,而黄色到绿色呢则从渐变区域10%开始到50%的地方以此类推,positions可以为空:
实际应用中线性渐变也是一个非常好玩的东西,比如哦我们呢可以通过它和混合模式一起制作我们常见的图片倒影效果:
效果并不复杂实现也非常简单:
径向渐变,径向渐变说的简单点就是个圆形中心向四周渐变的效果,他也一样有两个构造方法……艾玛!我都要吐了……
是不是很漂亮?不过哥压根不放在眼里,哥女朋友比她更漂亮~~好,我们应用1/6里学到的知识给她校校色,因为这张图片的颜色实在太过鲜艳了不符合小清新的LOMO风格~~~~
所以……大家一定要活用工具~~~~善于组合思考!说起组合,接下来Shader的最后一个子类简直就是组合他妈生的
ComposeShader
就是组合Shader的意思,顾名思义就是两个Shader组合在一起作为一个新Shader……老掉牙的剧情是吧!同样,这锤子玩意也有两个构造方法
可见渐变的坡度太平缓了,不符合我们的Style,能不能改一下让它拉伸下变成一个竖向的椭圆形呢?比如下图这样的:
我们来试试看:
是不是感脚暗角比上面我们做的那个更Perfect?图片中心大部分区域不受任何干扰只把四角边缘处的亮度压了下去,当然这个效果对哥来说也不是很满意,不过先将就凑合着 = = 。上例中我们使用到了两个东西,一个是在独立的Canvas中绘制自己的Bitmap,在哥下一节我们就会详细讲到这里就先不扯了,而另一个则是我们说的重点:Matrix,其实在上面我们做倒影的时候已经简单地使用过了Matrix,那么Matrix究竟是什么呢?其中文直译为矩阵,而我更愿意称之为矩阵变换或者图形变换,它和我们1/6中学到的图形的混合可谓是双簧,同样地重要!
在本文的开头哥给大家挖了一个坑,在讲BitmapShader的时候我们在
不知道大家有没有质疑过为什么?如果没有,情有可原!但是在我们使用另外两种模式REPEAT和MIRROR的时候难道大家就没想过为什么我们的位图会像那样重复或者镜像吗?是必须那样吗?那个例子中我们使用的是一个边长为800置于屏幕中心的矩形,我们何不尝试将其铺满屏幕看看?
是不是忽然明白了什么?好了,你可以从坑里爬出来了…………这样看是不是懂了?BitmapShader是从画布的左上方开始着色,回到我们刚才的问题,这个着色方式必须是这样的么?显然不是!在Shader类中有一对setter和getter方法:setLocalMatrix(Matrix localM)和getLocalMatrix(Matrix localM)我们可以利用它们来设置或获取Shader的变换矩阵,比如上面的例子我还是绘制成一个边长为800的矩形:
Is anyone has question yet?是不是感觉有点儿懂Matrix了?Really?
其实说了半天我们依然没进入到Matrix的本质,在1/6中我们学习了一个和它比较类似的玩意叫做ColorMatrix大家还记得否?我在讲ColorMatrix的时候说过其是一个4x5的颜色矩阵,而同样,我们的Matrix也是一个矩阵,只不过不是4*5而是3*3的位置坐标矩阵:
变换变换,既然说到变换那么必定涉及最基本的旋转啊、缩放啊、平移之类,而在Matrix中除了该三者还多了一种:错切,什么叫错切呢?所谓错切数学中也称之为剪切变换,原理呢就是将图形上所有点的X/Y坐标保持不变而按比例平移Y/X坐标,并且平移的大小和某点到X/Y轴的垂直距离成正比,变换前后图形的面积不变。其实对于Matrix可以这样说:图形的变换实质上就是图形上点的变换,而我们的Matrix的计算也正是基于此,比如点P(x0,y0)经过上面的矩阵变换后会去到P(x,y)的位置:
注:除了平移外,缩放、旋转、错切、透视都是需要一个中心点作为参照的,如果没有平移,默认为图形的[0,0]点,平移只需要指定移动的距离即可,平移操作会改变中心点的位置!非常重要!记牢了!
PS:唉……说实话矩阵的计算原理真心不想写……不过算是个小总结吧,爱看看不爱看直接跳过即可……
有一点需要注意的是,矩阵的乘法运算是不符合交换律的,因此矩阵B*A和A*B是两个截然不同的结果,前者表示A右乘B,是列变换;后者表示A左乘B,是行变换。如果有心的童鞋会发现Matrix类中会有三大类方法:setXXX、preXXX和postXXX,而preXXX和postXXX就是分别表示矩阵的左右乘,也有前后乘的说法,对于不懂矩阵的人来说都一样 = = ……但是要注意一点!!!大家在理解Matrix的时候要把它想象成一个容器,什么容器呢?存放我们变换信息的容器,Matrix的所有方法都是针对其自身的!!!!当我们把所有的变换操作做完后再“一次性地”把它注入我们想要的地方,比如上面我们为shader注入了一个Matrix。还有一点要注意,一定要注意:ColorMatrix和Matrix在应用给其他对象时都是左乘的,而其自身内部是可以左右乘的!千万别搞混了!UnderStand?一定要理解这一点,不然我只能哭晕在厕所了压根没法讲下去你也不会听的懂……
上图的公式中,GHI都表示的是透视参数,一般情况下我们不会去处理,三维的透视我更乐意使用Camare,所以很多时候G和H的值都为0而I的值恒为1,至于为什么如果有时间待会会说。
所有的Matrix变换中最好理解的其实是缩放变换,因为缩放的本质其实就是图形上所有的点X/Y轴沿着中心点放大一定的倍数,比如:
这么一个矩阵变换实质就是x = x0 * a、y = y0 * b,难度系数:0
X/Y轴分别放大a\b倍
相对来说平移稍难但是也好理解:
同理x = x0 + a、y = y0 + b,难度系数:0
X/Y轴分别平移!¥¥%#%……%……#¥%¥%
旋转就很复杂了……分为两种:一种是直接绕默认中点[0,0]旋转,另一种是指定中点,也就是将中点[0,0]平移后在旋转:
直接绕[0,0]顺时针转:
唉、这个先看图吧:
根据三角函数的关系我们可以得出p(x,y)的坐标:
同样根据三角函数的关系我们也可以得出p(x0,y0)的坐标:
上述两公式结合我们则可以得出简化后的p(x,y)的坐标:
这是什么公式呢?是不是就是上面矩阵的乘积呢?囧……
绕点p(a,b)顺时针转:
其实绕某个点旋转没有想象中的那么复杂,相对于绕中心点来说就多了两步:先将坐标原点移到我们的p(a,b)处然后执行旋转最后再把坐标圆点移回去:
对了……开头忘说了……矩阵的乘法是从右边开始的,额,其实也只有上面这算式才有多个矩阵相乘 = = 冏,也就是说最右边的两个会先乘,大家看看最右边的两个乘积是什么……是不是就是我们把原点移动到P(a,b)后[x0,y0]的新坐标啊?然后继续往左乘,旋转一定得角度这跟上面[0,0]旋转是一样的,最后往左乘把坐标还原
不扯了,你听得懂甚好!不懂没关系!真的没关系!哥不骗你!哥从不骗妹子……
哎……错切我也先不说了,大家听了这么多槽点是不是头都大了?麻痹的做个变换还这么麻烦劳资TM还不如不做!是的,这样去做变换真心太麻烦!要是开发Android这么麻烦的话特么谁还玩?所以这些复杂繁琐的扑街玩意Android早就为我们封装好了……压根就不需要我们去管那么多!上面我们曾提到的setXXX、preXXX和postXXX方法就是Android为我们封装好的针对不同运算的“档位”,那么怎么用呢?非常非常简单,你压根可以现在忘记上面我们说到的各种计算原理,比如,我这里还是拿BitmapShader来说吧,我们想要移动Shader的位置:
效果也很简单,那我们再来个旋转5度?
Why?其实是这样的,在我们new了一个Matrix对象后,这个Matrix对象中已经就为我们封装了一组原始数据:
同样地,对于类似的变换:
现在,大家回过头去再看看我给妹子图加暗角的那段代码,里面关于Matrix的操作能大致看懂了么:
好了,Paint所有的方法已经Over了~~~大家是不是觉得终于解放的赶脚呢?别急……还有个Canvas……光学会了如何用笔而不知道画什么有何用呢?不过别急,Canvas我们下一节再细讲,这一节我们算是对Paint来个总结,注意不是了断哦!了断可不行,上一节末尾我留了几张图说要在这一节给大家说怎么在View中画,这一节呢我们来实现它,注意哦,不要指望在这里你会得到一个完美的控件拿去用……我们还没学怎么去测绘View也还没有讲到ViewGroup,So~~~~在这我们只是单纯地先画。
几个图中最难的大概就是那个各种圈圈的了:
其他的三个图表千篇一律……会画上面那玩意几个图标简直就是小case,这个圈圈图也是我在群里搜刮的,看不出来是吧,没事,我临摹了一个差不多的:
这样看总清晰了吧……大家在自定义一个View的时候不要老想着自己是个Coder,你老是这样想越做不出棒的View,你要把自己看成一个Designer,这个View将会是由你创造的!而不是你敲出了它……本来在这一系列之后我有单独的番外篇叫如何去设计一个控件,今天在做这个圈圈图的时候突然有这么一个想法还是干脆穿插进来说算了,直接粗暴!
既然我们是一个设计者,那么我们必然要有这么一张图纸去概述我们的View,因为一般情况下大家都知道美工跟开发者之间是有代沟的,假如,我是说假如美工花了一个很屌的界面,但是你如果直接按着他给你的设计图照着做你会发现做不出一模一样的来……这时你就该调整自己多去与美工沟通在不大改设计图的前提下把难度降到你能力范围内。同样的,我们这里也一样。假如这个圈圈图是JB美工给你的效果图,叫你照着做,我们不可能真的这样照着做,因为他给的东西对我们来说无规律可循,而对于开发者来说,有规律的东西往往是最简单,这时我们就要“设计和代码相结合”:
大家看到我在刚才那张临摹图上做了一些改动,从这张草图上来看改动其实并不大,只是把一些位置啊、大小啊什么的做了一些调整,要记住一点,我们的控件要做到在任何屏幕设备上都能完美使用!而不是像布局、资源图之类的还要做好几套,那就是扯蛋!所以我们这的所有尺寸都是以控件的边长S作为参考依据的:
注:因为我们还没讲如何测绘控件,所以我们在自定义View的时候强制控件的长宽一致以简化不必要的口水
还是老套路,先分析一下:控件中心往下是最中心的圆,而其他的六个圆都直接或间接地与其相连,上面的三个是大圆而下面的三个是相对较小的圆,大圆之间的线段是紧挨着的而中心大圆和下面三个小圆之间的线段是有一定距离的,右上方的大圆上方还有一段实体描边弧,弧上有文字,而每个圆内都可以设置文本,大概就是酱紫,那么我们从哪作为插入点呢?当然是中心的那个圆,但是我们知道它的圆心是要往控件中心向下偏移一个半径的:
代码如何实现呢?不用我说你也应该知道:
然后下一步该如何做呢?画哪个呢?再从左上开始吧……,好我们计算坐标:
没错对吧,绿色的点就是我们要计算的坐标。但是这样去算太TM复杂了!毫无违和感!我们细心观察,左上边那一节不就是等同于:
这样的一个变换吗?之前我们曾多次使用到画布的图层,如果我们能这样画图形再以中心圆的圆心坐标为旋转点向右旋转画布岂不是可以很简单:
保存和释放画布就不说了,用过N次了。
这里有一点非常非常地重要!画布的平移旋转同样也会影响画布的自身坐标!如上图,我们看到画布的坐标也跟着旋转了30度!我们正是利用了这一点巧妙地在画图而避免繁杂的坐标计算!!
同理我们可以绘制出其他三个小圆:
上面的代码会有巨量的重复代码,正如上面我说,这个图形我们是画死的,在没讲完View的测绘和ViewGroup之前我们不会做任何一个完整的控件,So~~~~同时也是为了方便大家容易理解这玩意是怎么画的,我也就不对重复的方法做进一步封装了,不过在做项目的时候切忌大量的重复代码。
我们再给这些圈圈里加些文字,文字的中点很明显就是这些圈圈的圆心对吧,1/4中我说过如何把文字画到中心的?
稍微有点难的是右上方的那个半回扇形,扇形的中心应该是与右上方的圆心重合的对吧,那我们在画右上方图形的时候一起画不就是了?
好了,正如我所说,这只是一个单纯地画,而且此类奇葩的玩意难以真正做成一个控件去复用除非真的是公事公办,但是,我们依然可以尝试把它做成一个独立的控件,这在我们学写了如何测绘View和ViewGroup之后对你来说一定是小case。
上面我们的最终效果有一点是不对的,大家发现文字TMD居然都旋转了 - - ,那有木有方法让文字保持水平呢?其实答案我已经告诉你。自己去发掘吧!
源码下载:传送门
这几天龙体欠安啊、哎……更新进度会有打乱~~~敬请大家follow~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。