美文网首页
Android(Java/Kotlin)在任何地方Showing

Android(Java/Kotlin)在任何地方Showing

作者: 想看烟花么 | 来源:发表于2022-07-02 00:29 被阅读0次

    最近做一个项目,项目中好几处用到了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-----------------------------

    我也是有底线的,感谢您的耐心阅读,欢迎支持与点赞。

    相关文章

      网友评论

          本文标题:Android(Java/Kotlin)在任何地方Showing

          本文链接:https://www.haomeiwen.com/subject/lmuhbrtx.html