Android进阶之路 - DialogFragment有没有了解的必要?

news2024/10/7 14:30:33

几个月前写到了弹框业务,以前经常用Dialog、ButtomDialog 、popupWindow 组件,为了契合项目结构参考了原有的 DialogFragment 组件,特此予以记录

我一般在项目中写弹框组件的话,主要用到 alertDialogpopupWindow 组件,关于 DialogFragment 组件将在该篇简单学一下

弹框

  • Dialog 基础入门 - 普通对话框、水平进度条对话框、普通列表对话框、单选对话框、复选对话框
  • ButtomDialog 使用方式(早期所写,回头看来当时词不达意,本质还是用了alertDialog ,参考意义不大)
  • popupWindow 使用方式 - 个人认为可以和Dialog一较高下,项目中使用相对频繁,定制化也高
  • 用Kotlin写个能让我进步的Dialog - 近几年因为Kotlin盛行,写了一个项目中的应用场景

Tip:嗯… 可以了解,但是如果你已经掌握了其他弹框技术,在无特定需求下可以先不学,毕竟这么多年下来这款组件的普及率、使用率好像并不太高,而我也在逐渐替换掉项目中 DialogFragment 的使用场景…

六月梅雨季

    • 基础了解
    • 函数分析
    • 实战检验

基础了解

起初其实我不太理解为何要用DialogFragment?它相比常用弹框组件的优势在哪里?

通过DialogFragment源码可以确定其继承自Fragment故拥有其特性,同时实现了Dialog接口监听弹框的一个取消状态、关闭状态

在这里插入图片描述

查看内部方法并不多,除了 Dilaog 的一些show、dismiss方法外,我觉得最能引起能注意的应该就是生命周期的特性了,所以这应该算是这款组件的一个优势,可以动态监听与Activity的绑定状态,以及自身的一个生命周期状态

在这里插入图片描述

单从以上源码来看,目前为止至少具备一些基础优势

  • 生命周期清晰,扩展了适用场景
  • 支持弹框布局自定义化
  • 支持Dialog相关设置
  • 与Activity生命周期绑定,会随着Activity消失而消失(未复测)

函数分析

当我们创建 DialogFragment 时,因未声明抽象方法,所以我们根据需求,可自行选择重写几个关键方法,如

  • onCreate:生命周期第一步,一般根据业务可获取创建DialogFragment时的入参,用于当前组件显示等
  • onCreateView: 用于设置弹框布局、事件处理
  • onCreateDialog:可在此处调用其Dialog特性
  • onResume:弹窗展示,可在此处获取当前显示的Dialog,便于设置一些特定属性,常用于设置组件展示范围、形式
  • onDismiss: 监听弹窗消失,根据业务需求自行加入逻辑
  • onActivityCreated:便于监听Activity的状态,支持Activity关联创建后,及时展示DialogFragment(方法已过时,未用过)

onCreate

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle bundle = getArguments();
        //获取外部传入的数据
        String ourContent = bundle.getString("keyContent");
    }

onCreateView

绑定弹框要显示的布局,同时可以设置一些组件显示、事件等

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.our_layout, container, false);
        TextView tvContent = rootView.findViewById(R.id.tv_content);
        TextView tvClose = rootView.findViewById(R.id.tv_close);

        tvClose.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismissAllowingStateLoss();
            }
        });
        tvContent.setOnClickListener(ourClick);
        return rootView;
    }

有的人蛮喜欢抽方法,其实本质相同,这里就是将点击事件抽到了外部(建议初方法内部复杂、繁琐、调用频繁外,并不推荐抽方法)

    View.OnClickListener ourClick = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            dismissAllowingStateLoss();
        }
    };

onCreateDialog

关于Dialog属性设置,可以在此处进行设置,例如触摸、点击视图以外区域不会关闭弹框等

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Dialog dialog = super.onCreateDialog(savedInstanceState);
        // dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setCanceledOnTouchOutside(false);
        return dialog;
    }

onResume

显示时设置弹框大小、位置、背景等

    @Override
    public void onResume() {
        super.onResume();
        Dialog dialog = getDialog();
        if (dialog != null && dialog.getWindow() != null) {
            dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
            WindowManager.LayoutParams layoutParams = dialog.getWindow().getAttributes();
            layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
            layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
            layoutParams.gravity = Gravity.CENTER;
            dialog.getWindow().setAttributes(layoutParams);
        }
    }

onDismiss

通常为了灵活性,我们大多会监听弹框消失做一些逻辑处理,不过也不排除一些固有行为直接在组件内部实现

    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
// 可自行做一些数据记录之类的  SPUtils.AppSP().put("key", "value");
    }

关于 DialogFragment 常用函数,我们已经都说完了,那么简单说一下它的调用方式,通常我们主要有俩种常见方式

  • 创建 DialogFragment 实例后,调用类似show()函数显示弹框

在这里插入图片描述

  • DialogFragment 内部提供静态方法

有的说遇到了 Fragment already added(重复添加) 的问题,可以参考下方DialogFragment 内部提供的静态方法

  /**
   * 静态方法,支持便捷调用,同时传入所需参数
   */
  public static OurFirstDialogFragment display(final FragmentManager fragmentManager, String contentUrl) {
      OurFirstDialogFragment fragment = (OurFirstDialogFragment) fragmentManager.findFragmentByTag(OurFirstDialogFragment.class.getCanonicalName());
      if (fragment == null) {
          fragment = new OurFirstDialogFragment();
          Bundle bundle = new Bundle();
          bundle.putString(KEY_CONTENT, contentUrl);
          fragment.setArguments(bundle);
      }
      if (!fragment.isAdded()) {
          fragment.show(fragmentManager, OurFirstDialogFragment.class.getCanonicalName());
      }
      return fragment;
  }

关于 DialogFragment 显示,通常有showshowNow函数,主要区别于此

  • show显示稍慢于showNow,这导致调用show了后,立刻修改dialog中的view(例如textView修改字符内容)会崩溃,而showNow不会(showNow容错率更高
  • 待检验:(废弃)展示弹窗后fragment对象会添加到activity,showNow会在弹窗dismiss消失后移除fragment,show不会移除
    (以前同一个对象非连续地调用两次show会崩溃,现在不会了,可能是google更新了,使show也在弹窗消失后移除了)
  • 待检验:不可连续地调用show或者showNow;这个“连续”是指在弹窗还没有消失的时候再次调用,原因在上方说了,展示弹窗后fragment对象会添加到activity,而同一个fragment只能添加一次,所以连续调用可能会崩溃

实战检验

调用方式

涉及到了Fragment,所以一般会用到fragmentManager ,在Activity、Fragment都有现成API

 //当前我用的是静态函数,可以直接通过类型+函数调用
 //关于调用函数,如果为了兼容多场景,可以重载其静态方法
 OurFirstDialogFragment .display(fragmentManager) //不传值
 OurFirstDialogFragment .display(fragmentManager,"数据") //传值

 //通过创建实例的方式,显示弹框,因项目未采用此方式,仅做示例
 var ourFirstDialogFragment = OurFirstDialogFragment()
 fragmentManager?.let { ourFirstDialogFragment.show(it, "tag一般独一无二,防止重复") }

OurFirstDialogFragment(自定义DialogFragment )

package xx;

import android.app.Dialog;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;

public class OurFirstDialogFragment extends DialogFragment {
    public static final String KEY_CONTENT = "KEY_CONTENT";
    private String ourContent;
    private TextView tvContent, tvClose;

  /**
   * 静态方法,支持便捷调用,同时传入所需参数
   */
  public static OurFirstDialogFragment display(final FragmentManager fragmentManager, String content) {
      OurFirstDialogFragment fragment = (OurFirstDialogFragment) fragmentManager.findFragmentByTag(OurFirstDialogFragment.class.getCanonicalName());
      if (fragment == null) {
          fragment = new OurFirstDialogFragment();
          Bundle bundle = new Bundle();
          bundle.putString(KEY_CONTENT, content);
          fragment.setArguments(bundle);
      }
      if (!fragment.isAdded()) {
          fragment.show(fragmentManager, OurFirstDialogFragment.class.getCanonicalName());
      }
      return fragment;
  }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle bundle = getArguments();
        //获取外部传入的数据
        ourContent = bundle.getString(KEY_CONTENT);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.our_layout, container, false);
        tvContent = rootView.findViewById(R.id.tv_content);
        tvClose = rootView.findViewById(R.id.tv_close);

        tvClose.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismissAllowingStateLoss();
            }
        });
        tvContent.setOnClickListener(ourClick);
        return rootView;
    }

    View.OnClickListener ourClick = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            dismissAllowingStateLoss();
        }
    };

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Dialog dialog = super.onCreateDialog(savedInstanceState);
        // dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        dialog.setCanceledOnTouchOutside(false);
        return dialog;
    }

    @Override
    public void onResume() {
        super.onResume();
        Dialog dialog = getDialog();
        if (dialog != null && dialog.getWindow() != null) {
            dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
            WindowManager.LayoutParams layoutParams = dialog.getWindow().getAttributes();
            layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
            layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
            layoutParams.gravity = Gravity.CENTER;
            dialog.getWindow().setAttributes(layoutParams);
        }
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        //SPUtils.AppSP().put("key", "value");
    }

}

嗯,当你看到这儿的话,不知道你是否遇到了下面这个问题,当我们在Dialog内部操作时,我们希望外部可以实时监听,这时候就用到了接口回调

    public interface DialogCallback {
        void onButtonClicked(String buttonText);
    }

    private DialogCallback callback;

   public static OurFirstDialogFragment display(final FragmentManager fragmentManager, String content) {
      OurFirstDialogFragment fragment = (OurFirstDialogFragment) fragmentManager.findFragmentByTag(OurFirstDialogFragment.class.getCanonicalName());
      if (fragment == null) {
          fragment = new OurFirstDialogFragment();
          Bundle bundle = new Bundle();
          bundle.putString(KEY_CONTENT, content);
          fragment.setArguments(bundle);
      }
      //在静态方法中绑定监听回调,如果你采用的是实例调用,需要重写构造参数,然后在绑定监听回调
      fragment.callback = callback;
      if (!fragment.isAdded()) {
          fragment.show(fragmentManager, OurFirstDialogFragment.class.getCanonicalName());
      }
      return fragment;
  }

   tvView.setOnClickListener(v -> {
     dismissAllowingStateLoss();
     callback.onButtonClicked("动态回传要监听的内容即可");
  });

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

对于AI最敏感的问题,高盛内部分歧不小,但“即便是泡沫,也会持续很长时间”

展望未来十年,质疑者预测,AI只能将美国生产率提高0.5%,对GDP增长的贡献累计仅为0.9%。乐观分析师则预计,生成式AI最终将自动化25%的工作任务,并推动美国生产率提高9%,GDP增长6.1%。 人工智能信仰下&#xf…

【Proteus仿真】多变循环彩灯

【Proteus仿真】多变循环彩灯 ‍ 01功能描述 10种灯光效果可通过按键进行切换/通过按键切换灯光效果,通过数码管显示当前灯光模式,并通过按键调节当前灯光速度快慢。 02原理图 ​​ ‍ 03资料内容 源码仿真 04资料获取链接 https://docs.qq.co…

MySQL事物

事务的概念 ●事务是一种机制、一个操作序列,包含了一组数据库操作命令,并且把所有的命令作为一个整体 一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行。 ●事务是一个不可分割的工作逻辑单元&#…

Animate源文件修改要注意什么?

最近经常有同学提问,如何对Animate源文件,也就是fla格式文件进行修改,这里简单说一下要注意的内容。 首先是要使用Animate软件打开源文件,要尽量使用完整版本的软件,以免无法正常运行代码。接下来就是要注意的几点&…

fmsbggradar|复现CNS同款雷达图并且加上显著P值

文章目录 前言fmsb数据格式参数调整demo ggradar单样本demo多组demo 实战-给radar添加统计量 前言 雷达图,Radar(也称为蜘蛛图或网络图)是一种二维图表,用于显示多变量数据。每个变量由从中心延伸出来的辐条(或轴&…

uniapp 使用cavans 生成海报

uniapp 使用cavans 生成海报 npm install qs-canvas1.创建 useCanvas.js /*** Shopro qs-canvas 绘制海报* version 1.0.0* author lidongtony* param {Object} options - 海报参数* param {Object} vm - 自定义组件实例*/ import QSCanvas from qs-canvas; import { getPos…

花了大几万的踩坑经验!宠物空气净化器哪个牌子好:希喂、小米、有哈PK

我的闺蜜最近向我大吐苦水,自从家里养了猫之后,她发现家里的空气质量大不如前。宠物的浮毛和排泄物的气味在空气中飘散,让她非常怀念以前没有养猫时家里清新的呼吸环境。她觉得这些漂浮的毛发和异味大大降低了居家的舒适度。 还引起了身体上…

容器:string

以下是对于string容器常用功能和函数的总结 主要包括 1、定义string 2、字符串赋值 3、字符串拼接:str.append() 4、字符串查找:str.find() / str.rfind() 5、字符串替换:str.replace() 6、字符串长度比较:str.compare…

Quartz定时任务组件

官网:http://www.quartz-scheduler.org/ 1)job - 任务 - 你要做什么事? 2)Trigger - 触发器 - 做什么事,什么时候触发,可以传入任务 3)Scheduler - 任务调度 - 可以传入多个触发器进行任务调…

ASUS/华硕天选Air 2021 FX516P系列 原厂win10系统

安装后恢复到您开箱的体验界面,带原机所有驱动和软件,包括myasus mcafee office 奥创等。 最适合您电脑的系统,经厂家手调试最佳状态,性能与功耗直接拉满,体验最原汁原味的系统。 原厂系统下载网址:http:…

Transformer教程之序列到序列模型(Seq2Seq)

在自然语言处理(NLP)的领域中,Transformer模型无疑是近年来最具革命性的方法之一。它的出现不仅大大提高了机器翻译、文本生成等任务的精度,还推动了整个深度学习研究的进步。本文将详细介绍Transformer模型中的序列到序列模型&am…

【Linux】性能分析器 perf 详解(一):简介、安装、stat命令演示

1、简介 perf 是由 Linux 官方提供的系统性能分析工具 。它包含两部分: perf_events ,Linux 内核中的一个子系统perf 命令,用户空间的应用程序内核子系统 perf_events 提供了性能计数器(hardware performance counters)和性能事件的支持,它以事件驱动型的方式工作,通过…

NVIDIA-NCCL下载资源分享,跳过Authenticator验证

目录 Authenticator进入下载页面Download NCCL 2.22.3, for CUDA 12.5, June 18th, 2024Local installers (x86)Network installers (x86)Local installers (ARM)Network installers (ARM) Download NCCL 2.22.3, for CUDA 12.4, June 18th, 2024Local installers (x86)Network…

如何实现高精度PCB切割?— 在线式激光切割机解决方案

在线式PCB精密激光切割机是一种专门用于电子行业中印刷电路板(PCB)切割的高精度设备。以下是关于在线式PCB精密激光切割机的详细信息和特点: 1. 工作原理: 在线式PCB精密激光切割机主要通过激光束的高能量密度对PCB板进行瞬时加热…

2024年客户体验的几个预测

数字化转型、以客户为中心的理念、数字技术的发展和产品的不断创新,都为客户体验带来了巨大的改变。 目前,我们看到很多公司都在致力于塑造一种以客户为中心的商业模式。企业开始用更多技术、更多数据和更多产品来强化自己在客户体验方面的能力。 那么&a…

应用DMAIC方法解决问题的注意事项有哪些?

在解决问题的过程中,DMAIC方法作为一种强大的工具,广泛应用于各类质量管理、流程改进以及持续优化的项目中。DMAIC代表定义(Define)、测量(Measure)、分析(Analyze)、改进&#xff0…

嵌入式应用开发屏幕教程8080并口通信

目录 #8080相关概念介绍 #8080并行通信硬件连接部分 #并行通信硬件电路连接图 #并行通信读数据规定 #并行通信写数据规定 #8080相关概念介绍 通信协议分为串行通信协议,并行通信协议,而本章所讲的8080是一种并行通信协议,并行通信协议 Pa…

FullScreen API与F11快捷键的相关问题排查与解决

前言 某个项目需要点击全屏按钮将页面中某个容器内的元素进行全屏显示便于用户操作,点击退出全屏时显示原来的页面内容 问题 1:指定元素全屏存在部分元素无法显示 记得之前看 FullScreen 相关API时有印象可以让某一元素直接全屏显示,随即…

视频技术朝着8K超高清方向发展,安防监控领域将迎来怎样变化?

一、背景 随着科技的日新月异,视频技术已逐渐成为我们日常生活中不可或缺的一部分。从娱乐、教育到安全监控,视频技术无处不在,并以其独特的方式影响着我们的生活方式。本文将探讨视频技术的发展趋势,并重点关注其在监控领域的应…

哨兵模式--哨兵节点的功能?

哨兵节点的主要功能有: 集群监控:监控 主、从节点的健康状况;自动切换主节点:当 Master 运行故障,哨兵启动自动故障恢复流程:从 slave 中选择一台作为新 master。通知:让 slave 执行 replicaof…