android view 绘制顺序,android 绘制流程之修改子View的绘制顺序

android为我们提供了一个修改View绘制顺序的方法getChildDrawingOrder,今天我们就来学习一下这个方法。

如果对修改View的绘制顺序没有太好的理解可以先参考下这一篇文章

再网络上流传比较广泛的是 修改RecyclerView的绘制层级的解决方法如下

@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 

return super.getChildDrawingOrder(childCount, i);

}

if (i == childCount - 1) {

return position;

}

if (i == position) {

return childCount - 1;

}

return super.getChildDrawingOrder(childCount, i);

}

复制代码

实现思路也比较简单,就是将焦点View的order与最后一个View的 order进行互换。这样就能实现焦点View在最上层。

那么getChildDrawingOrder是返回的View的绘制顺序吗?

我们来做一个简单的测试,代码布局如下:

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

android:background="@color/colorPrimary">

android:layout_width="wrap_content"

android:layout_height="wrap_content">

android:id="@+id/tv_01"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="1"/>

android:id="@+id/tv_02"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="2"/>

android:id="@+id/tv_03"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="3"/>

复制代码

默认的绘制顺序是 1 2 3;我们的目标绘制顺序是 231

DrawOrderTestFrameLayout代码如下:

public class DrawOrderTestFrameLayout extends FrameLayout{

public DrawOrderTestFrameLayout(@NonNull Context context){

this(context,null);

}

public DrawOrderTestFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs){

this(context, attrs,0);

}

public DrawOrderTestFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr){

super(context, attrs, defStyleAttr);

init();

}

private void init(){

setChildrenDrawingOrderEnabled(true);

}

@Override

protected int getChildDrawingOrder(int childCount, int i){

View child = getChildAt(i);

switch (child.getId()){

case R.id.tv_01:{

return 2;

}

case R.id.tv_02:{

return 0;

}

case R.id.tv_03:{

return 1;

}

}

return super.getChildDrawingOrder(childCount, i);

}

}

复制代码

DrawOrderTestTextView代码如下:

public class DrawOrderTestTextView extends TextView{

public DrawOrderTestTextView(Context context){

super(context);

}

public DrawOrderTestTextView(Context context, @Nullable AttributeSet attrs){

super(context, attrs);

}

public DrawOrderTestTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr){

super(context, attrs, defStyleAttr);

}

@Override

protected void onDraw(Canvas canvas){

super.onDraw(canvas);

Log.d("DrawOrderTestTextView","onDraw "+getText());

}

}

复制代码

上面的代码输出日志如下:

1766ca21ca85c6bf39283e5ea010007d.png

可以看到并不是按照 我们想要的顺序进行绘制,那么getChildDrawingOrder的返回值到底是什么意思呢?

ViewGroup#dispatchDraw

@Override

protected void dispatchDraw(Canvas canvas){

//usingRenderNodeProperties 始终为false

boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);

final int childrenCount = mChildrenCount;

final View[] children = mChildren;

int flags = mGroupFlags;

//省略代码

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);

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

if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {

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

}

}

//省略代码

}

private int getAndVerifyPreorderedIndex(int childrenCount, int i, boolean customOrder){

final int childIndex;

if (customOrder) {

final int childIndex1 = getChildDrawingOrder(childrenCount, i);

if (childIndex1 >= childrenCount) {

throw new IndexOutOfBoundsException("getChildDrawingOrder() "

+ "returned invalid index " + childIndex1

+ " (child count is " + childrenCount + ")");

}

childIndex = childIndex1;

} else {

childIndex = i;

}

return childIndex;

}

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;

}

复制代码

整体的逻辑如下:

因为usingRenderNodeProperties始终为false,所以preorderedList会一直为空,getAndVerifyPreorderedIndex返回的childIndex就是getChildDrawingOrder返回的int值。getAndVerifyPreorderedView在preorderedList为空的情况下返回的View是child = children[childIndex];也就是说getChildDrawingOrder 的返回值是当前想要绘制View子在数组中的下标。

对应于getChildDrawingOrder(int childCount, int i) 就应该是 根据绘制的位置i 返回 当前想要绘制子View的在数组中的下标。

再来看看先前的代码:

View child = getChildAt(i);

switch (child.getId()){

case R.id.tv_01:{//i=0

return 2;

}

case R.id.tv_02:{//i=1

return 0;

}

case R.id.tv_03:{//i=2

return 1;

}

}

复制代码

根据刚才的分析我们就应该知道为什么绘制顺序是 312了。

输入 i =0 返回2 绘制 mChildren[2] text = 3

输入 i =1 返回0 绘制 mChildren[0] text = 1

输入 i =2 返回1 绘制 mChildren[1] text = 2

显然要实现我们的 绘制目标 231,应该按照下面的逻辑进行返回

输入 i =0 返回1 绘制 mChildren[1] text = 2

输入 i =1 返回2 绘制 mChildren[2] text = 3

输入 i =2 返回0 绘制 mChildren[0] text = 1

int getChildDrawingOrder(int childCount, int i)中i 代表的是绘制的顺序,第几个 返回值 代表 当前容器的 第几个子View。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android的视图绘制Android应用程序中的重要部分,它涉及到将用户界面元素绘制到屏幕上。以下是Android视图绘制的基本流程: 1. 触发绘制:当应用程序启动、布局发生变化或者手动调用 `invalidate()` 方法时,会触发视图绘制。 2. 测量布局:在绘制之前,Android会测量每个视图的大小。这个过程称为“测量布局”。测量布局是为了确定每个视图在屏幕上的位置和大小。 3. 布局:一旦测量完成,Android会根据视图的测量结果进行布局,确定每个视图在屏幕上的位置。 4. 绘制:布局完成后,Android会调用每个视图的 `draw()` 方法进行绘制。在 `draw()` 方法中,视图会绘制自己的内容,包括背景、文字、图片等。 5. 绘制层次:视图的绘制按照层次结构进行,即从父视图到视图的顺序。父视图会先绘制自己,然后再绘制视图。 6. 递归绘制:当父视图绘制完成后,它会递归地调用视图的 `draw()` 方法,依次完成整个视图树的绘制过程。 7. 绘制缓存:为了提高绘制性能,Android使用了绘制缓存。绘制缓存可以将视图的绘制结果保存起来,在下次绘制时直接使用缓存,而不需要重新执行绘制操作。 总结来说,Android的视图绘制过程包括测量布局、布局、绘制绘制缓存。通过这个过程,Android应用程序可以将用户界面元素绘制到屏幕上,实现丰富多样的交互效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值