众所周知,在Android实际开发中,对于某些复杂多变的情况,控件的位置摆放、大小控制并非是xml类型的layout文件完全可以搞定的。此时,我们通常会使用Java代码来通过动态计算,将指定的控件摆放在相应的位置,并限定其大小。同样地,也需要获取某个控件的大小。

对于获取控件宽、高的方法,大家可以自行谷歌或者百度,大抵无非一下三种方法:

  1. 给相应的View控件添加ViewTreeObserver回调
  2. Override onWindowFocusChange方法
  3. 在需要测量时(而不是onCreate或onResume中),使用MeasureSpec内部类获取宽高。

对于上述第三种情况,我们暂且不论。对于前二者而言,有没有更简单的实现呢?

为何获取宽高要如此?

对于初学者,可能会有这样的疑问:为什么我们不能在onCreate()或者onResume()中直接使用上述第三种方案获取宽高呢?

结论是:那样的话,获取来的值很可能皆为0,即使实际的宽高不是0。那么这是为何呢?

这其实是由Android的UI绘制流程决定的。大家不妨试着做一下实验,即使是在onResume()方法后,它的意义也仅仅是指Activity进入了可见的状态,这并不意味着界面绘制的结束。我们可以用一个简单的带有宽高值得View来做实验,观察Activity中各回调方法的调用顺序,得到的结果将是这样的:

Activity.oncreate() → Activity.onResume() → View.onMeasure() → View.onLayout() → onGlobalLayoutListener() → Activity.onWidnowFocusChanged() → ... → View.onDraw() -> ...

因此,如果我们在onResume()中尝试获取View宽高的话,很大概率是会失败的。

巧用Handler获取View控件信息

这里我们开门见山地先放上代码片:

  1. private int[] measureView(final View view) {
  2. final int[] returnData = new int[2];
  3. view.post(new Runnable() {
  4. @Override
  5. public void run() {
  6. returnData[0] = view.getWidth();
  7. returnData[1] = view.getHeight();
  8. Log.i(TAG, "Width: " + returnData[0] + ", height: " + returnData[1]);
  9. }
  10. });
  11. return returnData;
  12. }

上述代码作为通用的方法将获取任意View的宽高做了封装,其妙处就在‘view.post’处。

将其置于onCreate()、onResume()方法中调用,均可获取到正确的宽高。

  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3. super.onCreate(savedInstanceState);
  4. Log.i(TAG, "onCreate start!");
  5. setContentView(R.layout.activity_main);
  6. testTv = (TextView) findViewById(R.id.testTv);
  7. measureView(testTv);
  8. }
  9. @Override
  10. protected void onResume() {
  11. super.onResume();
  12. Log.i(TAG, "onResume start!");
  13. measureView(testTv);
  14. }

Logcat中的运行结果:

2019-01-14 22:33:13.874 18355-18355/com.example.wenhan.helloandroid I/MainActivity: Width: 225, height: 57

2019-01-14 22:33:13.874 18355-18355/com.example.wenhan.helloandroid I/MainActivity: Width: 225, height: 57

为何如此就可获取到正确的值了呢?

其中的玄机在于,我们在View.post()中所写的语句并没有立即执行,而在其真正执行的时候,View的宽高已经被测量完成了,那时我们再去获取宽高时,就会很容易地获取到正确的值了。

通过断点Debug,可以轻松地发现,在Activity启动过程的调用栈中,存在ActivityThread类被执行了,具体按照:

main() -> handleResumeActivity() -> addView() -> setView() -> requestLayout() -> scheduleTraversals() -> 执行mTraversalRunnable异步线程 -> doTraversal() -> performTraversals() -> ... -> performMeasure() -> ...

的执行顺序。

在我们获取宽高的语句执行前,主线程的Handler正在执行TraversalRunnable(见上述方法具体实现),而performMeasure也被包含其中。又因为我们获取宽高的语句要排队,处于等待状态,直到主线程Handler轮到执行我们的语句,而此时View的宽高的测量已经结束。

完整示例代码: Github

巧用Handler获取View控件信息的更多相关文章

  1. appium获取APP控件信息

    uiautomatorviewer.bat 该文件位于SDK安装目录tools下,如笔者在“C:\Program Files (x86)\Android\android-sdk\tools”下,双击u ...

  2. Android自动化测试中AccessibilityService获取控件信息(1)

    Android自动化测试中AccessibilityService获取控件信息(1) 分类: android自动化测试2014-03-24 15:31 3455人阅读 评论(16) 收藏 举报 and ...

  3. UiAutomator源码分析之获取控件信息

    根据上一篇文章<UiAutomator源码分析之注入事件>开始时提到的计划,这一篇文章我们要分析的是第二点: 如何获取控件信息 我们在测试脚本中初始化一个UiObject的时候通常是像以下 ...

  4. C# 获取往控件中拖进的文件或文件夹的信息

    C# 获取往控件中拖进的文件或文件夹的信息(原创)       在做C#的WinForm开发的时候,有时需要用户往指定的控件中拖进文件或者文件夹.然后根据用户拖进来的文件或者文件夹获取其信息并进行下一 ...

  5. Android自动化测试中AccessibilityService获取控件信息(2)-三种方式对比

    Android自动化测试中AccessibilityService获取控件信息(2)-三种方式对比   上一篇文章: Android自动化测试中AccessibilityService获取控件信息(1 ...

  6. UiAutomator源代码分析之获取控件信息

    依据上一篇文章<UiAutomator源代码分析之注入事件>開始时提到的计划,这一篇文章我们要分析的是第二点: 怎样获取控件信息 我们在測试脚本中初始化一个UiObject的时候一般是像下 ...

  7. iOS开发UI篇—使用picker View控件完成一个简单的选餐应用

    iOS开发UI篇—使用picker View控件完成一个简单的选餐应用 一.实现效果 说明:点击随机按钮,能够自动选取,下方数据自动刷新. 二.实现思路 1.picker view的有默认高度为162 ...

  8. 获取android控件的高度

    问题 如何获取一个控件的长和高,相信很多朋友第一眼看见这个问题都会觉得很简单,直接在onCreate里面调用getWidth.getMeasuredWidth不就可以获得了吗,但是,事实上是并没有简单 ...

  9. pywinauto如何获取gridwindow控件的屏幕位置

    一:问题描述 问题一:通过查找pywinauto在线文档,其中没有讲解到gridwindow控件的方法,我不知道这个控件是不是标准控件,还是pywinauto根本就没适配这个控件.从网上查询了好多资料 ...

随机推荐

  1. Unity_新手必懂知识点

    翻车了!!!一个小例子带你了解闭包.事故现场:场景:6个button,上方1个text.点击button,text会显示button上的数字.代码如下: //在unity里面赋值public List ...

  2. MobileForm控件的使用方式-用.NET(C#)开发APP的学习日志

    今天继续Smobiler开发APP的学习日志,这次是做一个title.toolbar.侧边栏三种效果 样式一 一.          Toolbar 1.       目标样式 我们要实现上图中的效果 ...

  3. str.方法的整理(字符串类型内置方法的具体使用)

    <1>str.strip().str.lstrip()和str.rstrip() 1' str.strip()(主要方法) 方法:str.strip(self,chars) 作用:移除字符 ...

  4. Git:三、工作原理

    首先,我们对工作区也就是文件夹中的文档进行修改. 然后,把修改并需要存档的文档用add命令放到暂存区,并且可以放很多文档. 最后,一个阶段的工作告一段落,使用commit命令把暂存区的内容一股脑存到G ...

  5. CSS3D 转换调试

    css3d 测试工具 效果如图: 代码如下: <!DOCTYPE html> <html lang="zh-CN"> <head> <me ...

  6. ABP学习笔记总汇

    首先立下一个目标,未来一段时间开始学习ABP. 先立一个flag.之后会再次更新目录和文章连接 目录 1.ABP学习笔记(1)-使用mysql

  7. TensorFlow 2.0 Alpha pip安装指令

    TensorFlow 2.0 Alpha目前已经可以通过pip安装,亲测有效,安装指令为: # 普通版本 pip install tensorflow==2.0.0-alpha0 # GPU版本 pi ...

  8. 5. VIM 系列 - 文件管理

    目录 目录树 文件检索 ctrlp.vim fzf.vim 目录树 安装 nerdtree.vim 插件 Plug 'scrooloose/nerdtree', { 'on': 'NERDTreeTo ...

  9. 将Python 程序打包成 .exe格式入门

    PyInstaller PyInstaller 是一个十分有用的第三方库,可以用来打包 python 应用程序,打包完的程序就可以在没有安装 Python 解释器的机器上运行了. 它能够在 Windo ...

  10. 【深度学习】--DCGAN从入门到实例应用

    一.前述 DCGAN就是Deep Concolutions应用到GAN上,但是和传统的卷积应用还有一些区别,最大的区别就是没有池化层.本文将详细分析卷积在GAN上的应用. 二.具体 1.DCGAN和传 ...