android view 绘制顺序,关于android:ViewGroup-默认顺序绘制子-View如何修改什么场景需要修改绘制顺序...

本文介绍了如何调整ViewGroup的绘制顺序,以解决Android TV应用中焦点View被遮挡的问题。通过开启自定义绘制顺序并重写`getChildDrawingOrder()`方法,可以确保获取焦点的ItemView最先绘制,避免被其他View覆盖。这种方法对于优化TV用户体验,特别是处理焦点高亮和放大效果时非常有用。
摘要由CSDN通过智能技术生成

一、前言

View 的三大流程,测量、布局、绘制,我想大家应该都烂熟于心。而在绘制阶段,ViewGroup 不光要绘制本身,还需循环绘制其一众子 View,这个绘制策略默认为程序绘制,即 [0 ~ childCount)。 这个默认的策略,有方法调整吗?例如批改成 (childCount ~ 0],或是修成某个 View 最初绘制。同时又有什么场景须要咱们做这样的批改? 须要留神的是,绘制程序会影响笼罩程序,同时也会影响 View 的事件散发,这些都是关联影响的,堪称是牵一发而动全身。

二、TV App 的 Item 解决

批改 View 的绘制程序,在日常开发中,根本用不到。泛滥手机端 App 的 UI 设计,大部分采纳扁平化的设计思维,除非是一些很特地的自定义 View,少数状况下,咱们无需思考 View 的默认绘制程序。 这也很好了解,失常状况下,ViewGroup 中后增加的 View,视觉上就是应该笼罩在之前的 View 之上的。 然而有一个场景的设计,很特地,那就是 Android TV App。 在 TV 的设计上,因为须要遥控器按键管制,为了更丰盛的视觉体验,是须要额定解决 View 对焦点状态的变动的。 例如:获取焦点的 ItemView 整个高亮,放大再加个暗影,都是很常见的设计。 那么这就带来一个问题,失常咱们应用 RecyclerView 实现的列表成果,当 Item 之间的间距过小时,单个 Item 被放大就会呈现遮蔽的成果。

例如上图所示,一个很常见的焦点放大高亮的设计,但却被前面的 View 遮蔽了。 这样的状况,如何解决呢? 拍脑袋想,既然是间距太小了,那咱们就拉大间距就好了。批改一个属性解决一个需要,设计师哭晕在工位上。 不过的确有一些设计成果,间距足够,也就不存在遮蔽的景象,例如 Bilibili 的 TV 端的局部页面。

然而咱们不能只靠改间距解决问题,少数状况下,设计师留给咱们的间距并不多。大部分 TV App 是这样的。

既然逃不掉,那就钻研一下如何解决。

三、批改绘制程序原理

批改绘制程序,其实很简略,Android 曾经为咱们留出了扩大点。 咱们晓得,ViewGroup 通过其成员 mChildren 数组,存储子 View。而在 ViewGroup 绘制子 View 的 dispatchDraw() 办法循环中,并不是间接利用索引从 mChildren 数组中取值的。

@Override

protected void dispatchDraw(Canvas canvas) {

// ...

final ArrayList preorderedList = usingRenderNodeProperties

? null : buildOrderedChildList();

final boolean customOrder = preorderedList == null

&& isChildrenDrawingOrderEnabled();

for (int i = 0; i < childrenCount; i++) {

// ...

final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);

// 并非间接从 mChildren 中获取

final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);

if ((child.mViewFlags & VISIBILITY\_MASK) == VISIBLE || child.getAnimation() != null) {

more |= drawChild(canvas, child, drawingTime);

}

}

// ...

}

能够看到,child 并非是从 mChildren 中直取的,而是通过 getAndVerifyPreorderedView() 取得,它的参数除了 children 外,还有一个 preorderedList 的 ArrayList,及子 View 的索引。

private static View getAndVerifyPreorderedView(ArrayList preorderedList,

View\[\] children,

int childIndex) {

final View child;

if (preorderedList != null) {

child = preorderedList.get(childIndex);

if (child == null) {

throw new RuntimeException("Invalid preorderedList contained null child at index "

+ childIndex);

}

} else {

child = children\[childIndex\];

}

return child;

}

在其中,若 preorderedList 不为空,则从其中获取子 View,反之则还是从 children 中获取。 回到后面 dispatchDraw() 中,这里应用的 preorderedList  要害列表,来自 buildOrderedChildList(),在办法中通过 getAndVerifyPreorderedIndex() 获取对应子 View 的索引,此办法须要一个 Boolean 类型的 customOrder,即示意是否须要自定义程序。

ArrayList buildOrderedChildList() {

// ...

final boolean customOrder = isChildrenDrawingOrderEnabled();

for (int i = 0; i < childrenCount; i++) {

// add next child (in child order) to end of list

final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);

final View nextChild = mChildren\[childIndex\];

final float currentZ = nextChild.getZ();

// insert ahead of any Views with greater Z

int insertIndex = i;

while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {

insertIndex--;

}

mPreSortedChildren.add(insertIndex, nextChild);

}

return mPreSortedChildren;

}

buildOrderedChildList() 的逻辑就是依照 Z 轴调整 children 程序,Z 轴值雷同则参考 customOrder 的配置。 通常 ViewGroup 中的子 View,Z 值统一,所以要害参数是 customOrder 开关。 从代码上理解到 customOrder 是通过 isChildrenDrawingOrderEnabled() 办法获取,与之对应的是 setChildrenDrawingOrderEnabled() 能够设置 customOrder 的取值。 也就是说,如果咱们要调整程序,能够应用以下步骤调整:

调用 setChildrenDrawingOrderEnable(true) 开启自定义绘制程序

重写 getChildDrawingOrder() 批改 View 的取值索引

四、实例

最初,咱们写个 Demo,重写 RecycleView 的 getChildDrawingOrder() 办法,来实现取得焦点的 View 最初绘制。

@Override

protected int getChildDrawingOrder(int childCount, int i) {

View view = getLayoutManager().getFocusedChild();

if (null == view) {

return super.getChildDrawingOrder(childCount, i);

}

int position = indexOfChild(view);

if (position < 0) {

return super.getChildDrawingOrder(childCount, i);

}

if (i == childCount - 1) {

return position;

}

if (i == position) {

return childCount - 1;

}

return super.getChildDrawingOrder(childCount, i);

}

别忘了还须要调用 setChildrenDrawingOrderEnabled(true) 开启自定义绘制程序。

此时,焦点放大时,就不会被其余 View 遮挡。

最初

一点题外话:

须要理解更多Android技术,能够复制链接后用石墨文档 App 或小程序关上链接或者私信我“材料”支付。

https://shimo.im/docs/TG8PDh9…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值