弹窗在安卓开发中是必不可缺的一部分,通知,余额不足提示,第三方SDK的跳转,一些条件筛选的弹窗等等,在Android中,最常用的一般是Dialog和PopWindow这俩个控件。
自己平时也用得多,今天就把这个基础的东西总结下。
其实这俩个控件有一些实现和展示上的区别,不过也可以通过代码使得展示上大差不差
PopupWindow
为非模态,可以继续操作弹出界面之下的控件;Dialog
为模态,必须先取消Dialog才能操作Dialog之下的控件;两者最根本的区别在于有没有新建一个 Window
,PopupWindow 没有新建,而是通过 WMS 将 View 加到 DecorView;Dialog 是新建了一个 window (PhoneWindow),相当于走了一遍 Activity 中创建 window 的流程。
一、Dialog
1、AlertDialog
AlertDialog是Dialog的子类,所以它包含了Dialog类的很多属性和方法。
他的实现非常简单:
val dialog1: AlertDialog.Builder = AlertDialog.Builder(this);
dialog1.setTitle("提示")
.setMessage("你的金额不足!")
.setNegativeButton("关闭", null)
.create()
.show();
展示为:
具体可以设置其属性的方法:
setTitle :为对话框设置标题
setIcon :为对话框设置图标
setMessage:为对话框设置内容
setView : 给对话框设置自定义样式
setItems :设置对话框要显示的一个list,一般用于显示几个命令时
setMultiChoiceItems :用来设置对话框显示一系列的复选框setPositiveButton :给对话框添加"Yes"按钮
setNegativeButton :对话框添加"No"按钮
create : 创建对话框
show :显示对话框
2、Dialog
直接上代码:
1.首先new 一个Dialog,第二个属性是为其配置一些dialog属性,例如背景,show()/dismiss()动画,是否有标题等等
2.inflate一个布局文件,再通过 dialog.setContentView(localView) 设置其展示的自定义布局
3.获取到dialog的window,通过window设置其布局的属性,长宽自适应,展示的位置等等
4.为弹窗布局里的控件赋值啊,添加点击事件等等,我这儿是一个弹窗展示下载进度,进度懒得写,就为图片和按钮增加了点击事件。
5.dialog.show()展示就完事
val dialog = Dialog(this, R.style.jz_style_dialog_progress) //设置一些属性
val localView = LayoutInflater.from(this).inflate(R.layout.common_global_volume_dialog, null) //设置自定义的弹窗UI
dialog.setContentView(localView)
val window = dialog.window
window?.setLayout(-2, -2) //-2 其实就是WRAP_CONTENT
val localLayoutParams = window?.attributes
localLayoutParams?.gravity = Gravity.BOTTOM
window?.attributes = localLayoutParams
val imageV: AppCompatImageView = dialog.findViewById(R.id.volume_image_tip)
val cancelTv = dialog.findViewById<AppCompatTextView>(R.id.volume_tv_cancel)
val confirmTv = dialog.findViewById<AppCompatTextView>(R.id.volume_tv_confirm)
imageV.setOnClickListener {
dialog.dismiss()
}
cancelTv.setOnClickListener {
Toast.makeText(it.context, "Cancel", Toast.LENGTH_SHORT).show()
dialog.dismiss()
}
confirmTv.setOnClickListener {
Toast.makeText(it.context, "Confirm", Toast.LENGTH_LONG).show()
dialog.dismiss()
}
dialog.show() //展示弹窗
展示效果:
1、当参数值包含Gravity.LEFT时,对话框出现在左边,所以lp.x就表示相对左边的偏移,负值忽略.
2、当参数值包含Gravity.RIGHT时,对话框出现在右边,所以lp.x就表示相对右边的偏移,负值忽略.
3、当参数值包含Gravity.TOP时,对话框出现在上边,所以lp.y就表示相对上边的偏移,负值忽略.
4、当参数值包含Gravity.BOTTOM时,对话框出现在下边,所以lp.y就表示相对下边的偏移,负值忽略.
5、当参数值包含Gravity.CENTER_HORIZONTAL时
对话框水平居中,所以lp.x就表示在水平居中的位置移动lp.x像素,正值向右移动,负值向左移动.
6、当参数值包含Gravity.CENTER_VERTICAL时
,对话框垂直居中,所以lp.y就表示在垂直居中的位置移动lp.y像素,正值向右移动,负值向左移动.
7、gravity的默认值为Gravity.CENTER,即Gravity.CENTER_HORIZONTAL |
Gravity.CENTER_VERTICAL.
8、Dialogh距离边界都有一小段距离
二、popWindow
这是popwindow实例
package com.example.lxview.base.pop
import android.annotation.SuppressLint
import android.graphics.drawable.ColorDrawable
import android.view.*
import android.widget.PopupWindow
import androidx.appcompat.widget.AppCompatTextView
import com.example.lxview.R
@SuppressLint("InflateParams")
class PickHistorySelectPop private constructor(v: View, val listener: (Int?) -> Unit?, private val focusAble: Boolean = true) {
var popWindow: PopupWindow? = null
companion object {
fun show(v: View, focusAble: Boolean? = true, listener: (Int?) -> Unit?): PickHistorySelectPop {
return PickHistorySelectPop(v, listener, focusAble ?: true)
}
}
private lateinit var replied: AppCompatTextView
private lateinit var reply: AppCompatTextView
private lateinit var refused: AppCompatTextView
private lateinit var all: AppCompatTextView
init {
show(v, listener)
}
enum class SelectType(val type: Int) {
REPLIED(1), NO_REPLY(0), REFUSED(3), ALL(-1)
}
private fun show(v: View, onResult: (Int?) -> Unit?) {
popWindow = PopupWindow(v.context)
val view = View.inflate(v.context, R.layout.select_pop_content, null)
popWindow?.contentView = view
replied = view.findViewById(R.id.pick_history_filter_replied)
reply = view.findViewById(R.id.pick_history_filter_no_reply)
refused = view.findViewById(R.id.pick_history_filter_refused)
all = view.findViewById(R.id.pick_history_filter_all)
popWindow?.setBackgroundDrawable(ColorDrawable())
popWindow?.isOutsideTouchable = true
popWindow?.isTouchable = true
popWindow?.isFocusable = true
replied.setOnClickListener {
onResult(SelectType.REPLIED.type)
popWindow?.dismiss()
}
reply.setOnClickListener {
onResult(SelectType.NO_REPLY.type)
popWindow?.dismiss()
}
refused.setOnClickListener {
onResult(SelectType.REFUSED.type)
popWindow?.dismiss()
}
all.setOnClickListener {
onResult(SelectType.ALL.type)
popWindow?.dismiss()
}
popWindow?.showAtLocation(v, Gravity.END or Gravity.TOP, 50, 200)
}
}
调用的地方:
easyPopTv?.setOnClickListener {
PickHistorySelectPop.show(it, true) { type ->
when (type) {
0 -> {
Toast.makeText(it.context, "只展示未回复的", Toast.LENGTH_LONG).show()
}
1 -> {
Toast.makeText(it.context, "只展示已回复的", Toast.LENGTH_LONG).show()
}
3 -> {
Toast.makeText(it.context, "只展示拒绝的", Toast.LENGTH_LONG).show()
}
else -> {
Toast.makeText(it.context, "展示所有问答", Toast.LENGTH_LONG).show()
}
}
}
}
展示效果:
这个弹窗,因为要实现多语言,所以宽度不定,我用的是ninePatch来实现的宽度拉伸,这个用于项目里右上角点击图标对展示内容进行筛选。
- showAsDropDown(View anchor):相对某个控件的位置(正左下方),无偏移。
- showAsDropDown(View anchor, int xoff, int yoff):相对某个控件的位置,有偏移。
- showAtLocation(View parent, int gravity, int x, int y):相对于父控件的位置(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),可以设置偏移或无偏移。
三、细节上的区别
(1)Popupwindow在显示之前一定要设置宽高,Dialog无此限制。
(2)Popupwindow默认不会响应物理键盘的back,除非显示设置了popup.setFocusable(true);而在点击back的时候,Dialog会消失。
(3)Popupwindow不会给页面其他的部分添加蒙层,而Dialog会。
(4)Popupwindow没有标题,Dialog默认有标题,可以通过dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);取消标题,第二个dialog实例我就是通过在style里面设置其没得标题。
(5)二者显示的时候都要设置Gravity。如果不设置,Dialog默认是Gravity.CENTER。
(6)二者都有默认的背景,都可以通过setBackgroundDrawable(new ColorDrawable(android.R.color.transparent));去掉。
通过Dialog和popWindow,可以实现各种各样的效果,什么全局加载loading,五花八门的分享弹窗等等,具体选择实现可以根据需求而定。如果用的地方比较多,可以进行简单的封装,只需要简单的传入布局文件,点击事件,内容,通过几行代码便可以实现好看的弹窗功能。
当然,更简单且不需要交互的提示只需要用Toast就可以展示了。
因为想着简单明了的说明dialog和popwindow,所以几个样例都是很简单的实现。还有些自定义的弹窗在下面的Demo里