Android:弹出对话框方式梳理一览(一)

Android:弹出对话框方式梳理一览(一)

在这里插入图片描述

Guide|导言

在Android开发中,对话框可能是我们与用户交互的非常常用的方式,包括弹出一个小界面,可能在实际场景中都非常实用。本篇文章主要就是对Android弹出对话框的一些方式的梳理,同时也帮助我自己巩固,避免遗忘。

本文主要还是参考Google的官方文档,详见:对话框|Android开发者;

Dialog|对话框的基类

在Android中,Dialog类是对话框的基类,它负责实现对话框的一些共有属性,不过我们一般不直接使用Dialog类,而是使用它的衍生类,比如AlertDialog(可显示标题、最多三个按钮、可选项目列表或自定义布局的对话框),DatePickerDialog 或 TimePickerDialog(一个对话框,带有可让用户选择日期或时间的预定义界面)。

之前的文章中我们已经简单介绍过了Android中的Window相关机制,实际上Dialog也是通过Window机制显示出来的,我们可以简单看一眼源码(此处跳过也不影响后边内容):

Dialog(@UiContext @NonNull Context context, @StyleRes int themeResId,boolean createContextThemeWrapper) {......//获取WindowManager服务mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);//新创建一个Window用来显示对话框final Window w = new PhoneWindow(mContext);mWindow = w;w.setCallback(this);w.setOnWindowDismissedCallback(this);w.setOnWindowSwipeDismissedCallback(() -> {if (mCancelable) {cancel();}});//将新创建出来的Window与WindowManager关联w.setWindowManager(mWindowManager, null, null);//设置弹出的位置为中心w.setGravity(Gravity.CENTER);mListenersHandler = new ListenersHandler(this);}

一些重要的点已经注释在了代码中,可以看到,在Dialog的构造方法中就创建出来了一个Window来显示需要弹出的内容,所以说它本质上也是使用到了Window机制来进行内容的显示。接下来继续看它的show方法的部分逻辑:

    public void show() {......mWindowManager.addView(mDecor, l);if (restoreSoftInputMode) {l.softInputMode &=~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;}mShowing = true;sendShowMessage();}

这个show方法就是最终显示对话框的方法,可以看到,这里显然是使用到了windowManager的addView方法,所以到这里我们就可以明确其显示原理了,就是使用到了windowManagerService的功能。

Basic|基础使用

介绍完了Dialog的基本原理,接下来我们来了解Dialog的基础使用方法。使用Dialog大致可以分为五步:

  1. 创建Dialog的构造器
  2. 配置Dialog的内容
  3. [可选]进行一些生命周期配置(比如onStart,onStop等,此处可能在自定义Dialog中使用到)
  4. 显示Dialog
  5. 注销/隐藏Dialog

如果只是简单的使用,其实两步就可以概括:

  • 创建Dialog
  • 显示Dialog

比如一个最简单的示例如下所示:

//1.创建构造器
val dialogBuilder = AlertDialog.Builder(this)
//2.配置dialog内容
dialogBuilder.apply {//配置正文内容setMessage("This is the Message!")//配置标题setTitle("This is a Dialog!")
}
//3.创建并显示dialog
dialogBuilder.create().show()

这里注意似乎语法糖有一些问题,比如这样写:

 //1.创建构造器val dialogBuilder = AlertDialog.Builder(this)//2.配置dialog内容dialogBuilder.apply {//配置正文内容setMessage("This is the Message!")//配置标题 -- 使用语法糖将会导致失效title = "This is a Dialog!"}//3.创建并显示dialogdialogBuilder.create().show()

配置出来的标题就会失效,因为这个语法糖关联的方法的是Activity的方法,目前建议还是先别用语法糖。如果需要添加按钮,也按照上面这个方式来添加即可。

添加按钮&Message

对于AlertDialog来说,最多可以添加三个按钮,性质分别是Positive,Negative,Neutral,官方意义上可以理解成确认,取消,中立三个意思。比如说申请权限时的授权拒绝授权仅在使用中允许就差不多可以对应上前面的三个意思。

对于Positive性质的按钮,我们可以调用setPositiveButton(CharSequence text, final OnClickListener listener) 方法来设置,比如:

setPositiveButton("确认",object :DialogInterface.OnClickListener {override fun onClick(dialog: DialogInterface?, which: Int) {Log.d(TAG, "onClick: 确认")}})

不过,我们当然也可以替换成Lambda表达式:

setNegativeButton("取消") {dialog,which->Log.d(TAG, "onCreate: 取消")}

我们来改进上面这段代码,最终为:

//1.创建构造器
val dialogBuilder = AlertDialog.Builder(this)
//2.配置dialog内容
dialogBuilder.apply {//配置正文内容setMessage("This is the Message!")//配置标题setTitle("This is a Dialog!")//设置按钮//确认setPositiveButton("确认",object :DialogInterface.OnClickListener{override fun onClick(dialog: DialogInterface?, which: Int) {Log.d(TAG, "onClick: 确认")}})//取消setNegativeButton("取消"){dialog,which->Log.d(TAG, "onCreate: 取消")}//中立setNeutralButton("中立"){dialog,which ->Log.d(TAG, "onCreate: 中立")}}
//3.创建并显示dialog
dialogBuilder.create().show()

最终显示的效果:
在这里插入图片描述

添加一般列表

除了一般的按钮和文本之外,我们还可以向对话框内填入列表,不过由于列表显示在对话框的内容区域中,因此对话框无法同时显示消息和列表

添加列表项的最简单的方法通过setItems方法:

setItems(arrayOf("列表一","列表二","列表三"),object :DialogInterface.OnClickListener{override fun onClick(dialog: DialogInterface?, which: Int) {Log.d(TAG, "index:${which} ,dialog:${dialog}")}})

其中dialog参数为弹出的Dialog对象,which参数为之前传入的String数组的索引值,需要说明的是这个Dialog应该是不会被复用的,是一个非永久的Dialog,我们可以通过打印出来的日志看出来:
在这里插入图片描述

理解了上边的代码后,可以进一步简化为:

setItems(arrayOf("列表一","列表二","列表三"),{dialog, which ->Log.d(TAG, "index:${which} ,dialog:${dialog}")
})

实现的效果如下:

在这里插入图片描述
另外,也可以使用实现了ListAdapter接口的Adapter来设置列表项,比如说:

setAdapter(ArrayAdapter(context,com.google.android.material.R.layout.support_simple_spinner_dropdown_item,arrayOf("1","2")
), object :DialogInterface.OnClickListener{override fun onClick(dialog: DialogInterface?, which: Int) {Log.d(TAG, "index:${which} ,dialog:${dialog}")}})

添加永久性列表

前边提到过我们添加的DIalog不是一个可复用的,而是每次弹出都会创建一个新的Dialog,接下来我们来介绍可以添加永久性列表的方法。

添加永久性复选框☑️

所谓复选框,就是可以同时选择多个列表项的Dialog,它的添加方法和之前的也类似,我们可以调用setMultiChoiceItems来设置,比如:

setMultiChoiceItems(arrayOf("Item1","Item2","Item3"), booleanArrayOf(true,false,true),object :DialogInterface.OnMultiChoiceClickListener {override fun onClick(dialog: DialogInterface?,which: Int,isChecked: Boolean) {Log.d(TAG, "dialog is ${dialog},index is ${which},is checked? ${isChecked}")}}
)

这个方法相比之前添加列表的参数中多了一个 BooleanArray 类型,该参数指定的是第一次弹出复选框时的列表选择状态,比如说这里我们传入的是 true,false,true的参数,最终第一次弹框的效果就是:
在这里插入图片描述
如果想一开始什么都不选中,那传入一个null值即可。

最后我们来验证一下该Dialog是否是一个永久性的,分别点击列表项,我们可以发现每次打印的Dialog都是同一项:
在这里插入图片描述
说明这确实是一个永久性的Dialog。

添加永久性的单选框

最后就是添加一个永久性的单选框,这个其实和一开始的添加一个非永久性的单选框的方法很类似,唯一的一点就是多了一个参数来指定第一次弹出时选中的列表项,跟之前的类似:

setSingleChoiceItems(arrayOf("item1","item2","item3"),1,object :DialogInterface.OnClickListener {override fun onClick(dialog: DialogInterface?, which: Int) {Log.d(TAG, "index:${which} ,dialog:${dialog}")}}
)

为Dialog添加自定义内容

到之前为止其实都是Dialog的很基础的应用,实际上在使用过程中我们可能需要弹出一个Dialog来展示我们自己的定制化的内容,比如说一个登录页面,这个时候我们就不能用之前的简单的方法了,取而代之,我们可以调用setView方法来为我们的Dialog填充自定义的内容,这个过程实际上类似于动态添加View的过程。

举例来说,我们可以先设计一个布局来描述我们需要填充的具体内容:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><ImageViewandroid:id="@+id/header_img"android:layout_width="match_parent"android:layout_height="wrap_content"android:scaleType="centerCrop"app:srcCompat="@drawable/images" /><com.google.android.material.textfield.TextInputLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><com.google.android.material.textfield.TextInputEditTextandroid:id="@+id/password_input"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="password" /></com.google.android.material.textfield.TextInputLayout><com.google.android.material.textfield.TextInputLayoutandroid:id="@+id/textInputLayout"android:layout_width="match_parent"android:layout_height="wrap_content"><com.google.android.material.textfield.TextInputEditTextandroid:id="@+id/user_input"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="user" /></com.google.android.material.textfield.TextInputLayout>
</LinearLayout>

这里用了一个线性布局来描述,至于为什么不用更好用的约束布局,因为我测试过发现直接丢一个约束布局可能会带来显示异常的问题,所以如果想使用约束布局的话可能需要在约束布局外边包一层线性布局。

然后我们来配置这个自定义的Dialog:

//1.创建构造器
val dialogBuilder = AlertDialog.Builder(this)
//2.配置dialog内容
dialogBuilder.apply {//添加自定义的布局setView(R.layout.dialog_layout)setNegativeButton("Cancel",object :DialogInterface.OnClickListener {override fun onClick(dialog: DialogInterface?, which: Int) {}})setPositiveButton("Confirm",object : DialogInterface.OnClickListener {override fun onClick(dialog: DialogInterface?, which: Int) {}})
}
//3.创建并显示dialog
dialogBuilder.create().show()

最终显示的效果就是这样的:
在这里插入图片描述
我们可以和官方文档上实现的效果进行对比:
在这里插入图片描述
可以发现我们的按钮是比较丑的,实际上这部分我们也可以直接做进我们的自定义布局里:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><ImageViewandroid:id="@+id/header_img"android:layout_width="match_parent"android:layout_height="wrap_content"android:scaleType="centerCrop"app:srcCompat="@drawable/images" /><com.google.android.material.textfield.TextInputLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><com.google.android.material.textfield.TextInputEditTextandroid:id="@+id/password_input"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="password" /></com.google.android.material.textfield.TextInputLayout><com.google.android.material.textfield.TextInputLayoutandroid:id="@+id/textInputLayout"android:layout_width="match_parent"android:layout_height="wrap_content"><com.google.android.material.textfield.TextInputEditTextandroid:id="@+id/user_input"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="user" /></com.google.android.material.textfield.TextInputLayout><LinearLayoutandroid:orientation="horizontal"android:layout_width="match_parent"android:layout_height="wrap_content"><androidx.appcompat.widget.AppCompatButtonandroid:id="@+id/positivebtn"android:layout_width="0dp"android:layout_height="60dp"android:layout_margin="0dp"android:layout_weight="1"android:background="#C8E6C9"android:elevation="0dp"android:text="Confirm" /><androidx.appcompat.widget.AppCompatButtonandroid:id="@+id/negbtn"android:layout_width="0dp"android:layout_height="60dp"android:layout_margin="0dp"android:layout_weight="1"android:background="#FFCDD2"android:elevation="0dp"android:text="Cancel" /></LinearLayout>
</LinearLayout>

最终的效果就好上很多:
在这里插入图片描述
至于我们具体的点击事件的设置,则可以通过布局膨胀器获取具体的View来为按钮设置点击事件监听 :

//2.配置dialog内容
dialogBuilder.apply {val customView = layoutInflater.inflate(R.layout.dialog_layout,null,false)setView(customView)customView.findViewById<Button>(R.id.negbtn).setOnClickListener {Toast.makeText(context, "cancel!", Toast.LENGTH_SHORT).show()}customView.findViewById<Button>(R.id.positivebtn).setOnClickListener {Toast.makeText(context, "load!", Toast.LENGTH_SHORT).show()}}
//3.创建并显示dialog
dialogBuilder.create().show()

为Dialog添加进出场动画

在使用其他App时,我们往往会发现弹出的Dialog并不都是直接闪现出在屏幕中间的,经常会有一个从底部或者是从左右弹出的动画效果,实现该效果的步骤也很简单,不过我们需要有一些Android动画的基础,我们在anim文件夹下新建弹入和弹出动画:

//弹入动画

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"><translate android:fromYDelta="100%p" android:toYDelta="0"android:duration="@android:integer/config_mediumAnimTime"/>
</set>

//弹出动画

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"><translate android:fromYDelta="0" android:toYDelta="100%p"android:duration="@android:integer/config_mediumAnimTime"/>
</set>

之后我们在styles文件下新建style属性:

    <style name="BottomAni"><item name="android:windowEnterAnimation">@anim/pop_from_bottom</item><item name="android:windowExitAnimation">@anim/exit_from_bottom</item></style>

最后在创建完Dialog时指定window动画属性:

        viewBinding.button.setOnClickListener {//1.创建构造器val dialogBuilder = AlertDialog.Builder(this)//2.配置dialog内容dialogBuilder.apply {val customView = layoutInflater.inflate(R.layout.dialog_layout,null,false)setView(customView)customView.findViewById<Button>(R.id.negbtn).setOnClickListener {Toast.makeText(context, "cancel!", Toast.LENGTH_SHORT).show()}customView.findViewById<Button>(R.id.positivebtn).setOnClickListener {Toast.makeText(context, "load!", Toast.LENGTH_SHORT).show()}}//3.创建并显示dialogdialogBuilder.create().apply {//指定窗口动画window?.attributes?.windowAnimations = R.style.BottomAniwindow?.setGravity(Gravity.BOTTOM)}.show()

最终效果:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/671318.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

245 基于matlab的MEEMD信号分解及重构算法

基于matlab的MEEMD信号分解及重构算法。MEEMD方法的主要步骤包括&#xff1a;1. 定义多元信号集合&#xff0c;将多个信号进行集合&#xff1b;2. 对多元信号集合进行EEMD分解&#xff0c;得到一组IMFs&#xff1b;3. 将相同IMF进行平均&#xff0c;得到改进的IMFs&#xff1b;…

2路模拟音频光端机 JR-CA02

概述 JR-CA02光端机由发送机JR-CA02 Tansmitter和接收机JR-CA02 Receiver组成&#xff0c;通过一定距离长度的光纤相连接&#xff0c;传输2路Audio模拟音频&#xff08;即1路立体声&#xff09;。且每路音频分配输出。 JR-CA02光端机具有运行主要技术参数的监测功能&#xff…

03_Redis

文章目录 Redis介绍安装及使用redis的核心配置数据结构常用命令stringlistsethashzset(sortedset) 内存淘汰策略Redis的Java客户端JedisRedisson Redis 介绍 Redis是一个NoSQL数据库。 NoSQL: not only SQL。表示非关系型数据库&#xff08;不支持SQL标准语法&#xff09;。 …

Java特性之设计模式【享元模式】

一、享元模式 概述 享元模式&#xff08;Flyweight Pattern&#xff09;主要用于减少创建对象的数量&#xff0c;以减少内存占用和提高性能。这种类型的设计模式属于结构型模式&#xff0c;它提供了减少对象数量从而改善应用所需的对象结构的方式 享元模式尝试重用现有的同类对…

RAG 修炼手册|一文讲透 RAG 背后的技术

今天我们继续剖析 RAG&#xff0c;将为大家详细介绍 RAG 背后的例如 Embedding、Transformer、BERT、LLM 等技术的发展历程和基本原理&#xff0c;以及它们是如何应用的。 01. 什么是 Embedding? Embedding 是将离散的非结构化数据转换为连续的向量表示的技术。 在自然语言…

spring高级篇(十)

1、内嵌tomcat boot框架是默认内嵌tomcat的&#xff0c;不需要手动安装和配置外部的 Servlet 容器。 简单的介绍一下tomcat服务器的构成&#xff1a; Catalina&#xff1a; Catalina 是 Tomcat 的核心组件&#xff0c;负责处理 HTTP 请求、响应以及管理 Servlet 生命周期。它包…

win2012磁盘空间不足,c盘正常,d盘无法写入,如何解决?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

android系统serviceManger源码解析

一&#xff0c;serviceManger时序图 本文涉及到的源码文件&#xff1a; /frameworks/native/cmds/servicemanager/main.cpp /frameworks/native/libs/binder/ProcessState.cpp /frameworks/native/cmds/servicemanager/ServiceManager.cpp /frameworks/native/libs/binder/IP…

WireShark对tcp通信数据的抓包

一、抓包准备工作 安装wireshark sudo apt update sudo apt install wireshark 运行 二、WireShark工具面板分析 上图中所显示的信息从上到下分布在 3 个面板中&#xff0c;每个面板包含的信息含义如下&#xff1a; Packet List 面板&#xff1a;显示 Wireshark 捕获到的所…

场景文本检测识别学习 day09(Swin Transformer论文精读)

Patch & Window 在Swin Transformer中&#xff0c;不同层级的窗口内部的补丁数量是固定的&#xff0c;补丁内部的像素数量也是固定的&#xff0c;如上图的红色框就是不同的窗口&#xff08;Window&#xff09;&#xff0c;窗口内部的灰色框就是补丁&#xff08;Patch&#…

数据结构链表

数据结构链表 链表 1&#xff09;链表的概念及结构: 链表是一种物理存储结构上非连续存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的引用链接次序实现的。 2&#xff09;实际中链表的结构非常多样&#xff0c;以下情况组合起来就有8种链表结构&#xff1a; 单向、双向…

MYSQL基础架构、执行过程分析、事务的实现、索引的选择、覆盖索引

本文是mysql45讲的1-5的总结 文章目录 基础架构连接器分析器优化器执行器SQL查询执行过程详细执行步骤 SQL更新执行过程重要的日志模块&#xff1a;redo log重要的日志模块&#xff1a;binlog阶段性提交 事务事务隔离的实现启动 索引数据库索引模型InnoDB索引组织结构主键选择…