测量控件宽高的四种方案

Android开发过程中,有时候需要测量某一个view的宽高,尤其是初始化的时候,但是在Activity的onCreate,onStart,onResume方法中都不能正确测量宽高。因为view的measure过程和Activity的生命周期不是同步执行的。
那么怎么测量呢,有四种方法,这里记录下来。

onWindowFocusChanged

   /**
     * Called when the current {@link Window} of the activity gains or loses
     * focus.  This is the best indicator of whether this activity is visible
     * to the user.  The default implementation clears the key tracking
     * state, so should always be called.
     *
     * <p>Note that this provides information about global focus state, which
     * is managed independently of activity lifecycles.  As such, while focus
     * changes will generally have some relation to lifecycle changes (an
     * activity that is stopped will not generally get window focus), you
     * should not rely on any particular order between the callbacks here and
     * those in the other lifecycle methods such as {@link #onResume}.
     *
     * <p>As a general rule, however, a resumed activity will have window
     * focus...  unless it has displayed other dialogs or popups that take
     * input focus, in which case the activity itself will not have focus
     * when the other windows have it.  Likewise, the system may display
     * system-level windows (such as the status bar notification panel or
     * a system alert) which will temporarily take window input focus without
     * pausing the foreground activity.
     *
     * @param hasFocus Whether the window of this activity has focus.
     *
     * @see #hasWindowFocus()
     * @see #onResume
     * @see View#onWindowFocusChanged(boolean)
     */
    public void onWindowFocusChanged(boolean hasFocus) {
    }

onWindowFocusChanged这个方法的含义是,view已经初始化完毕了,宽高已经准备好了,这时候去测量宽高是没有问题的。需要注意的是,onWindowFocusChanged会被调用很多次,当Activity的窗口得到焦点和失去焦点的时候均会被调用一次,具体来说,Activity继续执行和暂停执行的时候,onWindowFocusChanged均会被调用。如果频繁的onResume和onPause,那么onWindowFocusChanged也会被频繁调用

    override fun onWindowFocusChanged(hasFocus: Boolean) {
        super.onWindowFocusChanged(hasFocus)
        if (hasFocus) {
            val measuredWidth = tv_standard.measuredWidth
            val measuredHeight = tv_standard.measuredHeight
            Log.i("MainActivity", "windowFocusChanged===>width:$measuredWidth,height:$measuredHeight")

        }
    }

view.post(runnable)

通过post可以将一个runnable投递到消息队列的尾部,然后等待Looper调用此runnable的时候,view已经初始化好了

    override fun onStart() {
        super.onStart()
        tv_standard.post {
            val measuredWidth = tv_standard.measuredWidth
            val measuredHeight = tv_standard.measuredHeight
            Log.i("MainActivity", "post===>width:$measuredWidth,height:$measuredHeight")
        }
    }

ViewTreeObserver

使用ViewTreeObserver的众多回调可以完成这个功能,比如使用OnGlobalLayoutListener这个接口,当View树的状态发送改变或者View树内部的View可见性改变时,onGlobalLayout方法将会被回调,因此这是获取View的宽高一个很好的时机。需要注意的,伴随着View树的状态改变,onGlobalLayout会被调用多次。

     val viewTreeObserver = tv_standard.viewTreeObserver
        viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                tv_standard.viewTreeObserver.removeOnGlobalLayoutListener(this)
                val measuredWidth = tv_standard.measuredWidth
                val measuredHeight = tv_standard.measuredHeight
                printD("viewTreeObserver===>width:$measuredWidth,height:$measuredHeight")
            }
        })

view.measure(int widthMeasureSpec,int heightMeasureSpec)

通过手动对View进行测量,得到view的宽高。这种方法比较复杂,根据view的LayoutParams来区分

  • match_parent
    直接放弃,无法measure出具体的宽高,原因很简单,view的测量过程,构造此种MeasureSpec需要知道parentSzie,即父容器的剩余空间,二这个号死后无法知道parentSize的大小,所以不能测量出view的大小
  val widthSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY)
  val heightSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY)
  tvStandard.measure(widthSpec, heightSpec)
  • wrap_content
  val widthSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, View.MeasureSpec.EXACTLY)
  val heightSpec = View.MeasureSpec.makeMeasureSpec((1<<30)-1, View.MeasureSpec.EXACTLY)
  tvStandard.measure(widthSpec, heightSpec)

可以看到(1<<30)-1 ,通过分析Measure的实现可以知道,View的尺寸使用30位二进制表示,也就是说最大是30个1(即2^30-1),在最大模式下,用View理论上能支持的最大值去构造MeasureSpec是合理的。

注:本文章知识点来自学习《Android开发艺术探索》一书

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值