最近做一个项目,项目中好几处用到了popwindow,于是乎想简单的封装下popwindow,最初想着用MeasureSpec来获取暂未显示的view的宽高:
public static void showPopwindow(View anchor){
.....
binding.root.measure(makeDropDownMeasureSpec(AppUtils.dip2px(context, 254f)),
makeDropDownMeasureSpec(ViewGroup.LayoutParams.WRAP_CONTENT))
val offsetY: Int =
binding.root.measuredHeight + anchor.height
Log.d(TAG,
"xyInfo==>" + offsetY + "|" + binding.root.measuredHeight + "|" + anchor.height)
mPopWindow.showAsDropDown(anchor, 0, -offsetY, Gravity.END)
}
private fun makeDropDownMeasureSpec(measureSpec: Int): Int {
val mode: Int = if (measureSpec == ViewGroup.LayoutParams.WRAP_CONTENT) {
View.MeasureSpec.AT_MOST
} else {
View.MeasureSpec.EXACTLY
}
return View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(measureSpec), mode)
}
}
但如上方式,在手机上实际显示的效果发现位置不对,offsetY的值偏小了,日志打印出来不是预期的高度,想到popwindow的contentView都是在使用时才通过Layoutinflater初始化的可能测量无法精准,于是乎想到了另一种实现:
采用addOnGlobalLayoutListener来精确/准确获取View的大小(因为这时view已经初始化),果然完美解决,现在只需要简单就能实现在你想要的任何地方显示popwindow了。
public static void showPopwindow(View anchor){
......
binding.getRoot().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
binding.getRoot().getViewTreeObserver().removeOnGlobalLayoutListener(this);
binding.getRoot().setVisibility(View.VISIBLE);
int mWidth = binding.getRoot().getWidth();
int mHeight = binding.getRoot().getHeight();
Log.d("mPopWindowLocation", "xyInfo==>" + mPopWindow.isShowing() + "|" + binding.getRoot().getHeight() + "|" + anchor.getHeight());
if (mPopWindow.isShowing()) {
mPopWindow.update(anchor, 0, -(mHeight + anchor.getHeight()), mWidth, mHeight);
}
}
});
binding.getRoot().setVisibility(View.INVISIBLE);
mPopWindow.showAsDropDown(anchor, 0, 0, Gravity.END);
}
传统的Layoutinflater方式下上面的方式应该已经是正常工作了,但在用databinding的Layoutinflater时,其实应为databinding的机制问题,View中的getWidth 或者getHeight方法其实返回的依旧是之前的数据,databing过后的margin啊,padding啊并没有更新,所以测出来的数据并不对,应此在用databingding的方式下应该对如上实现方式做调整:
private fun showPopwindow(anchor: View) {
val binding: DialogAttachmentLayoutBinding =
DataBindingUtil.inflate(LayoutInflater.from(context),
R.layout.dialog_attachment_layout,
null,
false)
binding.theme = SDKProviderDelegate.getTheme().value
val mPopWindow = PopupWindow(binding.root,
AppUtils.dip2px(context, 254f),
ViewGroup.LayoutParams.WRAP_CONTENT, true)
//new implementation:
binding.root.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
binding.root.viewTreeObserver.removeOnGlobalLayoutListener(this)
binding.root.visibility = View.VISIBLE
var mHeight = 0
val viewParent = (binding.root as ViewGroup)
for (i in 0 until viewParent.childCount) {
val child: View = viewParent.getChildAt(i)
val lp: ViewGroup.LayoutParams = child.layoutParams
val widthMeasureMode = if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) MeasureSpec.AT_MOST else MeasureSpec.EXACTLY
val heightMeasureMode = if (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) MeasureSpec.AT_MOST else MeasureSpec.EXACTLY
val widthMeasure = MeasureSpec.makeMeasureSpec(binding.root.width, widthMeasureMode)
val heightMeasure = MeasureSpec.makeMeasureSpec(binding.root.height, heightMeasureMode)
child.measure(widthMeasure, heightMeasure)
mHeight += child.measuredHeight
}
mHeight = (mHeight + binding.root.paddingTop + binding.root.paddingBottom)
Log.d("mPopWindowLocation", "xyInfo==>" + mPopWindow.isShowing + "|" + binding.root.height + "|" + mHeight)
if (mPopWindow.isShowing) {
mPopWindow.update(anchor, 0, -(anchor.top + anchor.height + mHeight), -1, -1)
}
}
})
binding.root.visibility = View.INVISIBLE
mPopWindow.showAsDropDown(anchor, 0, 0, Gravity.END)
}
现在工作良好,所以看自己的项目决定采用哪种解决方法吧。
-----------------------------End-----------------------------
网友评论