一、前言

PopupWindow 实现的功能和 Dialog 基本一样,都是弹出对话框,给用户提示和反馈操作。相比较 Dialog,PopupWindow 没有默认的对话框,两者各有优缺点。PopupWindow 更适合自定义对话框。

二、基本使用

使用起来比较简单,只要实现几个方法就可以弹出一个对话框。

  • 1.实现一个简单弹出对话框,默认无阴影,点击外部不消失

image.png

  1. private fun showSimplePw(center: Int) {
  2. //默认整个对话框窗体没有阴影
  3. //这种初始化的方法,默认点击外部区域对话框可以消失(主要是最后一个参数是 true)
  4. val contentView = layoutInflater.inflate(R.layout.pw_center, null)
  5. val popup = PopupWindow(contentView,ViewGroup.LayoutParams.MATCH_PARENT,
  6. ViewGroup.LayoutParams.WRAP_CONTENT,true)
  7. //第二个参数控制显示的位置,后面两个参数控制对话框偏移量
  8. popup.showAtLocation(this.window.decorView,center,0,0)
  9. contentView.tv_pw_onetitle_cancel.setOnClickListener {
  10. popup.dismiss()
  11. }
  12. }
    1. 实现带阴影和点击外部消失的对话框

image.png

  1. private fun showShawSimplePw() {
  2. //设置阴影
  3. Utils.backgroundAlpha(this,0.5f)
  4. val contentView = layoutInflater.inflate(R.layout.pw_center, null)
  5. val popup = PopupWindow()
  6. popup.contentView = contentView
  7. popup.width = ViewGroup.LayoutParams.MATCH_PARENT
  8. popup.height = ViewGroup.LayoutParams.WRAP_CONTENT
  9. //设置点击空白区域消失的方法
  10. popup. isOutsideTouchable = true
  11. popup.isFocusable = true
  12. popup.setBackgroundDrawable(ColorDrawable())
  13. popup.showAtLocation(this.window.decorView,Gravity.CENTER,0,0)
  14. contentView.tv_pw_onetitle_cancel.setOnClickListener {
  15. popup.dismiss()
  16. }
  17. popup.setOnDismissListener {
  18. //去掉阴影
  19. Utils.backgroundAlpha(this,1f)
  20. }
  21. }

阴影是通过对屏幕设置透明度实现的,实现如下:

  1. /**
  2. * 设置添加屏幕的背景透明度
  3. * @param bgAlpha
  4. */
  5. fun backgroundAlpha(context: Activity,bgAlpha: Float) {
  6. val lp: WindowManager.LayoutParams = context.window.attributes
  7. lp.alpha = bgAlpha //0.0-1.0
  8. context.window.attributes = lp
  9. }
  • 3.实现菜单样式对话框,默认在 View 的下方

image.png

  1. val contentView = layoutInflater.inflate(R.layout.pw_menu, null)
  2. val popup = PopupWindow(contentView,
  3. ViewGroup.LayoutParams.WRAP_CONTENT,
  4. ViewGroup.LayoutParams.WRAP_CONTENT, true)
  5. //对整个 popupwindow 设置背景
  6. popup.showAsDropDown(btn_popup_menu)
    1. 点击对话框外部区域是否消失

如果想实现可以消失,第一种方法是在 PopupWindow 构造方法里设置 true, 示例:
PopupWindow(contentView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true)
最后一个参数设置为 true。如果没有使用这种方式初始化,还有一种方法,如下:
popup. isOutsideTouchable = true
popup.isFocusable = true
popup.setBackgroundDrawable(ColorDrawable())

三、实现阴影的方法

每个弹出对话框我都加了动画效果,PopupWindow 添加动画效果很简单,在 xml 中写好需要的动画文件,在 style 中引入动画,最后在使用 PopupWindow 示例设置动画就可以了,
示例如下:

popup.animationStyle = R.style.AnimFadeCenter

我找了很多方法,相对来说效果比较好的方法如下,Gif 图效果不是很好,真机效果更好。

    1. 通过设置 View 背景的方法

效果图:
iShot2020-08-0910.27.27.gif
这种方法阴影也有渐变动画效果,配合上对话框自身的渐变动画,效果还不错,有一个小的缺点就是在有些机型上,顶部的状态栏无法被阴影覆盖。不过这个影响不是很大。

    1. 通过对整个屏幕设置渐变度

效果图:
iShot2020-08-0910.29.49.gif
设置渐变的方法:

  1. /**
  2. * 设置添加屏幕的背景透明度
  3. * @param bgAlpha
  4. */
  5. fun setScreenAlpha(context: Activity, bgAlpha: Float) {
  6. val lp: WindowManager.LayoutParams = context.window.attributes
  7. lp.alpha = bgAlpha //0.0-1.0
  8. context.window.attributes = lp
  9. }

设置渐变背身是没有动画效果的,可以自己加上动画效果,配合对话框的动画,效果整体上不错,没有状态栏无法覆盖的缺陷。

    1. 通过设置dimAmount

效果图:
iShot2020-08-0910.31.03.gif
这个方法必须放在对话框显示以后设置才会生效。方法如下:

  1. fun setScreenAlpha(context: Context, pp: PopupWindow, dimAmount: Float) {
  2. val decorView: View? = getDecorView(pp)
  3. decorView?.let {
  4. val p = decorView.layoutParams as WindowManager.LayoutParams
  5. p.flags = p.flags or WindowManager.LayoutParams.FLAG_DIM_BEHIND
  6. p.dimAmount = dimAmount
  7. //modifyWindowLayoutParams(p)
  8. (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager)
  9. .updateViewLayout(decorView, p)
  10. }
  11. }
  12. private fun getDecorView(pp: PopupWindow): View? {
  13. var decorView: View? = null
  14. try {
  15. decorView = if (pp.background == null) {
  16. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  17. pp.contentView.parent as View
  18. } else {
  19. pp.contentView
  20. }
  21. } else {
  22. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  23. pp.contentView.parent.parent as View
  24. } else {
  25. pp.contentView.parent as View
  26. }
  27. }
  28. } catch (ignore: Exception) {
  29. }
  30. return decorView
  31. }

demo

参考:
亲,还在为PopupWindow烦恼吗?
Android PopupWindow详解