自定义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;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/709765.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL数据库安装与配置全攻略

MySQL安装配置教程 MySQL是一种流行的开源数据库管理系统&#xff0c;广泛用于网站和服务器应用程序中存储数据。本教程将引导你完成在Windows系统上安装和配置MySQL数据库的步骤。 一、下载MySQL安装包 访问MySQL官方网站&#xff1a;MySQL Downloads选择"MySQL Commu…

MySQL进阶:大厂高频面试——各类SQL语句性能调优经验

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位大四、研0学生&#xff0c;正在努力准备大四暑假的实习 &#x1f30c;上期文章&#xff1a;MySQL进阶&#xff1a;强推&#xff0c;冲大厂必精通&#xff01;MySQL索引底层&#xff08;BTree&#xff09;、性能分析、使用…

HTTP笔记(五)

个人学习笔记&#xff08;整理不易&#xff0c;有帮助点个赞&#xff09; 笔记目录&#xff1a;学习笔记目录_pytest和unittest、airtest_weixin_42717928的博客-CSDN博客 目录 一&#xff1a;HTTP报文首部 &#xff08;1&#xff09;HTTP请求报文 &#xff08;2&#xff09…

WPF MVVM中List<>和ObservableCollection<>的区别与对比分析

在WPF MVVM&#xff08;模型-视图-视图模型&#xff09;架构中&#xff0c;数据绑定是实现UI与后端逻辑分离的关键特性。为了使UI能够响应后端数据的变化&#xff0c;通常需要用到特定的集合类型。在WPF中&#xff0c;最常见的两种集合类型是List< T>和ObservableCollect…

Kaggle 竞赛入门

打比赛不用写算法源码&#xff0c;应用的时候不用自己写。学习的时候可以自己写。 Kaggle 竞赛入门 认识 Kaggle 平台Kaggle竞赛知识前提结构化数据前提图像数据文本数据 Kaggle竞赛套路一个赛题的完整流程 认识 Kaggle 平台 Kaggle 官网 主页&#xff0c;比赛&#xff08;数据…

复现nerfstudio并训练自己制作的数据集

网站&#xff1a;安装 - nerfstudio GitHub - nerfstudio-project/nerfstudio&#xff1a;NeRF 的协作友好工作室 安装之前要确保电脑上已经有CUDA11.8或以上版本&#xff08;更高版本的可以安装11.8的toolkit&#xff09; 创建环境 conda create --name nerfstudio -y pyt…

Vector容器介绍

一、引言 在C标准库中&#xff0c;vector是一个非常重要的容器&#xff0c;它提供了动态数组的功能。与静态数组不同&#xff0c;vector可以在运行时动态地增加或减少其大小。这使得vector在处理不确定数量的数据时变得非常有用。vector容器保证了元素的连续存储&#xff0c;因…

浅谈 Linux 网络编程 - Server 端模型、sockaddr、sockaddr_in 结构体

文章目录 前言前置知识Server 端核心模型 【重点】相关函数 【重点】socket 函数bind 函数listen 函数accept 函数close 函数 sockaddr 数据结构 【重点】 前言 本文主要是对 Linux 网络编程中&#xff0c;Server 端的模型、相关函数 以及 sockaddr、sockaddr_in 结构体做介绍…

图片压缩上传实例

前台图片上传压缩是一种常见的需求&#xff0c;可以通过前端技术实现图片的压缩&#xff0c;减小图片的大小&#xff0c;提高页面加载速度。以下是一个前台图片上传压缩的实例&#xff1a; 首先&#xff0c;在前端页面中添加一个文件上传的input标签&#xff1a; <input ty…

黑马程序员——接口测试——day05——Request库、Cookie、Session、UnitTest框架

目录&#xff1a; Requests库 Requests库安装和简介设置http请求语法应用案例 案例1案例2案例3案例4Cookie Cookie简介CookieSession认证方式案例5-看演示&#xff0c;此代码不需实现Session Session简介Session自动管理Cookie案例6面试题Cookie和Session区别获取指定响应数据…

Codeforces Round 929 (Div. 3)

A. 全部负数处理到前面&#xff0c;全部都能变成正数 // Problem: A. Turtle Puzzle: Rearrange and Negate // Contest: Codeforces - Codeforces Round 929 (Div. 3) // URL: https://codeforces.com/contest/1933/problem/A // Memory Limit: 256 MB // Time Limit: 2000 …

300分钟吃透分布式缓存(拉钩教育总结)

开篇寄语 开篇寄语&#xff1a;缓存&#xff0c;你真的用对了吗&#xff1f; 你好&#xff0c;我是你的缓存老师陈波&#xff0c;可能大家对我的网名 fishermen 会更熟悉。 我是资深老码农一枚&#xff0c;经历了新浪微博从起步到当前月活数亿用户的大型互联网系统的技术演进…

Linux|centos7|yum和编译安装ImageMagick记录

一&#xff0c; yum安装imagemagick imagemagick这个软件是图像文件的处理神器&#xff0c;可以文字转图像以及图像的剪辑等等工作&#xff0c;也是配合人工智能工程的不可或缺的工具&#xff0c;具体的用处和特点就不在这里废话了&#xff0c;有兴趣的百度就行了 本次是在…

SpringBoot底层原理

SpringBoot底层原理 一 配置优先级 1.配置方式 Springboot中支持三种配置方式&#xff0c;分别为&#xff1a; application.propertiesapplication.ymlapplication.yaml 2.配置优先级 当存在多份配置文件时&#xff0c;配置文件会按照它们的优先级生效。 优先级从高到底…

Linux中df和du命令

当涉及到在Linux系统中管理磁盘空间时&#xff0c;df和du命令是非常有用的工具。除了基本用法外&#xff0c;它们还具有一些高级用法&#xff0c;可以提供更详细和定制化的磁盘信息。下面是Linux中df和du命令的十个常用的高级用法&#xff0c;附带相应的代码和输出。 df -i - 显…

欧盟网络安全产业分析

文章目录 前言一、欧盟网络安全战略历程二、欧盟网络安全政策法规2.1 《电子识别和信任服务条例》2.2 《支付服务指令(第二版)》2.3 NIS 指令系列三.欧盟主要的网络安全机构3.1 ENISA3.2 欧盟委员会3.3 欧洲数据保护委员会3.4 欧盟成员国自设的网络安全机构四、欧盟网络安全…

蓝桥杯-灌溉

参考了大佬的解题思路&#xff0c;先遍历一次花园&#xff0c;找到所有的水源坐标&#xff0c;把它们存入 “水源坐标清单” 数组内&#xff0c;再读取数组里的水源坐标进行扩散。 #include <iostream> using namespace std; int main() {int n,m,t,r,c,k,ans0,list_i0;…

c# aes加密解密私钥公钥通钥

using System.Security.Cryptography; using System.Text; namespace EncryptTest { internal class Program { static void Main(string[] args) { Console.WriteLine("Hello, World!"); string 密 EncryptAESBASE64("你…

(每日持续更新)jdk api之OutputStreamWriter基础、应用、实战

博主18年的互联网软件开发经验&#xff0c;从一名程序员小白逐步成为了一名架构师&#xff0c;我想通过平台将经验分享给大家&#xff0c;因此博主每天会在各个大牛网站点赞量超高的博客等寻找该技术栈的资料结合自己的经验&#xff0c;晚上进行用心精简、整理、总结、定稿&…

c++ for 循环语句

循环语句 在C中&#xff0c;有几种循环语句可用于重复执行一段代码&#xff0c;直到满足指定条件为止&#xff0c;主要有 for 循环、while 循环、do-while 循环三种循环语句。三者区别&#xff1a; 循环类型特点for 循环1. 适用于已知循环次数的情况&#xff0c;循环次数事先…