快捷搜索: 王者荣耀 脱发

Activity中何时能拿到组件的宽高

在日常Android开发中,你什么时候才能在Activity中准确拿到组件View的宽高呢?大部分情况下我们在onCreate()中就已经findViewById()了,那是不是只要在这之后就能准确拿到组件的宽高呢?

我们写代码跑一下试试,我们分别在onCreate()结束时,onStart()开始和结束时,onResume()开始和结束时去尝试获取View的width和measuredWidth,看看到底能不能拿到宽高。

由于页面布局就是个约束布局里面放着一个按钮,因此布局文件不贴了。

package com.openld.seniorstructure.testobtainviewwidth

import android.os.Bundle
import android.util.Log
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import com.openld.seniorstructure.R

class TestObtainViewWidthActivity : AppCompatActivity() {
    private lateinit var mBtn: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test_obtain_view_width)

        initWidgets()

        Log.d(
            ">>>>>>",
            "onCreate end: btn.width = ${mBtn.width}  btn.measuredWidth = ${mBtn.measuredWidth}"
        )
    }

    private fun initWidgets() {
        mBtn = findViewById(R.id.btn)
    }

    override fun onStart() {
        Log.d(
            ">>>>>>",
            "onStart start: btn.width = ${mBtn.width}  btn.measuredWidth = ${mBtn.measuredWidth}"
        )

        super.onStart()

        Log.d(
            ">>>>>>",
            "onStart end: btn.width = ${mBtn.width}  btn.measuredWidth = ${mBtn.measuredWidth}"
        )
    }

    override fun onResume() {
        Log.d(
            ">>>>>>",
            "onResume start: btn.width = ${mBtn.width}  btn.measuredWidth = ${mBtn.measuredWidth}"
        )

        super.onResume()

        Log.d(
            ">>>>>>",
            "onResume end: btn.width = ${mBtn.width}  btn.measuredWidth = ${mBtn.measuredWidth}"
        )
    }
}

运行一下页面看下日志如下:

啪啪打脸,可以毫无疑问地说,即便是在onResume()结束的时候,你直接通过view.getWidth()或者view.getMeasuredWidth()也是拿不到实际的组件宽高的。

那怎么在Activity中去拿到组件的宽高呢?这里有两个思路,一个是在onCreate()中调用view.post()去拿。另一个是在onWindowFocusChanged()回调中去拿。

我们先试一下post方式。

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test_obtain_view_width)

        initWidgets()

        mBtn.post {
            Log.d(
                ">>>>>>",
                "onCreate 中调用mBtn.post  btn.width = ${mBtn.width}  btn.measuredWidth = ${mBtn.measuredWidth}"
            )
        }

        Log.d(
            ">>>>>>",
            "onCreate end: btn.width = ${mBtn.width}  btn.measuredWidth = ${mBtn.measuredWidth}"
        )
    }

可以看到正确拿到了。断点看了下会走到postOnAniumation()方法,这个方法注释很关键。

public static void postOnAnimation(@NonNull View view, @NonNull Runnable action) {
        if (Build.VERSION.SDK_INT >= 16) {
            Api16Impl.postOnAnimation(view, action);
        } else {
            view.postDelayed(action, ValueAnimator.getFrameDelay());
        }
    }

Causes the Runnable to execute on the next animation time step. The runnable will be run on the user interface thread. This method can be invoked from outside of the UI thread only when this View is attached to a window.

意思是说只有View被真正附加到了window上才会执行,那拿到宽高就不足为奇了。

我们再试一下onWindowFocusChanged()。

可以看到同样能够正确拿到组件宽高。 可以看下方法注释,其实就是页面真正获取或失去了焦点会回调这个方法,那获取焦点的时候拿宽高当然是拿得到的。

经验分享 程序员 微信小程序 职场和发展