说到变换,第一反应的便是矩阵变换。view是提供矩阵变换的,不过,如果没学过矩阵或者知识薄弱的可能有点难,今天就谈谈最基本的变换,旋转缩放和平移吧。
首先说下缩放和平移,对于一个View,有左上角的位置x,y和本身高度height和宽度width,所以也可以直接得到上下左右距离(left,top,right,bottom)对于平移和放大缩小,最直接的想法就是改变x,y和高度宽度。但是只有getX(),getY(),getWidth()和getHeight()却没有相应的set函数,所以,这里改变不是通过这些改变的,而是通过重新布局,也就是layout函数,会对本身大小位置进行重置,通过view.layout(x+dx,y+dy,x+dx+width*sx,y+dy+height*sy),这里的缩放是不能负数的,需要处理下,否则就不显示了。系统的viewDragHelper就是利用这个获取位置的,对于一个点,直接从顶层判断该点是不是在child的上下左右边距里,简单粗暴。但是也引起了一个严重的bug,这个接下来会说。
缩放和平移可以改变本身达到效果,但是旋转却没办法了。通过对系统api的了解,会发现一个getRotation和setRotation函数,这个便就是旋转了。看到这个,也会发现其他的函数,getTranslationX,getTranslationY(Z轴就先不讨论了)和getScaleX,getScaleY以及相应的set函数,这两个分别就是平移和缩放,那和直接改变本身位置和大小有啥差别呢?差别就在于,后者的函数是用于矩阵变换的,只是矩阵变换中的一种,实际位置和大小不受影响,影响的只是看到的视觉效果。而第一种则是通过改变本身属性,改变完后就就无法获知原来的大小和位置了。矩阵变换可以只记录大小和位置,其他变换通过维护一个矩阵,在draw中,通过对画布变换,达到视觉效果,而最初记录并不会改变,将本身属性和变换效果分离开来。而ViewDragHelper则是直接判断实际位置,平移通过变换实现,bug就是,你本身的View除了ViewDragHelper操作的平移外,不能有其他自定义的变换,否则操作区域和视觉区域将会分离开来。当然,细心的人也会发现,那事件分发总不能出现操作区域和视觉区域分离吧!实验了下,发现确实没分离,然后查看源码,发现系统内部的分发事件中,获取点是否在View里是有对这些变换做处理的,处理也很简单,将点逆变换后,判断是否在view的实际区域里,不过可惜的是,该函数被google隐藏了,原因不得而知。所以如果要得知点是否具体的在View(的视觉范围内),第一种就是通过反射调用该函数(之前试过,不知道有没有因为看乱了,调错类了还是其他原因,最终没调用到,有兴趣的可以去试试),第二种就直接自己算了,反正变换都在那儿了,求个逆还不简单。当然,这里自己也有个小小的疑问,如果变换不可逆呢?这个还没试过,有兴趣的可以去试试,也欢迎在评论区共享。
最后一点就是,因为上述只是取最简单的缩放平移和旋转的,如果水平反转的话,就是缩放-1倍,更复杂的话,只要写得出矩阵,都可以变换出来。这里,说一个比较重要的概念,那就是锚点。我们旋转缩放,总得根据一点来旋转和缩放吧,这个就是锚点,可以通过setPivotX和setPivotY设置,很多时候都是设置在中心点,然后,如果锚点不变的话,设置一次即可。