在自定义ListView中,需要将下拉刷新的View在初始化的时候设置padding隐藏起来,这时就要在初始化的时候获得要加载的布局View的高度。

private View headView;
headView = inflater.inflate(R.layout.header, null);

如果接下来调用:

headView.getHeight();
headView.getMeasuredHeight();

我们知道都会返回0,原因是getMeasuredHeight要在measure后才有值,getHeight要在layout后才有值。要在这里取得加载的布局的宽高,需要程序员调用measure测量出该布局的宽高。以前在写下拉ListView的时候有这样一段代码,一直不太明白,今天研究了一下:

private void measureView(View child) {
ViewGroup.LayoutParams lp = child.getLayoutParams();
if(lp == null){
lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
//headerView的宽度信息
int childMeasureWidth = ViewGroup.getChildMeasureSpec(0, 0, lp.width);
int childMeasureHeight;
if(lp.height > 0){
childMeasureHeight = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
//最后一个参数表示:适合、匹配
} else {
childMeasureHeight = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);//未指定
}
//System.out.println("childViewWidth"+childMeasureWidth);
//System.out.println("childViewHeight"+childMeasureHeight);
//将宽和高设置给child
child.measure(childMeasureWidth, childMeasureHeight);
}

在inflate完布局后,调用measureView(headView),然后再调用getMeasureHeight函数就可以获得实际高度了。

接下来从原理上分析一下这段代码:

通过inflater.inflate(R.layout.header, null);将XML转化为View的时候,是没有设置View的LayoutParams的,就会进入到lp ==null条件中new一个LayoutParams。接下来设置View的childMeasureWidth和childMeasureHeight这点比较费解。

childMeasureWidth:

调用 ViewGroup 的 getChildMeasureSpec 传递的第一个参数(父类的spec)竟然是0,传进0表示父类的spec是未指定的MeasureSpec.UNSPECIFIED模式,size也为0。那么该函数返回的值就是一个UNSPECIFIED类型size为0的值。

childMeasureHeight:

和上面类似,如果lp.height > 0就表示设置的是一个精确值,接下来会封装一个EXACTLY和lp.height的值给childMeasureHeight,然后这里lp.height是wrap_content,他是个负值(-2),所以会封装一个未指定UNSPECIFIED和size为0的值给childMeasureHeight。

即childMeasureWidth和childMeasureHeight两个参数均为0。

关于传递给child的widthMeasureSpec和heightMeasureSpec可以通过打印出的结果看出来确实是这样的。

然后将这两个值作为参数传递给子View的measure函数。

上次在张鸿洋大神的博客看到在划动删除代码里这样写,获取PopupWindow的宽高之前调用了mPopupWindow.getContentView().measure(0, 0);获取他的宽高。

View view = mInflater.inflate(R.layout.delete_btn, null);
mDelBtn = (Button) view.findViewById(R.id.id_item_btn);
mPopupWindow = new PopupWindow(view, LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
/**
* 先调用下measure,否则拿不到宽和高
*/
mPopupWindow.getContentView().measure(0, 0);
mPopupWindowHeight = mPopupWindow.getContentView().getMeasuredHeight();
mPopupWindowWidth = mPopupWindow.getContentView().getMeasuredWidth();

这样看来,其实和一开始给的代码是一样的。因为分析完开始的代码,我们发现最后执行child.measure(childMeasureWidth, childMeasureHeight);的时候两个参数就是0。

我最初很难理解父布局传递给子布局一个UNSPECIFIED和0的值。因为 默认情况下对于 View 来说会直接设置 :

setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));

里面的函数前面都学习过,很明显会将传过来的0设置给该View。但是调用这段代码后却是得到了真事的尺寸不是0。

带着问题,我将测量的布局仅仅写一个View,命名为 view_layout.xml:

<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#ffff0000"
/>

还有一个View被LinearLayout包裹着,viewgroup_layout.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<View
android:layout_width="50dp"
android:layout_height="50dp"
/>
</LinearLayout>

还有一个te xtview_layout.xml:

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="50dp"
android:layout_height="50dp"
android:text="Test"
/>

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="@+id/mainLayout"
>
</LinearLayout>

然后在onCerate的时候执行下面代码:

inflater = LayoutInflater.from(this);
mainLayout = (LinearLayout) findViewById(R.id.mainLayout);
view = inflater.inflate(R.layout.view_layout, null);
textview = inflater.inflate(R.layout.textview_layout, null);
viewgroup = inflater.inflate(R.layout.viewgroup_layout, null); measureView(view);
measureView(textview);
measureView(viewgroup); Log.i(TAG, "view的width和height分别是:" + view.getMeasuredWidth()+ " " + view.getMeasuredHeight());
Log.i(TAG, "textview的width和height分别是:" + textview.getMeasuredWidth()+ " " + textview.getMeasuredHeight());
Log.i(TAG, "viewgroup的width和height分别是:" + viewgroup.getMeasuredWidth()+ " " + viewgroup.getMeasuredHeight());

得到的结果是:

果然和最初解释的一样。对于View来说默认情况下会直接设置。

对于继承自View的TextView和ViewGroup而言却获得了其测量值。

原因在哪里?因为TextView和ViewGroup都重写了onMeasure。在ViewGroup 和TextView里面对AT_MOST和UNSPECIFIED的处理方式一样。

一直忽视了UNSPECIFIED很少使用,所以没能发现问题。不能惯性的认为onMeasure里面的操作就是调用setMeasuredDimension进行保存宽高的值。在这之前如果是ViewGroup还会具体的根据里面子View的情况进行处理。

既然谈到了View的尺寸,那么在将他们添加到父布局,再获取以下测量宽高,这时的宽高测量要在onWindowFocusChanged里面。

mainLayout.addView(view);
mainLayout.addView(textview);
mainLayout.addView(viewgroup); @Override
public void onWindowFocusChanged(boolean hasFocus) {
Log.i(TAG, "获得焦点后");
// Log.i(TAG, "view的width和height分别是:" + view.getMeasuredWidth()+ " " + view.getHeight());
Log.i(TAG, "textview的width和height分别是:" + textview.getMeasuredWidth()+ " " + textview.getMeasuredHeight());
Log.i(TAG, "viewgroup的width和height分别是:" + viewgroup.getMeasuredWidth()+ " " + viewgroup.getMeasuredHeight());
super.onWindowFocusChanged(hasFocus);
}

打印出来的Log如下:

View的宽高竟然有值了!原因是什么?在调用addView的时候会使试图重新绘制!至于宽和高为什么是这样的,那是因为调用generateDefaultLayoutParams返回的宽为match_parent高为wrap_content。最终传测量该View是的onMeasure的参数是AT_MOST和ScreenSize。因为是个View,直接调用setMeasuredDimension设置。

后面两个出现0的原因是View将屏幕占满了。如果不调用mainLayout.addView(view);可以看到:

改成"horizontal"后,打印信息如下:

至于高度,textview为15,由于是最外层,最终为包裹内容。Viewgroup的最终宽高为50dp,由于我的手机像素密度为0.75,因此换算为像素为38,结果一致。

这就告诉我,自己调用onMeasure只是暂时拿到了View的宽高测量值,这个值与传入的父布局的widthMeasureSpec, heightMeasureSpec有关。实际上添加到布局的真实宽高可能和这个值不一样。

ListView中也有类似的方法,和开始的方法基本一样。

private void measureItem(View child) {
ViewGroup.LayoutParams p = child.getLayoutParams();
if (p == null) {
p = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
} int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,
mListPadding.left + mListPadding.right, p.width);
int lpHeight = p.height;
int childHeightSpec;
if (lpHeight > 0) {
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
} else {
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
}
child.measure(childWidthSpec, childHeightSpec);
}
 

Android如何在初始化的时候获取加载的布局的宽高的更多相关文章

  1. Android中ViewPager+Fragment取消(禁止)预加载延迟加载(懒加载)问题解决方案

    转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/53205878本文出自[DylanAndroid的博客] Android中Vie ...

  2. Android Volley和Gson实现网络数据加载

    Android Volley和Gson实现网络数据加载 先看接口 1 升级接口 http://s.meibeike.com/mcloud/ota/cloudService POST请求 参数列表如下 ...

  3. 携程Android App的插件化和动态加载框架

    携程Android App的插件化和动态加载框架已上线半年,经历了初期的探索和持续的打磨优化,新框架和工程配置经受住了生产实践的考验.本文将详细介绍Android平台插件式开发和动态加载技术的原理和实 ...

  4. 我的Android进阶之旅------>Android疯狂连连看游戏的实现之加载界面图片和实现游戏Activity(四)

    正如在<我的Android进阶之旅------>Android疯狂连连看游戏的实现之状态数据模型(三)>一文中看到的,在AbstractBoard的代码中,当程序需要创建N个Piec ...

  5. Android 8.1 SystemUI虚拟导航键加载流程解析

    需求 基于MTK 8.1平台定制导航栏部分,在左边增加音量减,右边增加音量加 思路 需求开始做之前,一定要研读SystemUI Navigation模块的代码流程!!!不要直接去网上copy别人改的需 ...

  6. Android中使用WebView, WebChromeClient和WebViewClient加载网页 (能够执行js)

    Android中使用WebView, WebChromeClient和WebViewClient加载网页   在android应用中,有时要加载一个网页,如果能配上一个进度条就更好了,而android ...

  7. (六)《Java编程思想》——初始化及类的加载顺序

    package chapter7; /** * 初始化及类的加载顺序:顺序如下 * 1.基类的static变量 * 2.导出类的static变量 * 3.基类的变量 * 4.基类的构造函数 * 5.导 ...

  8. android: 动态加载碎片布局的技巧

    虽然动态添加碎片的功能很强大,可以解决很多实际开发中的问题,但是它毕竟只是在 一个布局文件中进行一些添加和替换操作.如果程序能够根据设备的分辨率或屏幕大小在运 行时来决定加载哪个布局,那我们可发挥的空 ...

  9. creator 2.0版本对于preloadScene函数获取加载进度

    有时候,当我们场景上挂载的资源过多时,我们使用cc.director.loadScene切换场景时会等一段时间才会切换过去,这对游戏的体验是相当不好的.所以我们可以使用cc.director.prel ...

随机推荐

  1. centos+nginx+redmine+gitosis安装指南

    说明 这篇文章我现在的主要目的是记录自己安装redmine和gitosis的过程,可能写的有些糙,请各位读者见谅.我会在后面的时间里逐渐完善细节.但我想,这已经是网上迄今为止国内最详细的nginx+r ...

  2. 使用Response.Write实现在页面的生命周期中前后台的交互

    Response.Write()方法非常的常见,也很普通,就是向http output中输出一string.其输出的内容位于页面的最顶端,常用来实现显示一些页面消息框等逻辑. 一般来说,在页面的整个生 ...

  3. Fluent Python: Mutable Types as Parameter Defaults: Bad Idea

    在Fluent Python一书第八章有一个示例,未看书以先很难理解这个示例运行的结果,我们先看结果,然后再分析问题原因: 定义了如下Bus类: class Bus: def __init__(sel ...

  4. canvas学习(二):渐变与曲线的绘制

    canvas学习(二):渐变与曲线的绘制 一:createLinearGradient()线性渐变: 二:createLinearGradient() 放射状/圆形渐变: 三:createPatter ...

  5. 软工第三次作业——个人PSP

    9.22--9.26本周例行报告 1.PSP(personal software process )个人软件过程. 类型 任务 预计时间 开始时间 结束时间 中断时间 实际用时 准备工作 学习重定向 ...

  6. Alpha冲刺——第二天

    Alpha第二天 听说 031502543 周龙荣(队长) 031502615 李家鹏 031502632 伍晨薇 031502637 张柽 031502639 郑秦 1.前言 任务分配是VV.ZQ. ...

  7. LintCode-61.搜索区间

    搜索区间 给定一个包含 n 个整数的排序数组,找出给定目标值 target 的起始和结束位置. 如果目标值不在数组中,则返回[-1, -1] 样例 给出[5, 7, 7, 8, 8, 10]和目标值t ...

  8. LintCode-532.逆序对

    逆序对 在数组中的两个数字如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.给你一个数组,求出这个数组中逆序对的总数. 概括:如果a[i] > a[j] 且 i < j, a[i ...

  9. iOS- 多线程技术的概述及优点

    1.概述 在iOS开发中: •耗时操作,例如网络图片.视频.歌曲.书籍等资源下载 •游戏中的声音播放   我们可以利用多线程: •充分发挥多核处理器的优势,并发(同时执行)执行任务让系统运行的更快.更 ...

  10. css3边框阴影效果

    下面来说下css3阴影的语法: box-shadow:none | <shadow> [ , <shadow> ]* <shadow> = inset? & ...