当前位置: 首页 > 科技观察

ViewGroup默认顺序绘制子View,如何修改?什么场景需要修改绘制顺序?

时间:2023-03-15 21:03:15 科技观察

1。前言大家好,我是呈祥墨影,好久不见,很想你们!今天我们就来说说View绘制过程的一个小细节,自定义绘制顺序。View的三大流程:测量、布局、绘图,想必大家都不陌生。在绘制阶段,ViewGroup不仅要绘制自己,还要循环绘制它的子View。这种绘制策略默认为顺序绘制,即[0~childCount)。有没有办法调整这个默认策略?比如修改成(childCount~0],或者修改成View最后绘制。同时,有哪些场景需要我们做这样的修改呢?需要注意的是绘制顺序会影响覆盖率。order也会影响View的事件分发,这些都是相关的影响,可以说是牵一发而动全身,今天就来说说这个问题。2.TVApp的Item处理修改View的绘制顺序,就是开发中基本不用,很多手机APP的UI设计大多采用扁平化的设计思路,除非是一些非常特殊的自定义View,否则大多数情况下,我们不需要考虑View的默认绘制顺序,这也是很容易理解,一般情况下,ViewGroup后面添加的View在视觉上应该覆盖前面的View。但是有一个场景设计,很特别,就是AndroidTVApp。在TV的设计中,因为Requires遥控器按钮控制。对于一个ri为了更好的视觉体验,需要额外处理View焦点状态的变化。比如,高亮整个获得焦点的ItemView,放大加阴影,是一种很常见的设计。那么这就带来了一个问题。通常我们使用RecyclerView来实现列表效果。当项目之间的距离太小时,单个项目将被放大和覆盖。比如上图,一个很常见的焦点放大到高亮的Design,但是被后面的View给遮住了。如何解决这种情况?拍拍脑袋想,既然间距太小了,那我们可以加大间距。修改一个属性解决一个需求,设计师在工位上哭晕了。但确实有一些设计效果,间距足够,没有覆盖现象,比如bilibiliTV端的一些页面。但是我们不能仅仅通过改变间距来解决问题。大多数情况下,设计师留给我们的空隙并不多。大多数电视应用程序都是这样的。既然逃不掉,那就研究怎么解决吧。3、修改绘制顺序的原理修改绘制顺序其实很简单,Android已经为我们预留了扩展点。我们知道ViewGroup是通过它的成员mChildren数组存储子View的。但是在ViewGroup绘制子View的dispatchDraw()方法循环中,并没有直接使用索引从mChildren数组中取值。@OverrideprotectedvoiddispatchDraw(Canvascanvas){//...finalArrayListpreorderedList=usingRenderNodeProperties?null:buildOrderedChildList();finalbooleancustomOrder=preorderedList==null&&isChildrenDrawingOrderEnabled();for(inti=0;ipreorderedList,View[]children,intchildIndex){finalViewchild;if(preorderedList!=null){child=preorderedList.get(childIndex);if(child==null){thrownewRuntimeException("InvalidpreorderedListcontainednullchildatindex"+childIndex);}}else{child=children[childIndex];}returnchild;}其中,如果preorderedList不为空,则从中获取子View,否则从children获取。回到dispatchDraw(),这里使用的preorderedList的关键列表来自于buildOrderedChildList()。方法中通过getAndVerifyPreorderedIndex()获取对应子View的index。该方法需要一个布尔类型的customOrder,表示是否需要自定义订单。ArrayListbuildOrderedChildList(){//...finalbooleancustomOrder=isChildrenDrawingOrderEnabled();for(inti=0;i0&&mPreSortedChildren.get(insertIndex-1).getZ()>currentZ){insertIndex--;}mPreSortedChildren.add(insertIndex,nextChild);}returnmPreSortedChildren;}buildOrderedChildList()逻辑是根据Z轴调整children的顺序。如果Z轴值相同,参考customOrder的配置。通常ViewGroup中的子View的Z值都是一样的,所以关键参数就是customOrder开关。从代码中我们知道customOrder是通过isChildrenDrawingOrderEnabled()方法获取的,对应的setChildrenDrawingOrderEnabled()可以设置customOrder的值。也就是说,如果我们要调整顺序,只需要2步调整:调用setChildrenDrawingOrderEnable(true)启用自定义绘制顺序重写getChildDrawingOrder()修改View的valueindex4.Example最后写一个demo并重写RecycleView的getChildDrawingOrder()方法来实现聚焦View的最终绘制。@OverrideprotectedintgetChildDrawingOrder(intchildCount,inti){Viewview=getLayoutManager().getFocusedChild();if(null==view){returnsuper.getChildDrawingOrder(childCount,i);}intposition=indexOfChild(view);if(position<0){returnsuper.getChildDrawingOrder(childCount,i);}if(i==childCount-1){returnposition;}if(i==position){returnchildCount-1;}returnsuper.getChildDrawingOrder(childCount,i);}别忘了返回您需要调用setChildrenDrawingOrderEnabled(true)以启用自定义绘图顺序。此时焦点放大时,不会被其他View挡住。