1 获取View的宽高
简单布局,获取宽高:
<LinearLayout
android:id="@+id/view_group_demo"
android:layout_width="match_parent"
android:layout_height="100dp"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="sjldjflsjfljs"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="sjldjflsjfljs"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="sjldjflsjfljs"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="sjldjflsjfljs"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="sjldjflsjfljs"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="sjldjflsjfljs"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="sjldjflsjfljs"/>
</LinearLayout>
Activity文件:
public class Main15Activity2 extends AppCompatActivity {
private LinearLayout mGroupDemo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main15);
mGroupDemo = findViewById(R.id.view_group_demo);
System.out.println("=============onCreate================="+mGroupDemo.getWidth()+" "+mGroupDemo.getHeight());
System.out.println("=============onCreate================="+mGroupDemo.getMeasuredWidth()+" "+mGroupDemo.getMeasuredHeight());
}
@Override
protected void onStart() {
super.onStart();
System.out.println("=============onStart================="+mGroupDemo.getWidth()+" "+mGroupDemo.getHeight());
System.out.println("=============onStart================="+mGroupDemo.getMeasuredWidth()+" "+mGroupDemo.getMeasuredHeight());
}
@Override
protected void onResume() {
super.onResume();
System.out.println("=============onResume================="+mGroupDemo.getWidth()+" "+mGroupDemo.getHeight());
System.out.println("=============onResume================="+mGroupDemo.getMeasuredWidth()+" "+mGroupDemo.getMeasuredHeight());
}
}
结果:
=onCreate=0 0
=onCreate=0 0
=onStart=0 0
=onStart=0 0
=onResume=0 0
=onResume=0 0
发现所有的结果都是0,如何获取呢?
可以获取的方法举例:
//这个是网上找到的方法,但是发现有时黑屏情况下获取到的值为0
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus){
System.out.println("=============onWindowFocusChanged================="+mGroupDemo.getWidth()+" "+mGroupDemo.getHeight());
System.out.println("=============onWindowFocusChanged================="+mGroupDemo.getMeasuredWidth()+" "+mGroupDemo.getMeasuredHeight());
}
}
ViewTreeObserver observer = mGroupDemo.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//一定要remove
mGroupDemo.getViewTreeObserver().removeOnGlobalLayoutListener(this);
System.out.println("=============onGlobalLayout================="+mGroupDemo.getWidth()+" "+mGroupDemo.getHeight());
System.out.println("=============onGlobalLayout================="+mGroupDemo.getMeasuredWidth()+" "+mGroupDemo.getMeasuredHeight());
}
});
结果:
=onGlobalLayout=720 200
=onGlobalLayout=720 200
=onWindowFocusChanged=720 200
=onWindowFocusChanged=720 200
2 说明getMeasuredHeight和getHeight的值的区别
通过上面的结果可以发现,getMeasuredHeight和getHeight的值是相同的,getMeasureWidth和getWidth的值是相同的,那么这两个值到底有什么区别呢?下面只讨论高度问题。
首先可以得出的结论是LinearLayout内部的内容超过了LinearLayout的高度,所以没有显示全内部内容,所以getMeasuredHeight和getHeight的值和内部关系不大(有关系也是需要我们去添加)。
public class LinearLayoutDemo extends LinearLayout {
public LinearLayoutDemo(Context context) {
this(context,null);
}
public LinearLayoutDemo(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public LinearLayoutDemo(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int childCount = getChildCount();
int actHeight = 0;
for (int i=0;i< childCount;i++){
View view = getChildAt(i);
MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams();
measureChild(view,widthMeasureSpec,heightMeasureSpec);
actHeight += view.getMeasuredHeight() + 50;
}
setMeasuredDimension(700,actHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
View view = getChildAt(0);
view.layout(l,0,r,100);
System.out.println("===========onLayout==============="+getMeasuredHeight());
System.out.println("===========onLayout==============="+getHeight());
}
}
布局文件
<com.ldx.canvasdrawdemo.LinearLayoutDemo
android:id="@+id/view_group_demo"
android:layout_width="match_parent"
android:layout_height="100dp"
android:orientation="vertical">
<TextView
android:id="@+id/demo_tv1"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="sjldjflsjfljs"/>
</com.ldx.canvasdrawdemo.LinearLayoutDemo>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main15);
mGroupDemo = findViewById(R.id.view_group_demo);
textView1 = findViewById(R.id.demo_tv1);
ViewTreeObserver observer = mGroupDemo.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//一定要remove
mGroupDemo.getViewTreeObserver().removeOnGlobalLayoutListener(this);
System.out.println("=============onGlobalLayout================="+textView1.getWidth()+" "+textView1.getHeight());
System.out.println("=============onGlobalLayout================="+textView1.getMeasuredWidth()+" "+textView1.getMeasuredHeight());
}
});
}
结果:
=onGlobalLayout=720 100
=onGlobalLayout=720 100
修改:
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
View view = getChildAt(0);
view.layout(l,0,r,150);
System.out.println("===========onLayout==============="+getMeasuredHeight());
System.out.println("===========onLayout==============="+getHeight());
}
结果:
=onGlobalLayout=720 150
=onGlobalLayout=720 100
说明:
- getMeasuredHeight,getMeasureWidth在view测量后确定( setMeasuredDimension()后),所以又被称为测量宽高,它们只有在measure函数执行后才会有值(或者主动调用measure,measureChild),在layout方法中会用到。
- getHeight,getWidth在layout函数执行之后有值,会在draw方法中使用。
- onLayout方法中可以使用getMeasuredWidth方法,如果要在onMeasure中使用getMeasuredHeight方法,只需要调用view的measure或者measureChild方法,之后就可以调用方法获取。
- 一般情况下,getHeight和getMeasureHeight得到的值是一致的,但是layout如果不按照measure测量得到的值进行设置,也可能两者不相同。
- getMeasureHeight是自己测量得到的值,告诉父布局我想要多少值,父布局分配宽高时会参考这个值,但具体分配多少由父布局定;getHeight是父布局告诉我值为多少值就是多少,都不是View的真实高度。
getMeasureHeight的值会根据布局文件中的模式和指定的高度进行确定,也可以直接复写onMeasure指定高度。调用 setMeasuredDimension(700,100);就可以完成对高度的指定,布局文件中的模式和高度值也就没有意义了。
源码分析
getMeasureHeight,最终和setMeasuredDimension设置的值有关
/*
* @return The raw measured height of this view.
* getMeasuredHeight,利用了mMeasuredHeight & MEASURED_SIZE_MASK两个值
*/
public final int getMeasuredHeight() {
return mMeasuredHeight & MEASURED_SIZE_MASK;
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
Insets insets = getOpticalInsets();
int opticalWidth = insets.left + insets.right;
int opticalHeight = insets.top + insets.bottom;
measuredWidth += optical ? opticalWidth : -opticalWidth;
measuredHeight += optical ? opticalHeight : -opticalHeight;
}
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
getHeight和父布局有关:
public final int getHeight() {
return mBottom - mTop;
}
/**
* Assign a size and position to a view and all of its
* descendants
*
* <p>This is the second phase of the layout mechanism.
* (The first is measuring). In this phase, each parent calls
* layout on all of its children to position them.
* This is typically done using the child measurements
* that were stored in the measure pass().</p>
*
* <p>Derived classes should not override this method.
* Derived classes with children should override
* onLayout. In that method, they should
* call layout on each of their children.</p>
* //仔细看四个参数的解释,都是相对父布局的,所以getHeight,getWidth都跟父布局有关系(默认情况下)。
* @param l Left position, relative to parent
* @param t Top position, relative to parent
* @param r Right position, relative to parent
* @param b Bottom position, relative to parent
*/
@SuppressWarnings({"unchecked"})
public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
//关键代码
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
。。。。。。。。。。。
}