自定义View Layout过程 详解

本文深入剖析Android自定义View的layout过程,包括单一View和ViewGroup的布局原理,通过实例解析LinearLayout和自定义ViewGroup的布局计算逻辑,探讨布局过程中关键方法的作用,并解答布局细节问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

Acty

1. 作用

计算视图(View)的位置

即计算View的四个顶点位置:Left、Top、Right 和 Bottom

2. 知识储备

请参考文章:自定义View基础

3. layout过程详解

类似measure过程,layout过程根据View的类型分为2种情况:
在这里插入图片描述
接下来,我将详细分析这2种情况下的layout过程

3.1 单一View的layout过程

3.1.1 应用场景

在无现成的控件View满足需求、需自己实现时,则使用自定义单一View
特征:继承自View、SurfaceView 或 其他View;不包含子View

  1. 如:制作一个支持加载网络图片的ImageView控件
  2. 注:自定义View在多数情况下都有替代方案:图片 / 组合动画,但二者可能会导致内存耗费过大,从而引起内存溢出等问题。

3.1.2 具体流程

在这里插入图片描述
下面我将一个个方法进行详细的源码分析
layout过程的入口 = layout(),具体如下:

/**
  * 源码分析:layout()
  * 作用:确定View本身的位置,即设置View本身的四个顶点位置
  */ 
  public void layout(int l, int t, int r, int b) {
     

    // 当前视图的四个顶点
    int oldL = mLeft;  
    int oldT = mTop;  
    int oldB = mBottom;  
    int oldR = mRight;  
      
    // 1. 确定View的位置:setFrame() / setOpticalFrame()
    // 即初始化四个顶点的值、判断当前View大小和位置是否发生了变化 & 返回 
    // ->>分析1、分析2
    boolean changed = isLayoutModeOptical(mParent) ?
            setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

    // 2. 若视图的大小 & 位置发生变化
    // 会重新确定该View所有的子View在父容器的位置:onLayout()
    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
     

        onLayout(changed, l, t, r, b);  
        // 对于单一View的laytou过程:由于单一View是没有子View的,故onLayout()是一个空实现->>分析3
        // 对于ViewGroup的laytou过程:由于确定位置与具体布局有关,所以onLayout()在ViewGroup为1个抽象方法,需重写实现(后面会详细说)
  ...

}  

/**
  * 分析1:setFrame()
  * 作用:根据传入的4个位置值,设置View本身的四个顶点位置
  * 即:最终确定View本身的位置
  */ 
  protected boolean setFrame(int left, int top, int right, int bottom) {
   
        ...
    // 通过以下赋值语句记录下了视图的位置信息,即确定View的四个顶点
    // 从而确定了视图的位置
    mLeft = left;
    mTop = top;
    mRight = right;
    mBottom = bottom;

    mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);

    }

/**
  * 分析2:setOpticalFrame()
  * 作用:根据传入的4个位置值,设置View本身的四个顶点位置
  * 即:最终确定View本身的位置
  */ 
  private boolean setOpticalFrame(int left, int top, int right, int bottom) {
   

        Insets parentInsets = mParent instanceof View ?
                ((View) mParent).getOpticalInsets() : Insets.NONE;

        Insets childInsets = getOpticalInsets();

        // 内部实际上是调用setFrame()
        return setFrame(
                left   + parentInsets.left - childInsets.left,
                top    + parentInsets.top  - childInsets.top,
                right  + parentInsets.left + childInsets.right,
                bottom + parentInsets.top  + childInsets.bottom);
    }
    // 回到调用原处

/**
  * 分析3:onLayout()
  * 注:对于单一View的laytou过程
  *    a. 由于单一View是没有子View的,故onLayout()是一个空实现
  *    b. 由于在layout()中已经对自身View进行了位置计算,所以单一View的layout过程在layout()后就已完成了
  */ 
 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
   

   // 参数说明
   // changed 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值