ViewGroup分配剪切区思路

作者: 暴走的Jacky | 来源:发表于2017-09-11 09:57 被阅读0次

         每个View从父视图拿到的剪切区的原点坐标都是View的视图坐标原点,剪切区的大小由View的布局(onLayout时确定)决定,剪切区的位置由View的布局和View的mScrollX\mScrollY决定。

    View分配剪切区思路图

    灰色矩形为最外层ViewGroup,不可滑动(mScrollX\mScrollY为0),蓝色矩形可滑动。


    初始状态

           父视图在给子视图分配剪切区的时候(在2.3的源码中是View的dispatchdraw()方法),会根据子视图在onLayout时确定的位置和子视图mScrollX\mScrollY的值确定,确保子视图在onDraw(Canvas canvas)方法中拿到的canvas的坐标起点是子视图自身大小的坐标起点。所谓的draw操作只管将子视图本身画出来,而暂时不管其能否全部显示在屏幕上。此时子视图自身的坐标起点,可能在屏幕外(如上面右边的图),也可能在屏幕内。

    在左图中,所有视图自身的大小,视图坐标原点,都是显示在屏幕上,你所看到的。

    滑动后状态

         现在,手指在屏幕上向上滑动了100px的距离,正好把T1滑出屏幕外,那么此时mScrollY=100px。

         此时,灰色矩形拿到的剪切区仍然不变,因为他的onLayout值和mScrollX/mScrollY的值都没变化。至于蓝色矩形,他已经滑到了屏幕外100px,滑动并不会改变剪切区的大小,而只会改变其剪切区的位置,从而影响了他给子视图分配的剪切区的位置,所以导致了一部分子视图消失。

    坐标变化图

    视图坐标变化

         当灰色矩形为蓝色矩形分配剪切区时,他的canvas坐标原点在1,而蓝色矩形的视图坐标原点在2,那么怎样将canvas的坐标原点从1——>2呢?

         看看究竟他要考虑哪些参数,第一个参数,在onLayout时确定的mLeft,mTop,mRight,mBottom4个值,在图中我只标注了mLeft。这四个值确定剪切区的大小,因为无论你的视图有多大,如一张很大的Bitmap显示在ImageView中,无论你的Bitmap有多大,在Android手机上,给到你的ImageView的大小也撑破天只能是个全屏(比如1920x1080),这是从内存等性能问题上的考虑,因为java层其实就是数据的封装,如果你告诉底层我要2000x2000的剪切区(在底层对应着内存分配),这完全就是浪费内存和性能,你在底层分配的内存以及绘制的那部分,根本无法显示到屏幕上。所以,onLayout()存在的意义在于确定视图在屏幕上的位置,无论你的视图是多大,经过了onLayout方法之后,你在屏幕上的位置也就确定了。

         对父视图的canvas进行平移操作,canvas.translate(mLeft+mScrollX(0),mTop(0)+(-mScrollY)),这样canvas坐标就从1点平移到了2点(此时的canvas还是父视图的canvas,只是坐标点进行了平移)。视图坐标原点确定后,就要为子视图裁剪剪切区,调用canvas.clipRect(sx,sy,sx+(mRight-mLeft),sy+(mBottom-mTop),实际截切区的位置由sx和sy所确定,而剪切区的大小就是视图的布局大小)。

         网上有无数这样对ScrollX/ScrollY的理解,如"对于ViewGroup来说,调用scrollTo()是滑动子视图,对于View来说,调用scrollTo()是滑动其中的内容。"这样的解释对我来说毫无意义!!!

         在View体系中,scrollTo()调用之后,并不影响onMeasure()和onLayout()之后确定的值,只会影响mScrollX/mScrollY的值,从而影响子视图剪切区的分配。对于蓝色矩形来说,他的scroll值的改变影响了灰色矩形为他分配的剪切区,从而影响了蓝色矩形为所有子视图分配的剪切区。而蓝色矩形了布局是确定的,scroll值的改变不会影响该区域,只有剪切区在该区域内的子视图才会显示出来,其他的就隐藏了。

    至于View内容的绘制,可以用数组的方式理解。

    View的数组模型

         这样想,给TextView设置的文本字符最终会变成一个字符数组(如0-99),然而只有50-99下标的字符才可以显示在TextView中,其他的字符则"隐藏了"。对于ImageView,也可以用这样的理解,bitmap对象最终也会变成一个数组,ImageView的宽高是有限制的,而bitmap的大小则是没有限制的,如果你给ImageView设置src属性,那么只有一部分bitmap会显示,而设置backgroup属性,则bitmap会被压缩后全部显示。

         还有一种滑动的方式View.offsetTopAndBottom,他其实就是改变View的mTop和mBottom,但却不会调用onLayout。这样的滑动方式是改变子视图,如T1的内部参数,从而改变T1的剪切区位置,而其父视图不会受到任何影响。

         任何一种滑动,其本质就是改变View内的某种参数,让其在绘制的时候重新计算出View的剪切区,达到显示或隐藏得目的。上面的两种方式都规避的耗时的onLayout操作,所以滑动起来还是很流畅,应该避免用onLayout或LayoutParams值来进行滑动操作。

    相关文章

      网友评论

        本文标题:ViewGroup分配剪切区思路

        本文链接:https://www.haomeiwen.com/subject/oefnjxtx.html