自定义View中的ListView和ScrollView嵌套的问题
当我们在使用到ScrollView和ListView的时候可能会出现显示不全的问题。那我们可以进行以下分析
ScrollView在测量子布局的时候会用UNSPECIFIED。通过源码观察,
在ScrollView的onMeasure方法中
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);if (!mFillViewport) {return;}final int heightMode = MeasureSpec.getMode(heightMeasureSpec);if (heightMode == MeasureSpec.UNSPECIFIED) {return;}if (getChildCount() > 0) {final View child = getChildAt(0);final int widthPadding;final int heightPadding;final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;final FrameLayout.LayoutParams lp = (LayoutParams) child.getLayoutParams();if (targetSdkVersion >= VERSION_CODES.M) {widthPadding = mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin;heightPadding = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin;} else {widthPadding = mPaddingLeft + mPaddingRight;heightPadding = mPaddingTop + mPaddingBottom;}final int desiredHeight = getMeasuredHeight() - heightPadding;if (child.getMeasuredHeight() < desiredHeight) {final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec, widthPadding, lp.width);final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(desiredHeight, MeasureSpec.EXACTLY);child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}}}
点击super.onMeasure()方法可以观察到进入了FrameLayout中
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int count = getChildCount();final boolean measureMatchParentChildren =MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;mMatchParentChildren.clear();int maxHeight = 0;int maxWidth = 0;int childState = 0;for (int i = 0; i < count; i++) {final View child = getChildAt(i);if (mMeasureAllChildren || child.getVisibility() != GONE) {measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);final LayoutParams lp = (LayoutParams) child.getLayoutParams();maxWidth = Math.max(maxWidth,child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);maxHeight = Math.max(maxHeight,child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);childState = combineMeasuredStates(childState, child.getMeasuredState());if (measureMatchParentChildren) {if (lp.width == LayoutParams.MATCH_PARENT ||lp.height == LayoutParams.MATCH_PARENT) {mMatchParentChildren.add(child);}}}}// Account for padding toomaxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();// Check against our minimum height and widthmaxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());// Check against our foreground's minimum height and widthfinal Drawable drawable = getForeground();if (drawable != null) {maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());}setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),resolveSizeAndState(maxHeight, heightMeasureSpec,childState << MEASURED_HEIGHT_STATE_SHIFT));count = mMatchParentChildren.size();if (count > 1) {for (int i = 0; i < count; i++) {final View child = mMatchParentChildren.get(i);final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();final int childWidthMeasureSpec;if (lp.width == LayoutParams.MATCH_PARENT) {final int width = Math.max(0, getMeasuredWidth()- getPaddingLeftWithForeground() - getPaddingRightWithForeground()- lp.leftMargin - lp.rightMargin);childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);} else {childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,getPaddingLeftWithForeground() + getPaddingRightWithForeground() +lp.leftMargin + lp.rightMargin,lp.width);}final int childHeightMeasureSpec;if (lp.height == LayoutParams.MATCH_PARENT) {final int height = Math.max(0, getMeasuredHeight()- getPaddingTopWithForeground() - getPaddingBottomWithForeground()- lp.topMargin - lp.bottomMargin);childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);} else {childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,getPaddingTopWithForeground() + getPaddingBottomWithForeground() +lp.topMargin + lp.bottomMargin,lp.height);}child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}}}
里面有个measureChildWithMargins方法
protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec, int heightUsed) {final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin+ widthUsed, lp.width);final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin+ heightUsed, lp.height);child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}
这个时候发现ScrollView有重写这个方法
@Overrideprotected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec, int heightUsed) {final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin+ widthUsed, lp.width);final int usedTotal = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin +heightUsed;final int childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - usedTotal),MeasureSpec.UNSPECIFIED);child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}
其中childHeightMeasureSpec的模式是MeasureSpec.UNSPECIFIED。这个表示尽可能的大,很少能用到。
而我们的ListView也会调用onMeaure方法
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// Sets up mListPaddingsuper.onMeasure(widthMeasureSpec, heightMeasureSpec);final int widthMode = MeasureSpec.getMode(widthMeasureSpec);final int heightMode = MeasureSpec.getMode(heightMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);int childWidth = 0;int childHeight = 0;int childState = 0;mItemCount = mAdapter == null ? 0 : mAdapter.getCount();if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED|| heightMode == MeasureSpec.UNSPECIFIED)) {final View child = obtainView(0, mIsScrap);// Lay out child directly against the parent measure spec so that// we can obtain exected minimum width and height.measureScrapChild(child, 0, widthMeasureSpec, heightSize);childWidth = child.getMeasuredWidth();childHeight = child.getMeasuredHeight();childState = combineMeasuredStates(childState, child.getMeasuredState());if (recycleOnMeasure() && mRecycler.shouldRecycleViewType(((LayoutParams) child.getLayoutParams()).viewType)) {mRecycler.addScrapView(child, 0);}}if (widthMode == MeasureSpec.UNSPECIFIED) {widthSize = mListPadding.left + mListPadding.right + childWidth +getVerticalScrollbarWidth();} else {widthSize |= (childState & MEASURED_STATE_MASK);}if (heightMode == MeasureSpec.UNSPECIFIED) {heightSize = mListPadding.top + mListPadding.bottom + childHeight +getVerticalFadingEdgeLength() * 2;}if (heightMode == MeasureSpec.AT_MOST) {// TODO: after first layout we should maybe start at the first visible position, not 0heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);}setMeasuredDimension(widthSize, heightSize);mWidthMeasureSpec = widthMeasureSpec;}
其中的heightMeasureSpec也就是我们在ScrollView中child.measure(childWidthMeasureSpec, childHeightMeasureSpec);在传递过来的。所以也是模式为MeasureSpec.UNSPECIFIED。而当heightMode == MeasureSpec.UNSPECIFIED时会调用
if (heightMode == MeasureSpec.UNSPECIFIED) {heightSize = mListPadding.top + mListPadding.bottom + childHeight +getVerticalFadingEdgeLength() * 2;}
这样在heightSize = mListPadding.top + mListPadding.bottom + childHeight +getVerticalFadingEdgeLength() * 2只是加了一个childHeight 所以展示的只有一个item的大小。
所以要解决这个问题应该让listView进入heightMode == MeasureSpec.AT_MOST里。
public class MyListView extends ListView
{public MyListView(Context context) {super(context);}public MyListView(Context context, AttributeSet attrs) {super(context, attrs);}public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//解决显示不全的问题heightMeasureSpec=MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,MeasureSpec.AT_MOST);super.onMeasure(widthMeasureSpec, heightMeasureSpec);}
}
网上有的博客换成MyListView这么做就实现了,那么为什么呢?
首先当我们的模式为AT_MOST
if (heightMode == MeasureSpec.AT_MOST) {// TODO: after first layout we should maybe start at the first visible position, not 0heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);}
调用ListView的measureHeightOfChildren
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition,int maxHeight, int disallowPartialChildPosition) {final ListAdapter adapter = mAdapter;if (adapter == null) {return mListPadding.top + mListPadding.bottom;}// Include the padding of the listint returnedHeight = mListPadding.top + mListPadding.bottom;final int dividerHeight = mDividerHeight;// The previous height value that was less than maxHeight and contained// no partial childrenint prevHeightWithoutPartialChild = 0;int i;View child;// mItemCount - 1 since endPosition parameter is inclusiveendPosition = (endPosition == NO_POSITION) ? adapter.getCount() - 1 : endPosition;final AbsListView.RecycleBin recycleBin = mRecycler;final boolean recyle = recycleOnMeasure();final boolean[] isScrap = mIsScrap;for (i = startPosition; i <= endPosition; ++i) {child = obtainView(i, isScrap);measureScrapChild(child, i, widthMeasureSpec, maxHeight);if (i > 0) {// Count the divider for all but one childreturnedHeight += dividerHeight;}// Recycle the view before we possibly return from the methodif (recyle && recycleBin.shouldRecycleViewType(((LayoutParams) child.getLayoutParams()).viewType)) {recycleBin.addScrapView(child, -1);}returnedHeight += child.getMeasuredHeight();if (returnedHeight >= maxHeight) {// We went over, figure out which height to return. If returnedHeight > maxHeight,// then the i'th position did not fit completely.return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1)&& (i > disallowPartialChildPosition) // We've past the min pos&& (prevHeightWithoutPartialChild > 0) // We have a prev height&& (returnedHeight != maxHeight) // i'th child did not fit completely? prevHeightWithoutPartialChild: maxHeight;}if ((disallowPartialChildPosition >= 0) && (i >= disallowPartialChildPosition)) {prevHeightWithoutPartialChild = returnedHeight;}}// At this point, we went through the range of children, and they each// completely fit, so return the returnedHeightreturn returnedHeight;}
因为returnedHeight一直在累加,这样我们让maxHeight为最大值,这样他就不会进入if (returnedHeight >= maxHeight) ,而是返回returnedHeight。
onMeasure方法中的widthMeasureSpec和heightMeasureSpec会包含两个信息,第一个信息是模式:2位,第二个信息是值:30位。一共32位


而这里为什么Integer.MAX_VALUE 要右移两位
public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,@MeasureSpecMode int mode) {if (sUseBrokenMakeMeasureSpec) {return size + mode;} else {return (size & ~MODE_MASK) | (mode & MODE_MASK);}}
因为Integer.MAX_VALUE右移两位等于一个30位的值,这样执行makeMeasureSpec就可以构建成一个32位的值。这样去替换ListView的heightMeasureSpec即可。
/*** A constant holding the maximum value an {@code int} can* have, 2<sup>31</sup>-1.*/@Native public static final int MAX_VALUE = 0x7fffffff;
相关文章:
自定义View中的ListView和ScrollView嵌套的问题
当我们在使用到ScrollView和ListView的时候可能会出现显示不全的问题。那我们可以进行以下分析 ScrollView在测量子布局的时候会用UNSPECIFIED。通过源码观察, 在ScrollView的onMeasure方法中 Overrideprotected void onMeasure(int widthMeasureSpec, int heightMe…...
支持向量机 SVM | 线性可分:硬间隔模型公式推导
目录 一. SVM的优越性二. SVM算法推导小节概念 在开始讲述SVM算法之前,我们先来看一段定义: 支持向量机(Support VecorMachine, SVM)本身是一个二元分类算法,支持线性分类和非线性分类的分类应用,同时通过OvR或者OvO的方式可以应用…...
【Unity实战】UGUI和Z轴排序那点事儿
如果读者是从Unity 4.x时代过来的,可能都用过NGUI这个插件(后来也是土匪成了正规军),NGUI一大特点是可以靠transform位移的Z值进行遮挡排序,然而这个事情在UGUI成了难题(Sorting Layer、Inspector顺序等因素…...
Vue/React 前端高频面试
说一说vue钩子函数 钩子函数是Vue实例创建和销毁过程中自动执行的函数。按照组件生命周期的过程分为:挂载阶段 -> 更新阶段 -> 销毁阶段。 每个阶段对应的钩子函数分别为:挂载阶段(beforeCreate,created,befor…...
[技巧]Arcgis之图斑四至范围批量计算
ArcGIS图层(点、线、面三类图形)四至范围计算 例外一篇介绍:[技巧]Arcgis之图斑四至点批量计算 说明:如下图画出来的框(范围标记不是很准) ,图斑的x最大和x最小,y最大,…...
C/C++工程师面试题(STL篇)
STL 中有哪些常见的容器 STL 中容器分为顺序容器、关联式容器、容器适配器三种类型,三种类型容器特性分别如下: 1. 顺序容器 容器并非排序的,元素的插入位置同元素的值无关,包含 vector、deque、list vector:动态数组…...
Effective Programming 学习笔记
1 基本语句 1.1 断言 在南溪看来,断言可以用来有效地确定编程中当前代码运行的前置条件,尤其是以下情况: 第三方工具库对输入数据的依赖,例如:minitouch库对Android版本的要求...
【MGR】MySQL Group Replication 背景
目录 17.1 Group Replication Background 17.1.1 Replication Technologies 17.1.1.1 Primary-Secondary Replication 17.1.1.2 Group Replication 17.1.2 Group Replication Use Cases 17.1.2.1 Examples of Use Case Scenarios 17.1.3 Group Replication Details 17.1…...
300分钟吃透分布式缓存-17讲:如何理解、选择并使用Redis的核心数据类型?
Redis 数据类型 首先,来看一下 Redis 的核心数据类型。Redis 有 8 种核心数据类型,分别是 : & string 字符串类型; & list 列表类型; & set 集合类型; & sorted set 有序集合类型&…...
思科网络设备监控
思科是 IT 行业的先驱之一,提供从交换机到刀片服务器的各种设备,以满足中小企业和企业的各种 IT 管理需求。管理充满思科的 IT 车间涉及许多管理挑战,例如监控可用性和性能、管理配置更改、存档防火墙日志、排除带宽问题等等,这需…...
深入剖析k8s-控制器思想
引言 本文是《深入剖析Kubernetes》学习笔记——《深入剖析Kubernetes》 正文 控制器都遵循K8s的项目中一个通用的编排模式——控制循环 for {实际状态 : 获取集群中对象X的实际状态期望状态 : 获取集群中对象X的期望状态if 实际状态 期望状态 {// do nothing} else {执行…...
go并发模式之----使用时顺序模式
常见模式之二:使用时顺序模式 定义 顾名思义,起初goroutine不管是怎么个先后顺序,等到要使用的时候,需要按照一定的顺序来,也被称为未来使用模式 使用场景 每个goroutine函数都比较独立,不可通过参数循环…...
[动态规划]---part1
前言 作者:小蜗牛向前冲 专栏:小蜗牛算法之路 专栏介绍:"蜗牛之道,攀登大厂高峰,让我们携手学习算法。在这个专栏中,将涵盖动态规划、贪心算法、回溯等高阶技巧,不定期为你奉上基础数据结构…...
java 关于 Object 类中的 wait 和 notify 方法。(生产者和消费者模式!)
4、关于 Object 类中的 wait 和 notify 方法。(生产者和消费者模式!) 第一:wait 和 notify 方法不是线程对象的方法,是 java 中任何一个 java 对象都有的方法,因为这两个方法是 Object 类中自带的。 wait 方…...
YOLOv8姿态估计实战:训练自己的数据集
课程链接:https://edu.csdn.net/course/detail/39355 YOLOv8 基于先前 YOLO 版本的成功,引入了新功能和改进,进一步提升性能和灵活性。YOLOv8 同时支持目标检测和姿态估计任务。 本课程以熊猫姿态估计为例,将手把手地教大家使用C…...
【海贼王的数据航海:利用数据结构成为数据海洋的霸主】链表—双向链表
目录 往期 1 -> 带头双向循环链表(双链表) 1.1 -> 接口声明 1.2 -> 接口实现 1.2.1 -> 双向链表初始化 1.2.2 -> 动态申请一个结点 1.2.3 -> 双向链表销毁 1.2.4 -> 双向链表打印 1.2.5 -> 双向链表判空 1.2.6 -> 双向链表尾插 1.2.7 -&…...
做测试还是测试开发,选职业要慎重!
【软件测试面试突击班】2024吃透软件测试面试最全八股文攻略教程,一周学完让你面试通过率提高90%!(自动化测试) 突然发现好像挺多人想投测开和测试的,很多人面试的时候也会被问到这几个职位的区别,然后有测…...
Java面试题总结200道(二)
26、简述Spring中Bean的生命周期? 在原生的java环境中,一个新的对象的产生是我们用new()的方式产生出来的。在Spring的IOC容器中,将这一部分的工作帮我们完成了(Bean对象的管理)。既然是对象,就存在生命周期,也就是作用…...
面试数据库篇(mysql)- 03MYSQL支持的存储引擎有哪些, 有什么区别
存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式 。存储引擎是基于表的,而不是基于库的,所以存储引擎也可被称为表类型。 MySQL体系结构 连接层服务层引擎层存储层 存储引擎特点 InnoDB MYSQL支持的存储引擎有哪些, 有什么区别 ? my…...
MySQL深入——25
Join语句如何优化? Join语句的两种算法,分别为Index Nested-Loop Join和Block Nested-Loop Join NLJ在大表Join当中还不错,但BNL在大表join时性能就差很多,很耗CPU资源。 如何优化这两个算法 创建t1,t2算法,在t1中…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
