前言
在介绍正确获取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()方式。至于其他的三种方式,了解即可。