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.创建并显示dialog
 dialogBuilder.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">

    <ImageView
        android: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.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.google.android.material.textfield.TextInputEditText
            android: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.TextInputLayout
        android:id="@+id/textInputLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.google.android.material.textfield.TextInputEditText
            android: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">

    <ImageView
        android: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.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.google.android.material.textfield.TextInputEditText
            android: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.TextInputLayout
        android:id="@+id/textInputLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/user_input"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="user" />
    </com.google.android.material.textfield.TextInputLayout>

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <androidx.appcompat.widget.AppCompatButton
            android: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.AppCompatButton
            android: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.创建并显示dialog
        dialogBuilder.create().apply {
        //指定窗口动画
          window?.attributes?.windowAnimations = R.style.BottomAni
          window?.setGravity(Gravity.BOTTOM)
        }.show()

最终效果:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

搭建Docker私有镜像仓库

大家好&#xff0c;今天给大家分享一下如何搭建私有镜像仓库&#xff0c;私有镜像仓库可以更好地管理和控制镜像的访问和使用&#xff0c;确保只有授权的人员能够获取和使用特定的镜像&#xff0c;而且方便团队内部共享定制化的镜像&#xff0c;提高开发和部署效率&#xff0c;…

MySQL 中的HASH详解

MySQL中的哈希索引&#xff08;Hash Index&#xff09;是一种特殊的数据库索引类型&#xff0c;它利用哈希表&#xff08;Hash Table&#xff09;的数据结构来存储索引项。哈希表通过哈希函数&#xff08;Hash Function&#xff09;将索引列的值转化为一个固定长度的哈希码&…

【目录】500 行或更少(500 Lines or Less)

AOSA 500 行或更少&#xff08;500 Lines or Less&#xff09;是《开源应用程序体系结构》(Architecture of Open Source Applications, AOSA)系列的第四卷。该系列的前三卷是关于大型程序必须解决的大问题&#xff0c;而本书专注于程序员在构建新事物时在小规模中做出的设计决…

数据结构链表

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

SAP PP模块学习提炼第一部分

SAP是ERP的一款软件。 SAP的入门困难&#xff1a; 听不懂&#xff0c;看不懂缺乏知识体系缺乏行业经验 SAP入门引导&#xff1a; 导师引导实战演练 SAP基础介绍 1.什么是SAP? System, Application and Products in Data Processing 即数据处理的系统、应用和产品。 2.…

7: 分配器

文章目录 operator new () 和 malloc()stl 中allocator的使用allocators 内部实现vc的分配器 并没有太多的技巧可言下面是BC5的stl 设计GCC的allocators2.9版本GCC使用的allocator是什么&#xff1f;这里面cookie的作用 4.9版本的GCC allocator operator new () 和 malloc() 所…

Redis系列之key过期策略介绍

为什么要有过期策略&#xff1f; Redis是一个内存型的数据库&#xff0c;数据是放在内存里的&#xff0c;但是内存也是有大小的&#xff0c;所以&#xff0c;需要配置redis占用的最大内存&#xff0c;主要通过maxmemory配置 maxmomory <bytes> # redis占用的最大内存官…

深入解析C#中的async和await关键字

文章目录 一、异步编程的基本概念及其在C#中的实现二、async关键字的定义及其用法三、await关键字的定义及其用法示例代码&#xff1a;使用async和await编写一个简单的异步程序 四、async和await的优点注意事项 五、C#下async和await中常见问题汇总1. 异步方法中的await调用2. …

CST电磁仿真软件远场源的导出调用和提取结果【小白必看】

远场源的导出&调用(1) 提取Hybrid仿真所需的远场源&#xff01; Post-Processing > Tools > Result Templates Tools >Farfield and Antenna Properties > Export Farfields As Source 混合求解(Hybrid Simulation)是对安装在舰船等大型平台上的天线进行仿真…

【Leetcode 42】 接雨水-单调栈解法

基础思路&#xff1a; 维持栈单调递减&#xff0c;一旦出现元素大于栈顶元素&#xff0c;就可以计算雨水量&#xff0c;同时填坑&#xff08;弹出栈顶元素&#xff09; 需要注意&#xff1a; 单调栈通常保存的是下标&#xff0c;用于计算距离 public static int trap2(int[…

什么是OSW(光交换)?

光交换&#xff08;OSW&#xff09;是光传输网络中的一项关键技术&#xff0c;为复杂网络内光信号的动态路由和管理提供了手段。OSW的工作原理涉及对光信号路径的精确控制&#xff0c;确保光通信系统中的高效和灵活传输。本文全面介绍了OSW的不同类型、功能、工作模式和优势&am…

Linux 二十一章

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…

上市企业扣非净利润是什么意思,可以反映什么问题?

扣非净利润&#xff0c;全称“扣除非经常性损益后的净利润”&#xff0c;是指企业在剔除与正常经营无关的、偶然发生的损益后所得到的利润。这些非经常性损益包括但不限于政府补贴、处置长期资产、税收返还等。 扣非净利润的计算公式为&#xff1a;扣非净利润 净利润 - 非经常…

python:机器学习特征优选

作者&#xff1a;CSDN _养乐多_ 在Python中进行机器学习特征选择的方法有很多种。以下是一些常用的方法&#xff1a; 过滤法&#xff08;Filter Methods&#xff09;&#xff1a;通过统计方法或者相关性分析来评估每个特征的重要性&#xff0c;然后选择最相关的特征。常用的…

基于改进遗传优化的BP神经网络金融序列预测算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 遗传算法&#xff08;GA&#xff09;原理 4.2 BP神经网络原理 4.3 遗传优化BP神经网络结合应用 4.4 遗传算法简要改进 5.完整程序 1.程序功能描述 基于改进遗传优化的BP神经网络金融…

电机控制系列模块解析(17)—— 速度环

一、电机转速控制 电机控制的速度环是整个电机控制系统中的外环&#xff0c;其主要任务是根据设定的转速指令值&#xff08;目标速度&#xff09;与实际电机转速之间的偏差&#xff0c;调整电流环的参考值&#xff08;d轴电流Id或q轴电流Iq&#xff0c;涉及类似单电流环的弱磁…

OpenCampass评测实战 作业

按照如下教程文档操作即可&#xff1a;https://aicarrier.feishu.cn/wiki/NxUOwnLuvi0clykyzj7ccSHPndb

JavaScript解决精度问题-math.js-使用入门

JavaScript精度失真案例 0.1+0.2 结果是:0.300000000000000041-0.9 结果是:0.099999999999999984.10*100 结果是:409.999999999999946.10/0.1 结果是:60.99999999999999大数计算 9007199254740992+1 结果是9007199254740992 JavaScript 浮点数运算结果不对,因浮点数的存储…

物料厘不清?企业如何做好“物料管理”

物料包括原材料、半成品、成品、辅助用品以及生产过程中必然产生的边角余料、废料等。在制造企业中&#xff0c;各个部门的业务流程几乎都要用到物料&#xff1a; 销售和订单录入部门要通过物料确定客户定制产品的构形&#xff1b; 计划部门要根据物料来计划物料和能力的需求…

AI绘画ComfyUI工作流安装教程,新手入门安装部署教程

ComfyUI 是专为 Stable Diffusion 打造的图形用户界面&#xff08;GUI&#xff09;&#xff0c;采用了基于节点的操作方式。用户可以通过连接不同的模块&#xff08;即节点&#xff09;来创建复杂的图像生成流程。这些节点涵盖了多样的功能&#xff0c;包括加载检查点模型、输入…