1、View的getWidth()和getMeasuredWidth()有什么区别吗?
View 的宽高是由 View 本身和 parent 容器共同决定的。
getMeasuredWidth() 与 getWidth() 分别对应于视图绘制 的 measure 与 layout 阶段。
getMeasuredWidth()(int widthMeasureSpec, int heightMeasureSpec)方法里面获得的,而getWidth()是在onLayout()方法中获得的。大部分情况下他们是相等,也有一些不相等的情况。比如在父布局的onLayout()方法或者此View的onDraw()方法里调用measure(0,0);(measure中的参数的值你自己可以定义)的话,两者的结果可能会不同。
getWidth()源码:
/**
* Return the width of the your view.
* @return The width of your view, in pixels.
*/
@ViewDebug.ExportedProperty(category = "layout")
public final int getWidth() {
return mRight - mLeft;
}
从源码可以看出getWidth()返回的是右边坐标减轻坐标减去左边坐标,这要在布局之后才能确定它们的坐标,也就是说在布局后onLayout()方法里面才能调用getWidth()来获取。所以getWidth()获得的宽度是View在设定好布局后整个View的宽度。
getMeasuredWidth()源码:
/**
* Like {@link #getMeasuredWidthAndState()}, but only returns the
* raw width component (that is the result is masked by
* {@link #MEASURED_SIZE_MASK}).
* @return The raw measured width of this view.
*/
public final int getMeasuredWidth() {
return mMeasuredWidth & MEASURED_SIZE_MASK;
}
从源码可以看出getMeasuredWidth()返回的是此视图的原始测量宽度。所以说getMeasuredWidth()是对View上的内容进行测量后得到的View内容占据的宽度。
一般情况下,getMeasuredWidth()和getWidth()获得的结果是一样的,但是如果在父布局的onLayout()方法或者此View的onDraw()方法里调用measure(0,0);(measure中的参数的值你自己可以定义)的话,两者的结果可能会不同。
例如:
public class CustomView extends android.support.v7.widget.AppCompatTextView {
private String TAG = CustomView.class.getSimpleName();
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left + 10, top, right + 10, bottom + 10);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
measure(0, 0);
Log.d(TAG, "width=" + getWidth());
Log.d(TAG, "height=" + getHeight());
Log.d(TAG, "MeasuredWidth=" + getMeasuredWidth());
Log.d(TAG, "MeasuredHeight=" + getMeasuredHeight());
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
android:layout_height="match_parent"
tools:context="com.shzx.viewdemo.MainActivity">
<com.shzx.viewdemo.CustomView
android:id="@+id/customView"
android:layout_width="100dp"
android:text="ceshi"
android:layout_height="100dp" />
</LinearLayout>
此时的打印结果是
12-16 07:13:40.050 8901-8901/com.shzx.viewdemo D/CustomView: width=350
12-16 07:13:40.050 8901-8901/com.shzx.viewdemo D/CustomView: height=350
12-16 07:13:40.050 8901-8901/com.shzx.viewdemo D/CustomView: MeasuredWidth=116
12-16 07:13:40.050 8901-8901/com.shzx.viewdemo D/CustomView: MeasuredHeight=66
此时getWidth()和getMeasuredWidth()不相等
因此可以得出 getWidth(): View在设定好布局后整个View的宽度。getMeasuredWidth(): 对View上的内容进行测量后得到的View内容佔据的宽度
2、如何在onCreate中拿到View的宽度和高度?
在onCreate()中获取View的宽高有三种方法:
获取view的宽高
private void getWidthHeight() {
int width = customView.getWidth();
int height = customView.getHeight();
LogUtils.e("width =" + width);
LogUtils.e("height =" + height);
int measuredWidth = customView.getMeasuredWidth();
int measuredHeight = customView.getMeasuredHeight();
LogUtils.e("measuredWidth =" + measuredWidth);
LogUtils.e("measuredHeight =" + measuredHeight);
}
1)、View.post(runnable)
利用 Handler 通信机制,发送一个 Runnable 到 message queue 中,当 view layout 处理完成时,自动发送消息,通知 UI 线程。借此机制,巧妙获取 View 的宽高属性。代码简洁,使用简单,相比 ViewTreeObserver 监听处理,还不需要手动移除观察者监听事件。
customView.post(new Runnable() {
@Override
public void run() {
getWidthHeight();
}
});
打印结果:
12-16 02:33:49.938 4238-4238/com.shzx.viewdemo E/MainActivity: width =116>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity$1,methodName=run,lineNumber=23 ]
12-16 02:33:49.941 4238-4238/com.shzx.viewdemo E/MainActivity: height =66>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity$1,methodName=run,lineNumber=24 ]
12-16 02:33:49.941 4238-4238/com.shzx.viewdemo E/MainActivity: measuredWidth =116>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity$1,methodName=run,lineNumber=28 ]
12-16 02:33:49.946 4238-4238/com.shzx.viewdemo E/MainActivity: measuredHeight =66>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity$1,methodName=run,lineNumber=29 ]
2)、ViewTreeObserver.addOnGlobalLayoutListener(OnGlobalLayoutListener listener)
监听 View 的 onLayout() 绘制过程,一旦 layout 触发变化,立即回调 onLayoutChange 方法。
注意,使用完也要注意调用 removeOnGlobalLayoutListener() 方法移除监听事件。避免后续每一次发生全局 View 变化均触发该事件,影响性能。
ViewTreeObserver observer = customView.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
getWidthHeight();
customView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
打印结果:
12-16 03:34:06.981 6116-6116/com.shzx.viewdemo E/MainActivity: width =116>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=getWidthHeight,lineNumber=61 ]
12-16 03:34:06.982 6116-6116/com.shzx.viewdemo E/MainActivity: height =66>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=getWidthHeight,lineNumber=62 ]
12-16 03:34:06.983 6116-6116/com.shzx.viewdemo E/MainActivity: measuredWidth =116>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=getWidthHeight,lineNumber=66 ]
12-16 03:34:06.983 6116-6116/com.shzx.viewdemo E/MainActivity: measuredHeight =66>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=getWidthHeight,lineNumber=67 ]
3)、View.measure(int widthMeasureSpec,int heightMeasureSpec)
View.MeasureSpec.makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size, @MeasureSpecMode int mode)
其中 MeasureSpec.MODE_SHIFT = 30
size最大值为 (1<<30)-1, 即用30位二进制表示,也就是说最大是30个1 (即2^30-1)。
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(200, View.MeasureSpec.EXACTLY);
int heigthMeasureSpec = View.MeasureSpec.makeMeasureSpec(200, View.MeasureSpec.EXACTLY);
customView.measure(widthMeasureSpec,heigthMeasureSpec);
int width = customView.getWidth();
int height = customView.getHeight();
LogUtils.e("width =" + width);
LogUtils.e("height =" + height);
int measuredWidth = customView.getMeasuredWidth();
int measuredHeight = customView.getMeasuredHeight();
LogUtils.e("measuredWidth =" + measuredWidth);
LogUtils.e("measuredHeight =" + measuredHeight);
打印结果:
12-16 03:04:23.278 5444-5444/com.shzx.viewdemo E/MainActivity: width =0>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=onCreate,lineNumber=42 ]
12-16 03:04:23.278 5444-5444/com.shzx.viewdemo E/MainActivity: height =0>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=onCreate,lineNumber=43 ]
12-16 03:04:23.278 5444-5444/com.shzx.viewdemo E/MainActivity: measuredWidth =200>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=onCreate,lineNumber=47 ]
12-16 03:04:23.278 5444-5444/com.shzx.viewdemo E/MainActivity: measuredHeight =200>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=onCreate,lineNumber=48 ]
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST);
int heigthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST);
customView.measure(widthMeasureSpec,heigthMeasureSpec);
int width = customView.getWidth();
int height = customView.getHeight();
LogUtils.e("width =" + width);
LogUtils.e("height =" + height);
int measuredWidth = customView.getMeasuredWidth();
int measuredHeight = customView.getMeasuredHeight();
LogUtils.e("measuredWidth =" + measuredWidth);
LogUtils.e("measuredHeight =" + measuredHeight);
打印结果:
12-16 03:03:28.394 5344-5344/com.shzx.viewdemo E/MainActivity: width =0>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=onCreate,lineNumber=42 ]
12-16 03:03:28.395 5344-5344/com.shzx.viewdemo E/MainActivity: height =0>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=onCreate,lineNumber=43 ]
12-16 03:03:28.395 5344-5344/com.shzx.viewdemo E/MainActivity: measuredWidth =116>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=onCreate,lineNumber=47 ]
12-16 03:03:28.395 5344-5344/com.shzx.viewdemo E/MainActivity: measuredHeight =66>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=onCreate,lineNumber=48 ]
除了在onCreate()中获得View的宽高,还可以在Activity的onWindowFocusChanged(boolean hasFocus)方法中获取宽高
源码:
/**
* 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) {
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
getWidthHeight();
}
打印结果:
12-16 03:46:36.929 6425-6425/com.shzx.viewdemo E/MainActivity: width =116>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=getWidthHeight,lineNumber=58 ]
12-16 03:46:36.929 6425-6425/com.shzx.viewdemo E/MainActivity: height =66>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=getWidthHeight,lineNumber=59 ]
12-16 03:46:36.929 6425-6425/com.shzx.viewdemo E/MainActivity: measuredWidth =116>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=getWidthHeight,lineNumber=63 ]
12-16 03:46:36.929 6425-6425/com.shzx.viewdemo E/MainActivity: measuredHeight =66>>>[ threadID=1,threadName=main,fileName=MainActivity.java,className=com.shzx.viewdemo.MainActivity,methodName=getWidthHeight,lineNumber=64 ]