Android绘制——自定义view之onLayout
创始人
2025-05-31 08:18:07
0

简介

在自定义view的时候,其实很简单,只需要知道3步骤:

  • 测量——onMeasure():决定View的大小,关于此请阅读《Android自定义控件之onMeasure》
  • 布局——onLayout():决定View在ViewGroup中的位置
  • 绘制——onDraw():如何绘制这个View。

View视图结构

View视图可以是单一的一个如TextView,也可以是一个视图组(ViewGroup)如LinearLayout。

如图:对于多View的视图他的结构是树形结构,最顶层是ViewGroup,ViewGroup下可能有多个ViewGroup或View。

这个树的概念很重要,因为无论我们是在测量大小或是调整布局的时候都是从树的顶端开始一层一层,一个分支一个分支的进行(树形递归)。

onLayout函数

measure的作用就是为整个View树计算实际的大小,而通过刚才对View树的介绍知道,想计算整个View树的大小,就需要递归的去计算每一个子视图的大小(Layout同理)。

对每一个视图通过onMeasure方法的一系列测量流程后计算出实际的高(mMeasuredHeight)和宽(mMeasureWidth)传入setMeasuredDimension()方法完成单个View的测量,如果所测的视图是ViewGroup则可以通过measureChild方法递归的计算其中的每一个子view。对于每个View的实际宽高都是由父视图和本身视图决定的。

Layout的作用就是为整个View树计算实际的位置,而通过刚才对View树的介绍知道,想计算整个View树的位置,就需要递归的去计算每一个子视图的位置(Measure同理)。

而确定这个位置很简单,只需要mLeft,mTop,mRight,mBottom四个值(注意:这4个值是子View相对于父View的值,下面会详细介绍)。

在代码中如何设置这4个值呢?

首先,无论是系统提供的LinearLayout还是我们自定义的View视图,他都需要继承自ViewGroup类,之后必须要做的就是重写onLayout方法(因为在onLayout在ViewGroup中被定义为抽象方法)。

自定义view—onLayout

view类的onLayout()是个空方法

viewGroup的onLayout()是个抽象方法

layou()中的onLayout() 是用来设置viewgroup中子view的位置的 ,而不是用来设置当前view的位置的

/** * 存储所有的View,按行记录 */

private List> mAllViews = new ArrayList>()

/** * 记录每一行的最大高度 */

private ListmLineHeight = new ArrayList();
​
@Override
​
protected void onLayout(boolean changed, int l, int t, int r, int b)
​
{
​
mAllViews.clear();
​
mLineHeight.clear();
​
int width = getWidth();
​
int lineWidth = 0;    // 记录每一行 每加入一个子view之后的当前行宽
​
int lineHeight = 0 ;  // 记录每一行 每加入一个子view之后的当前行高(取最大值)
​
ListlineViews = new ArrayList();
​
int cCount = getChildCount();
​
// 遍历所有的孩子     
​
for (int i = 0; i < cCount; i++)     {       
​
View child = getChildAt(i); 
​
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();    
​int childWidth = child.getMeasuredWidth();      
​
int childHeight = child.getMeasuredHeight();        
​
// 如果已经需要换行     
​
if (childWidth + lp.leftMargin + lp.rightMargin + lineWidth > width) {    
​
// 记录这一行所有的View以及最大高度  
​
mLineHeight.add(lineHeight);
​// 将当前行的childView保存,然后开启新的ArrayList保存下一行的childView         mAllViews.add(lineViews);         
​
lineWidth = 0;// 重置行宽         
​
lineViews = new ArrayList();
​
}
​
/**
​
* 如果不需要换行,则累加
​
*/
​
lineWidth += childWidth + lp.leftMargin + lp.rightMargin;                    
​
lineHeight = Math.max(lineHeight, childHeight + lp.topMargin+ lp.bottomMargin);
​
lineViews.add(child);
​
}
​
// 记录最后一行
​
mLineHeight.add(lineHeight);
​
mAllViews.add(lineViews);
​
此循环小结
​
// 获取到所有的子view 以及子view的Marginlayoutparams
​
// 根据当前子view的宽度左右margin 以及当前行的lineWindth 判断是否换行
​
// 如果换行 则 将行高加入保存下来 并重置行宽行高以及行集合
​
// 并将行集合保存到总集合之中
​
// 如果不换行 则记录下当前行的行宽行高 并将当前view加入行集合
​
// 遍历完所有的集合之后将行高与行集合分别保存下来
​
// (因为遍历完所有的子view之后,最后一行肯定是不换行,所以行高和行集合都没有保存)
​
int left = 0;
​
int top = 0; 
​
// 得到总行数
​
int lineNums = mAllViews.size();
​
for (int i = 0; i < lineNums; i++)
​
{
​
// 每一行的所有的views
​
lineViews = mAllViews.get(i);
​
// 当前行的最大高度
​
lineHeight = mLineHeight.get(i);
​
Log.e(TAG, "第" + i + "行 :" + lineViews.size() + " , " + lineViews);
​
Log.e(TAG, "第" + i + "行, :" + lineHeight);
​
// 遍历当前行所有的View
​
for (int j = 0; j < lineViews.size(); j++)
​
{
​
View child = lineViews.get(j);
​
if (child.getVisibility() == View.GONE)
​
{
​
continue;
​
}
​
MarginLayoutParams lp = (MarginLayoutParams) child
​
.getLayoutParams();
​
//计算childView的left,top,right,bottom
​
int lc = left + lp.leftMargin;     左                   
​
int tc = top + lp.topMargin;       上
​
int rc =lc + child.getMeasuredWidth();  右
​
int bc = tc + child.getMeasuredHeight();  下
​
Log.e(TAG, child + " , l = " + lc + " , t = " + t + " , r ="
​
+ rc + " , b = " + bc);
​
child.layout(lc, tc, rc, bc);
​
left += child.getMeasuredWidth() + lp.rightMargin
​
+ lp.leftMargin;
​
}
​
left = 0;
​
top += lineHeight;
​
}
​
}

此循环小结

之后遍历总集合 得到行集合 然后根据相应的下标获取到每一行的行高遍历行集合 得到每一行的子view 然后获取每个子view的 左上坐标 右下坐标 然后调用子view的layout()获取子view的左坐标 初始left为0 每次计算完之后 将当前view的宽度相加最后设置每个子view的layout() 。

相关内容

热门资讯

无需预约、提供插座,星巴克回应... 华声在线7月22日讯(全媒体记者 仝若楠 通讯员 万家忻)近日,有网友在社交媒体上发帖表示,广东部分...
黑天鹅突袭!7个跌停! 【导读】*ST苏吴控股孙公司独家经销权将被撤销,拟撤销方被爱美客接手不久 中国基金报记者 闻言 7月...
鲍威尔:美联储须专注于确保大型... 来源:智通财经网 美联储主席鲍威尔周二表示,美联储的监管实践“必须集中在决定安全与稳健的关键领域”。...
香港黄金交易所优势加持,金荣中... 香港作为国际金融中心,凭借高度开放的金融市场、成熟完善的监管体系以及与国际接轨的投资环境,成为亚洲及...
股市必读:惠泰医疗(68861... 截至2025年7月22日收盘,惠泰医疗(688617)报收于295.66元,下跌0.2%,换手率0....
独家|外汇展业改革参与银行增至... 新媒体编辑 | 实习生 宋语菡 7月22日,国新办举行新闻发布会,国家外汇局副局长、新闻发言人李斌,...
今年以来12大类家电以旧换新销... 商务部7月22日消息,商务部流通发展司负责人表示,上半年,我国以旧换新成效显著,国货“潮品”深受欢迎...
AI英语教育站上风口 “伴鱼阅... 本报讯 (记者贾丽)国内在线教育企业北京读我科技有限公司(以下简称“伴鱼”)旗下核心产品“伴鱼阅读营...
罕见一幕!六大期货品种集体涨停... 在“反内卷”政策的刺激下,工业品期货全线大涨! 7月22日,商品期货午后大爆发,焦煤、焦炭、多晶硅、...
Club Med换帅落定:法国... Club Med换帅风波终落定。7月21日夜,复星旅文旗下核心资产Club Med正式任命法国籍高管...
金价又“疯”了!克价1021元... #热点新知#家人们谁懂啊!一觉醒来,金饰克价直接蹦回1021元 !咱楼下金店那电子屏,数字跳得比我工...
读懂IPO|穿透明略科技盈利表... 来源丨时代商业研究院 作者丨陈丽娜 编辑丨郑琳 当前人工智能行业普遍面临盈利难题,例如,素有中国人工...
大摩高管“抄底”香港楼市,一次... 据媒体报道,摩根士丹利董事总经理戴维·约翰·赖特(David John Wraight)以1.473...
山西打造“专精特新”专板 首批... “‘专精特新’企业是培育新质生产力的核心引擎,山西‘专精特新’专板建设是打通金融活水精准灌溉的关键通...
【数据发布】2025年上半年辽... 根据辽宁省地区生产总值统一核算结果,2025年上半年全市实现地区生产总值457.0亿元,按不变价格计...
赴港上市潮起,半导体企业为何扎... 【大河财立方 记者 王磊彬】2025年以来,港股IPO市场迎来新一轮半导体企业上市热潮。 据不完全统...
煤炭板块异动拉升,多只资源主题... 7月22日,市场全天震荡走高,三大指数盘中均创年内新高。板块方面,超级水电、工程机械、煤炭、水泥等板...
中国太平李可东“履新”,兼任2... 中国太平总经理李可东“履新”,兼任旗下两家子公司董事长。 7月21日,太平财险公告称,经国家金融监管...
胜宏科技回应赴港二次IPO,受... 瑞财经 吴文婷7月22日,据媒体报道,胜宏科技证券部工作人员回应筹划港股IPO一事,称公司主要基于资...
洪灏:港股如约创新高,向上空间... 洪灏系莲华资产管理公司管理合伙人&CIO,中国首席经济学家论坛理事 知名经济学家、 莲花投资公司合伙...