在代码中正确获取View宽高的四种方法

前言

在介绍正确获取View宽高的方法之前,我们先看一下,如果直接在代码中用View.getWidth() / View.getHeight()看看能不能获取到View的宽高。其实不用写demo,你应该也能猜到,如果这种方式都可以,那我就没有必要在向你介绍正确的方法了。但是我们要抱着学习的心态(自己掉过的坑才是自己的)来掌握方法。

少废话,直接上代码!

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout 
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.chrissen.ysts.MainActivity">

        <TextView
            android:id="@+id/text_view"
            android:layout_width="100dp"
            android:layout_height="50dp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            android:background="@color/colorPrimary"
            android:text="Hello"
            android:gravity="center"
            android:textColor="@android:color/white"/>

    </android.support.constraint.ConstraintLayout>

我相信上面的代码,你应该能看懂:)

我们在看看代码中的。

    public class MainActivity extends AppCompatActivity {

        private static final String TAG = "MainActivity";

        private TextView mTextView;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView = findViewById(R.id.text_view);
            getViewWidthAndHeight();
        }

        /**
         * 获取View的宽高
         */
        private void getViewWidthAndHeight(){
            int width = mTextView.getWidth();
            int height = mTextView.getHeight();
            Log.i(TAG, "TextView Height is " + height + ", Width is " + width);
        }

    }

上面的代码也很简单,就是直接在onCreate()方法中调用了View的getWidth()/getHeight()方法来获取View的宽高。

那我们来看一下打印的日志信息。

正如我们所料,没有正确的获取到TextView的宽高。

可能有的读者朋友会说了,是不是调用的时机不对。行,那我们把获取宽高的方法写在onResume()方法中。

    public class MainActivity extends AppCompatActivity {

        private static final String TAG = "MainActivity";

        private TextView mTextView;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView = findViewById(R.id.text_view);
    //        getViewWidthAndHeight();
        }

        @Override
        protected void onResume() {
            super.onResume();
            getViewWidthAndHeight();
        }

        /**
         * 获取View的宽高
         */
        private void getViewWidthAndHeight(){
            int width = mTextView.getWidth();
            int height = mTextView.getHeight();
            Log.i(TAG, "TextView Height is " + height + ", Width is " + width);
        }

    }

我们再看一下打印的日志信息。

你没有看错,和之前的一模一样。

那为什么在生命周期里无法直接获取到View的宽高呢?这里我只能给你一个简单的结论:View的绘制(如果读者朋友了解过自定义View的话,View的宽高其实是在measure时确定的)和Activity的生命周期调用并没有直接的关系。换句话说就是:Activity的onCreate()并调用并不意味着View的measure,layout,draw流程都被调用了。如果读者朋友想深入的研究学习,建议你自行Google一下,网上其实已经有很多文章了。在这里我们就不做过多的分析了。

好,介绍完以上的内容,那我们有必要来介绍一下正确获取View宽高的方法了,正如标题所言,想要获取到View的宽高其实我们有四种方法。听着是不是很炫技?但是有的读者朋友会说:我不想掌握那么多方法,我只想知道最简洁最好记的方法就行了。行,为了满足这部分的需求,笔者先介绍一种最简洁也是最好记得一种方式。

View.post()

我在开发中最常用的一种方式就是使用view.post()方式。

    public class MainActivity extends AppCompatActivity {

        private static final String TAG = "MainActivity";

        private TextView mTextView;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView = findViewById(R.id.text_view);
            //使用view.post()方式
            mTextView.post(new Runnable() {
                @Override
                public void run() {
                    getViewWidthAndHeight();
                }
            });
        }



        /**
         * 获取View的宽高
         */
        private void getViewWidthAndHeight(){
            int width = mTextView.getWidth();
            int height = mTextView.getHeight();
            Log.i(TAG, "TextView Height is " + height + ", Width is " + width);
        }

    }

我们来看一下log日志。

下面是见证奇迹的时刻!

很显然我们获取到了View的宽高。有的人可能会说:“唉~~不对啊,xml文件里面写的是100和50啊,你这个打印出来的是150,300啊?“。额…单位不一样。

我自己在开发中最常用的就是这种方式,至于原理,还要请读者朋友自行学习研究。

至于其他的三种方法,掌握最好,但最起码我们要了解一下。

onWindowFocusChanged()

先上代码。

    public class MainActivity extends AppCompatActivity {

        private static final String TAG = "MainActivity";

        private TextView mTextView;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView = findViewById(R.id.text_view);
        }


        /**
         * Called when the current Window of the activity gains or loses
         * focus. 
         * @param hasFocus Whether the window of this activity has focus.
         */
        @Override
        public void onWindowFocusChanged(boolean hasFocus) {
            super.onWindowFocusChanged(hasFocus);
            if (hasFocus) {
                getViewWidthAndHeight();
            }
        }

        /**
         * 获取View的宽高
         */
        private void getViewWidthAndHeight(){
            int width = mTextView.getWidth();
            int height = mTextView.getHeight();
            Log.i(TAG, "TextView Height is " + height + ", Width is " + width);
        }

    }

这次我们是在onWindowFoucsChanged()回调方法中进行调用。注释已经写的很清楚了(当前Activity的Window的焦点改变时调用该方法)。之所以我们能在该方法中获取到View的宽高是因为这个方法调用之前View已经初始化完毕了。

log日志我就不贴了,和view.post()的结果一样。但是这个方法有一个不足之处是这个方法会被频繁的调用,正如其方法名,只要window的焦点改变了,那该方法就会被调用,所以我个人不太建议在这个方法里获取View的宽高。

OnGlobalLayoutListener

代码

    public class MainActivity extends AppCompatActivity {

        private static final String TAG = "MainActivity";

        private TextView mTextView;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView = findViewById(R.id.text_view);
            mTextView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    mTextView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    getViewWidthAndHeight();
                }
            });
        }

        /**
         * 获取View的宽高
         */
        private void getViewWidthAndHeight(){
            int width = mTextView.getWidth();
            int height = mTextView.getHeight();
            Log.i(TAG, "TextView Height is " + height + ", Width is " + width);
        }

    }

首先通过getViewTreeObserver()获取到ViewTreeObserver实例,然后调用addOnGlobalLayoutListener()添加监听。这个监听会在全局layout状态和在该view树里的view的visibility的改变时进行回调。很显然这个获取View宽高的一个很好的时机。但是这个方法也会被调用多次,所以也不太建议在该方法中获取view的宽高。

View.measure()

这种方法的实现方式是手动进行measure来得到view的宽高。这个流程其实和自定义view重写onMeasure()方法类似,需要分情况处理:match_parent,具体的数值,wrap_content;对于第一种,很显然无法测出,第二种就比较简单,直接传入具体的数值进行测量即可;对于第三种我们需要通过(1<<30) - 1的方式得到。

下面给出代码:

  • 具体的数值:
    int widthMeasureSpec = MeasureSpec.makeMeasureSpec(100,MeasureSpec.EXACTLY);
    int heightMeasureSpec = MeasureSpec.makeMeasureSpec(100,MeasureSpec.EXACTLY);
    view.measure(widthMeasureSpec,heightMeasureSpec);
  • wrap_content:
    int widthMeasureSpec = MeasureSpec.makeMeasureSpec((1 << 30) - 1 ,MeasureSpec.AT_MOST);
    int heightMeasureSpec = MeasureSpec.makeMeasureSpec((1 << 30) - 1 ,MeasureSpec.AT_MOST);
    view.measure(widthMeasureSpec,heightMeasureSpec);

这个方法非常不建议,大家了解一下就好。

总结

前面我们总结了正确获取View宽高的四种方法,但其实,在我们的开发中,最常用的其实就是第一种中方式:view.post()方式。至于其他的三种方式,了解即可。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值