起因
故事得从一个小功能说起。最近的一个需求要做一个长按复制标题的功能。这本来是一个很简单的需求,直接给textView设置
android:textIsSelectable="true"
即可。可是它却是要儿子送兄弟。
View popView = LayoutInflater.from(mContext).inflate(R.layout.copy_background, null, false);
mCopyPopupWindow = new PopupWindow(popView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
mCopyPopupWindow.setOutsideTouchable(true);
mCopyPopupWindow.setBackgroundDrawable(new BitmapDrawable());
popView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SystemUtil.copyToClipboard(data);
mCopyPopupWindow.dismiss();
}
});
mCopyPopupWindow.showAsDropDown(view, view.getWidth() / 2 - popView.getWindth()/2, -popView.getWidth()- view.getHeight());
使其如系统那么显示即可。嗯,so easy。当我把代码写完跑一遍的时候,突然发现显示的位置居然错了。如图所示
怎么可能。于是只能打断点找原因。发现getWindth显示的是个0。好吧问题就出在这,于是,找了一下google帮忙。知道问题的原因就是此时我们的popupWindow还未经历过测量布局绘制。所以你去取它的宽当然为0。同时我也获得了解决方法。把getWidth换成getMeasureWidth,同时在showAsDropDown之前加上popView.measure(0,0);
试了一下。容我大笑三声。哈!哈!哈!。那么问题又来了,把getMeasureWidth换成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;
}
/**
* 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;
}
/**
* Width as measured during measure pass.
* {@hide}
*/
@ViewDebug.ExportedProperty(category = "measurement")
int mMeasuredWidth;
从这两端代码的注解以及注释中我们能够知道。getMeasureWidth()获得的宽是对应于View经历了measure过程后存在的,而getWidth()获得的宽是需要View经历了layout过程才会存在。而我们在getMeasureWidth()方法之前调用的Measure(0,0)方法即是让view去经历measure过程。到此,这两个方法的不同有了大致的了解。
结果
知道了这些那我们有什么用呢?我想了一下,平时遇到的一个难题貌似有解了。就是在Activity的onCreate,onStart等方法中如何去获得某个view的长宽,相信聪明的你应该也知道怎么玩了吧。
废话有点多,各位看官轻喷。有啥不对的多多指教