一、什么是自定义弹窗
安卓的自定义弹窗(Custom Dialog) ,是指开发者不使用系统默认样式的弹窗(如 AlertDialog
),而是通过自己定义布局、逻辑和样式来创建一个具有个性化外观和行为的弹窗。

📌 为什么要用自定义弹窗?
系统提供的 AlertDialog
虽然简单易用,但样式固定、功能受限。如果你想实现如下需求时就需要自定义弹窗:
- 弹窗中有复杂布局(比如图片、输入框、按钮等)
- 需要适配品牌风格、主题
- 实现动画、全屏或圆角弹窗等特殊效果
- 更灵活的交互逻辑
二、实践步骤
2.1 自定义弹窗样式
编写 res/layout/dialog_health_save.xml

xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/app_dialog_bg"
android:orientation="vertical"
>
<!-- 标题 -->
<TextView
android:id="@+id/tvDialogTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:gravity="left"
android:padding="10dp"
android:text="未填写内容:"
android:textColor="@color/status_blue"
android:textSize="20sp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/_999" />
<!-- 可滑动的未保存项区域 -->
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="8"
android:fillViewport="true"
android:maxHeight="300dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical">
<!--般状况-->
<LinearLayout
android:id="@+id/tvGeneralConditionContextLine"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone"
>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="9"
>
<TextView
android:id="@+id/tvGeneralConditionContext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="一般状况:人群分类、联系电话、联系人姓名、联系人电话、现住址、户籍地址未填写\n基本信息:人群分类、联系电话、联系人姓名、联系人电话、现住址、户籍地址未填写"
android:textColor="@color/black"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="right">
<!-- 确定按钮 -->
<Button
android:id="@+id/tvGeneralConditionContextEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:text="编辑"
android:textColor="@color/status_blue"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>
<!--生活方式-->
<LinearLayout
android:id="@+id/tvLifestyleContextLine"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone"
>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="9"
>
<TextView
android:id="@+id/tvLifestyleContext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="生活方式:人群分类、联系电话、联系人姓名、联系人电话、现住址、户籍地址未填写\n基本信息:人群分类、联系电话、联系人姓名、联系人电话、现住址、户籍地址未填写"
android:textColor="@color/black"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="right"
>
<!-- 确定按钮 -->
<Button
android:id="@+id/tvLifestyleContextEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:text="编辑"
android:textColor="@color/status_blue"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>
<!--症状-->
<LinearLayout
android:id="@+id/tvSymptomContentLine"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone"
>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="9"
>
<TextView
android:id="@+id/tvSymptomContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="症状:人群分类、联系电话、联系人姓名、联系人电话、现住址、户籍地址未填写\n基本信息:人群分类、联系电话、联系人姓名、联系人电话、现住址、户籍地址未填写"
android:textColor="@color/black"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="right"
>
<!-- 确定按钮 -->
<Button
android:id="@+id/tvSymptomEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:text="编辑"
android:textColor="@color/status_blue"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>
<!--脏器功能-->
<LinearLayout
android:id="@+id/tvOrganFunctionContextLine"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone"
>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="9"
>
<TextView
android:id="@+id/tvOrganFunctionContext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="脏器功能:人群分类、联系电话、联系人姓名、联系人电话、现住址、户籍地址未填写\n基本信息:人群分类、联系电话、联系人姓名、联系人电话、现住址、户籍地址未填写"
android:textColor="@color/black"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="right"
>
<!-- 确定按钮 -->
<Button
android:id="@+id/tvOrganFunctionContextEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:text="编辑"
android:textColor="@color/status_blue"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>
<!--查体-->
<LinearLayout
android:id="@+id/physicalExaminationLine"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone"
>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="9"
>
<TextView
android:id="@+id/physicalExamination"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="查体:人群分类、联系电话、联系人姓名、联系人电话、现住址、户籍地址未填写\n基本信息:人群分类、联系电话、联系人姓名、联系人电话、现住址、户籍地址未填写"
android:textColor="@color/black"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="right"
>
<!-- 确定按钮 -->
<Button
android:id="@+id/physicalExaminationEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:text="编辑"
android:textColor="@color/status_blue"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>
<!--辅助检查-->
<LinearLayout
android:id="@+id/auxiliaryExamLine"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone"
>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="9"
>
<TextView
android:id="@+id/auxiliaryExam"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="赋值检查:人群分类、联系电话、联系人姓名、联系人电话、现住址、户籍地址未填写\n基本信息:人群分类、联系电话、联系人姓名、联系人电话、现住址、户籍地址未填写"
android:textColor="@color/black"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="right"
>
<!-- 确定按钮 -->
<Button
android:id="@+id/auxiliaryExamEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:text="编辑"
android:textColor="@color/status_blue"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>
<!--现存健康问题-->
<LinearLayout
android:id="@+id/tvCurrentHealthIssuesContextLine"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone"
>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="9"
>
<TextView
android:id="@+id/tvCurrentHealthIssuesContext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="现存健康问题:人群分类、联系电话、联系人姓名、联系人电话、现住址、户籍地址未填写\n基本信息:人群分类、联系电话、联系人姓名、联系人电话、现住址、户籍地址未填写"
android:textColor="@color/black"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="right"
>
<!-- 确定按钮 -->
<Button
android:id="@+id/tvCurrentHealthIssuesContextEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:text="编辑"
android:textColor="@color/status_blue"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>
<!--更多-->
<LinearLayout
android:id="@+id/tvMoreContextLine"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone"
>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="9"
>
<TextView
android:id="@+id/tvMoreContext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:text="更多:人群分类、联系电话、联系人姓名、联系人电话、现住址、户籍地址未填写\n基本信息:人群分类、联系电话、联系人姓名、联系人电话、现住址、户籍地址未填写"
android:textColor="@color/black"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="right"
>
<!-- 确定按钮 -->
<Button
android:id="@+id/tvMoreContextEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:text="编辑"
android:textColor="@color/status_blue"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
<View
android:layout_width="match_parent"
android:paddingTop="5dp"
android:layout_height="1dp"
android:background="@color/_999" />
<!-- 确定按钮 -->
<Button
android:id="@+id/btnDialogCancel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:text="确认"
android:textColor="@color/status_blue"
android:textSize="30sp" />
</LinearLayout>
2.2 编写质控判空方法
java
package com.imindbot.medicalinformation.utils;
import cn.hutool.core.collection.CollectionUtil;
import com.google.gson.Gson;
import com.imindbot.medicalinformation.data.param.FamilyBedHistoryParam;
import com.imindbot.medicalinformation.data.param.HealthCheckUpSaveParam;
import com.imindbot.medicalinformation.data.param.HospitalizationHistoryParam;
import com.imindbot.medicalinformation.data.param.MedicationParam;
import com.imindbot.medicalinformation.data.param.NonImmunizationVaccinationHistoryParam;
import com.imindbot.medicalinformation.data.vo.HealthSaveEmptyVo;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
// 引入集合工具类,方便检查列表是否为空
import java.text.SimpleDateFormat;
import java.util.*;
/**
* author:
* date:2025-03-31 9:38
* 服务类,用于检查健康体检保存参数中的空值项。
**/
public class NullCheckServiceNew {
/**
* 检查健康检查表单中的空值项。
*
* @param
* @return 一个 HealthSaveEmptyVo 对象,包含了各个部分未填写字段的提示信息。
*/
public static HealthSaveEmptyVo healthCheckEmpty(HealthCheckUpSaveParam entity) {
// Gson gson = new Gson();
//
// HealthCheckUpSaveParam entity = gson.fromJson(e, HealthCheckUpSaveParam.class);
HealthSaveEmptyVo result = new HealthSaveEmptyVo();
if (entity == null) {
return null;
}
List<String> generalConditionEmptyFields = new ArrayList<>(); // 一般状况
List<String> lifestyleEmptyFields = new ArrayList<>(); // 生活方式
List<String> symptomEmptyFields = new ArrayList<>(); // 症状
List<String> organFunctionEmptyFields = new ArrayList<>(); // 脏器功能
List<String> physicalExamEmptyFields = new ArrayList<>(); // 查体
List<String> existingProblemsEmptyFields = new ArrayList<>(); // 现存健康问题
List<String> moreInfoEmptyFields = new ArrayList<>(); // 更多信息(住院史等)
List<String> auxiliaryExamEmptyFields = new ArrayList<>(); //辅助检查
// 一般状况
if (ObjectUtils.isEmpty(entity.getTemperature())) { // 体温
generalConditionEmptyFields.add("体温");
}
if (ObjectUtils.isEmpty(entity.getBreathingRate())) { // 呼吸频率
generalConditionEmptyFields.add("呼吸频率");
}
// 血压需要同时看收缩压和舒张压,左右两侧都看
if (ObjectUtils.isEmpty(entity.getLeftSystolic()) || ObjectUtils.isEmpty(entity.getLeftDiastolic())) { // 左侧血压
generalConditionEmptyFields.add("左侧血压");
}
if (ObjectUtils.isEmpty(entity.getRightSystolic()) || ObjectUtils.isEmpty(entity.getRightDiastolic())) { // 右侧血压
generalConditionEmptyFields.add("右侧血压");
}
if (ObjectUtils.isEmpty(entity.getPulse())) { // 脉率
generalConditionEmptyFields.add("脉率");
}
if (ObjectUtils.isEmpty(entity.getHeight())) { // 身高
generalConditionEmptyFields.add("身高");
}
if (ObjectUtils.isEmpty(entity.getWeight())) { // 体重
generalConditionEmptyFields.add("体重");
}
if (ObjectUtils.isEmpty(entity.getWaist())) { // 腰围
generalConditionEmptyFields.add("腰围");
}
if (StringUtils.isNotBlank(entity.getBirthday())) {
int age = calculateAge(entity.getBirthday());
if (age >= 65) {
if (StringUtils.isBlank(entity.getHealthSelfRating())) { // 健康状态自我评估
generalConditionEmptyFields.add("健康状态自我评估");
}
if (StringUtils.isBlank(entity.getElderlySelfCareScore())) { // 生活自理能力 (老年人)
generalConditionEmptyFields.add("生活自理能力"); // 注意:字段名叫 elderlySelfCareScore,可能只对老年人有意义
}
if (StringUtils.isBlank(entity.getCognition())) { // 认知功能
generalConditionEmptyFields.add("认知功能");
}
}
}
// 生活方式
if (ObjectUtils.isEmpty(entity.getWeeklyExercises())) { // 每次锻炼时间
lifestyleEmptyFields.add("锻炼频率");
}
if (ObjectUtils.isEmpty(entity.getExerciseTime())) { // 每次锻炼时间
lifestyleEmptyFields.add("每次锻炼时间");
}
if (StringUtils.isBlank(entity.getExerciseYears())) { // 坚持锻炼时间
lifestyleEmptyFields.add("坚持锻炼时间");
}
if (StringUtils.isBlank(entity.getExerciseType())) { // 锻炼方式
lifestyleEmptyFields.add("锻炼方式");
}
// 吸烟情况需要综合判断
if (StringUtils.isBlank(entity.getSmoking())) { // 吸烟情况(从不、已戒烟、吸烟)
lifestyleEmptyFields.add("吸烟情况");
} else if ("吸烟".equals(entity.getSmoking())) { // 如果选择吸烟,才检查下面两个
if (ObjectUtils.isEmpty(entity.getDailySmoke())) { // 日吸烟量
lifestyleEmptyFields.add("日吸烟量");
}
if (ObjectUtils.isEmpty(entity.getStartSmokingAge())) { // 开始吸烟年龄
lifestyleEmptyFields.add("开始吸烟年龄");
}
} else if ("已戒烟".equals(entity.getSmoking())) { // 如果选择已戒烟,才检查戒烟年龄
if (ObjectUtils.isEmpty(entity.getQuitSmokingAge())) { // 戒烟年龄
lifestyleEmptyFields.add("戒烟年龄");
}
if (ObjectUtils.isEmpty(entity.getStartSmokingAge())) { // 开始吸烟年龄也可能需要填
lifestyleEmptyFields.add("开始吸烟年龄");
}
}
// 饮酒情况类似
if (StringUtils.isBlank(entity.getDrinkingFrequency())) { // 饮酒频率
lifestyleEmptyFields.add("饮酒频率");
} else if (!"从不".equals(entity.getDrinkingFrequency())) { // 如果不是从不饮酒
if (ObjectUtils.isEmpty(entity.getDailyDrinkAmount())) { // 日饮酒量
lifestyleEmptyFields.add("日饮酒量");
}
if (ObjectUtils.isEmpty(entity.getStartDrinkingAge())) { // 开始饮酒年龄
lifestyleEmptyFields.add("开始饮酒年龄");
}
// 检查饮酒种类 (优先检查 List,如果 List 为空再看 String)
if (CollectionUtil.isEmpty(entity.getDrinkTypeList()) && StringUtils.isBlank(entity.getDrinkType())) {
lifestyleEmptyFields.add("饮酒种类");
}
// 检查是否戒酒和戒酒年龄
if (StringUtils.isBlank(entity.getQuitDrinking())) { // 是否戒酒
lifestyleEmptyFields.add("是否戒酒");
} else if ("是".equals(entity.getQuitDrinking())) { // 如果选择是戒酒
if (ObjectUtils.isEmpty(entity.getQuitDrinkingAge())) { // 戒酒年龄
lifestyleEmptyFields.add("戒酒年龄");
}
}
}
// 症状
// 如果 symptomList 为空,并且 symptomOther 也为空,则认为未填写
if (CollectionUtil.isEmpty(entity.getSymptomList()) && StringUtils.isBlank(entity.getSymptomOther())) {
if (StringUtils.isBlank(entity.getSymptom())) {
symptomEmptyFields.add("症状");
}
}
// 脏器功能
//口唇
if (ObjectUtils.isEmpty(entity.getLips())) {
organFunctionEmptyFields.add("口唇");
}
//咽部
if (ObjectUtils.isEmpty(entity.getThroatList())) {
organFunctionEmptyFields.add("咽部");
}
// 齿列 (优先检查 List)
if (CollectionUtil.isEmpty(entity.getTeethAlignmentList()) && StringUtils.isBlank(entity.getTeethAlignment())) {
organFunctionEmptyFields.add("齿列");
} else {
// 如果齿列选了"缺齿",检查缺齿部位
boolean needCheckMissing = (entity.getTeethAlignmentList() != null && entity.getTeethAlignmentList().contains("缺齿"))
|| "缺齿".equals(entity.getTeethAlignment()); // 假设单选或多选都可能包含
if (needCheckMissing && CollectionUtil.isEmpty(entity.getMissingTeethList()) && StringUtils.isBlank(entity.getMissingTeeth())) {
organFunctionEmptyFields.add("缺齿部位");
}
// 如果齿列选了"龋齿",检查龋齿部位
boolean needCheckCavities = (entity.getTeethAlignmentList() != null && entity.getTeethAlignmentList().contains("龋齿"))
|| "龋齿".equals(entity.getTeethAlignment());
if (needCheckCavities && CollectionUtil.isEmpty(entity.getCavitiesList()) && StringUtils.isBlank(entity.getCavities())) {
organFunctionEmptyFields.add("龋齿部位");
}
// 如果齿列选了"义齿",检查义齿部位
boolean needCheckDentures = (entity.getTeethAlignmentList() != null && entity.getTeethAlignmentList().contains("义齿(假牙)"))
|| "义齿(假牙)".equals(entity.getTeethAlignment());
if (needCheckDentures && CollectionUtil.isEmpty(entity.getDenturesList()) && StringUtils.isBlank(entity.getDentures())) {
organFunctionEmptyFields.add("义齿部位");
}
}
// 视力
if (CollectionUtil.isEmpty(entity.getVisionList()) && StringUtils.isBlank(entity.getVision())) {
organFunctionEmptyFields.add("视力");
}
// 听力
if (StringUtils.isBlank(entity.getHearing())) {
organFunctionEmptyFields.add("听力");
}
if (StringUtils.isBlank(entity.getMotorFunction())) {
organFunctionEmptyFields.add("运动功能");
}
// 查体
if (isEffectivelyBlank(entity.getSkin()) && isEffectivelyBlank(entity.getSkinOther())) { // 皮肤
physicalExamEmptyFields.add("皮肤");
}
if (isEffectivelyBlank(entity.getSclera()) && isEffectivelyBlank(entity.getScleraOther())) { // 巩膜
physicalExamEmptyFields.add("巩膜");
}
if (CollectionUtil.isEmpty(entity.getLymphNodesList()) && isEffectivelyBlank(entity.getLymphNodes()) && isEffectivelyBlank(entity.getLymphNodesOther())) { // 淋巴结
physicalExamEmptyFields.add("淋巴结");
}
if (isEffectivelyBlank(entity.getBarrelChest())) { // 肺部桶状胸
physicalExamEmptyFields.add("肺部桶状胸");
}
if (isEffectivelyBlank(entity.getBreathSounds()) && isEffectivelyBlank(entity.getBreathSoundsOther())) { // 肺部呼吸音
physicalExamEmptyFields.add("肺部呼吸音");
}
if (CollectionUtil.isEmpty(entity.getRalesList()) && isEffectivelyBlank(entity.getRales()) && isEffectivelyBlank(entity.getRalesListOther())) { // 肺部啰音
physicalExamEmptyFields.add("肺部啰音");
}
if (isEffectivelyBlank(entity.getHeartRhythm())) { // 心律
physicalExamEmptyFields.add("心律");
}
if (isEffectivelyBlank(entity.getHeartMurmur()) && isEffectivelyBlank(entity.getHeartMurmurOther())) { // 心脏杂音
physicalExamEmptyFields.add("心脏杂音");
}
if (isEffectivelyBlank(entity.getAbdominalTenderness()) && isEffectivelyBlank(entity.getAbdominalTendernessOther())) { // 腹部压痛
physicalExamEmptyFields.add("腹部压痛");
}
if (isEffectivelyBlank(entity.getAbdominalMass()) && isEffectivelyBlank(entity.getAbdominalMassOther())) { // 腹部包块
physicalExamEmptyFields.add("腹部包块");
}
if (isEffectivelyBlank(entity.getHepatomegaly()) && isEffectivelyBlank(entity.getHepatomegalyOther())) { // 腹部肝大
physicalExamEmptyFields.add("腹部肝大");
}
if (isEffectivelyBlank(entity.getSplenomegaly()) && isEffectivelyBlank(entity.getSplenomegalyOther())) { // 腹部脾大
physicalExamEmptyFields.add("腹部脾大");
}
if (isEffectivelyBlank(entity.getShiftingDullness()) && isEffectivelyBlank(entity.getShiftingDullnessOther())) { // 移动性浊音
physicalExamEmptyFields.add("移动性浊音");
}
if (CollectionUtil.isEmpty(entity.getDorsalPulsePartList()) && isEffectivelyBlank(entity.getDorsalPulse())) { // 足背动脉搏动
physicalExamEmptyFields.add("足背动脉搏动");
}
// 现存健康问题
// 如果对应的 List 和 Other 字段都为空,则认为未填写
if (CollectionUtil.isEmpty(entity.getCerebrovascularDiseaseList()) && (StringUtils.isBlank(entity.getCerebrovascularDiseaseOther())||entity.getCerebrovascularDiseaseOther().equals("null") )) {
existingProblemsEmptyFields.add("脑血管疾病");
}
if (CollectionUtil.isEmpty(entity.getKidneyDiseaseList()) && (StringUtils.isBlank(entity.getKidneyDiseaseOther())||entity.getKidneyDiseaseOther().equals("null") )) {
existingProblemsEmptyFields.add("肾脏疾病");
}
if (CollectionUtil.isEmpty(entity.getHeartDiseaseList()) && (StringUtils.isBlank(entity.getHeartDiseaseOther())||entity.getHeartDiseaseOther().equals("null") )) {
existingProblemsEmptyFields.add("心脏疾病");
}
if (CollectionUtil.isEmpty(entity.getVascularDiseaseList()) && (StringUtils.isBlank(entity.getVascularDiseaseOther())||entity.getVascularDiseaseOther().equals("null") )) {
existingProblemsEmptyFields.add("血管疾病");
}
if (CollectionUtil.isEmpty(entity.getEyeDiseaseList()) && (StringUtils.isBlank(entity.getEyeDiseaseOther())||entity.getEyeDiseaseOther().equals("null") )) {
existingProblemsEmptyFields.add("眼部疾病");
}
// 神经系统疾病 和 其他系统疾病 在 HealthCheckup 中有 String 字段,在 Param 中只有 Other 字段
if (StringUtils.isBlank(entity.getNervousSystemDisease()) && (StringUtils.isBlank(entity.getNervousSystemDiseaseOther())||entity.getNervousSystemDiseaseOther().equals("null") )) {
existingProblemsEmptyFields.add("神经系统疾病");
}
if (CollectionUtil.isEmpty(entity.getOtherSystemDiseaseList()) && (StringUtils.isBlank(entity.getOtherSystemDiseaseOther())||entity.getOtherSystemDiseaseOther().equals("null") )) {
existingProblemsEmptyFields.add("其他系统疾病");
}
// 更多信息
// 检查住院史列表是否为空
List<String> hospital = new ArrayList<>();
if (CollectionUtil.isNotEmpty(entity.getHospitalHistoryList())) {
for (HospitalizationHistoryParam param : entity.getHospitalHistoryList()) {
if (ObjectUtils.isEmpty(param.getMedicalInstitutionName())) {
hospital.add("医疗机构名称");
}
if (ObjectUtils.isEmpty(param.getReason())) {
hospital.add("原因");
}
if (ObjectUtils.isEmpty(param.getAdmissionDate())) {
hospital.add("入院日期");
}
if (ObjectUtils.isEmpty(param.getDischargeDate())) {
hospital.add("出院日期");
}
if (ObjectUtils.isEmpty(param.getMedicalRecordNumber())) {
hospital.add("病案号");
}
if (ObjectUtils.isNotEmpty(hospital)){
moreInfoEmptyFields.add("住院史:" + String.join("、",hospital) + "未填写");
break;
}
}
}
// 检查家庭病床史列表是否为空
List<String> familyBed = new ArrayList<>();
if (CollectionUtil.isNotEmpty(entity.getFamilyBedHistoryList())) {
for (FamilyBedHistoryParam param : entity.getFamilyBedHistoryList()) {
if (ObjectUtils.isEmpty(param.getMedicalInstitutionName())) {
familyBed.add("医疗机构名称");
}
if (ObjectUtils.isEmpty(param.getReason())) {
familyBed.add("原因");
}
if (ObjectUtils.isEmpty(param.getBedEstablishedDate())) {
familyBed.add("建床日期");
}
if (ObjectUtils.isEmpty(param.getBedRemovedDate())) {
familyBed.add("撤床日期");
}
if (ObjectUtils.isEmpty(param.getMedicalRecordNumber())) {
familyBed.add("病案号");
}
if (ObjectUtils.isNotEmpty(familyBed)){
moreInfoEmptyFields.add("家庭病床史:" + String.join("、",familyBed) + "未填写");
break;
}
}
}
// 检查非免疫规划接种史列表是否为空
List<String> nonVaccine = new ArrayList<>();
if (CollectionUtil.isNotEmpty(entity.getNonVaccineHistoryList())) {
for (NonImmunizationVaccinationHistoryParam param : entity.getNonVaccineHistoryList()) {
if (ObjectUtils.isEmpty(param.getVaccineName())) {
nonVaccine.add("疫苗名称");
}
if (ObjectUtils.isEmpty(param.getVaccinationDate())) {
nonVaccine.add("接种日期");
}
if (ObjectUtils.isEmpty(param.getVaccinationSite())) {
nonVaccine.add("接种结构");
}
if (ObjectUtils.isNotEmpty(nonVaccine)){
moreInfoEmptyFields.add("非免疫规划接种史:" + String.join("、",nonVaccine) + "未填写");
break;
}
}
}
// 检查用药情况列表是否为空
List<String> medication = new ArrayList<>();
if (CollectionUtil.isNotEmpty(entity.getMedicationList())) {
for (MedicationParam param : entity.getMedicationList()) {
if (ObjectUtils.isEmpty(param.getMedicationName())) {
medication.add("药品名称");
}
if (ObjectUtils.isEmpty(param.getMedicationTime())) {
medication.add("用药时间");
}
if (ObjectUtils.isEmpty(param.getFrequencyDay())) {
medication.add("频次");
}
if (ObjectUtils.isEmpty(param.getDosagePerDay())) {
medication.add("每次用量");
}
if (ObjectUtils.isNotEmpty(medication)){
moreInfoEmptyFields.add("用药情况:" + String.join("、",medication) + "未填写");
break;
}
}
}
// 辅助检查
// 血常规检查
if (isEffectivelyBlank(entity.getRandomGlucose())) { // 随机血糖
auxiliaryExamEmptyFields.add("随机血糖");
}
if (isEffectivelyBlank(entity.getFastingBloodGlucose())) { // 空腹血糖
auxiliaryExamEmptyFields.add("空腹血糖");
}
if (isEffectivelyBlank(entity.getPro())) { // 尿蛋白
auxiliaryExamEmptyFields.add("尿蛋白");
}
if (isEffectivelyBlank(entity.getSug())) { // 尿糖
auxiliaryExamEmptyFields.add("尿糖");
}
if (isEffectivelyBlank(entity.getKet())) { // 尿酮体
auxiliaryExamEmptyFields.add("尿酮体");
}
if (isEffectivelyBlank(entity.getBld())) { // 尿潜血
auxiliaryExamEmptyFields.add("尿潜血");
}
// 心电图检查
if (isEffectivelyBlank(entity.getEcgResult())) { // 心电图结果
auxiliaryExamEmptyFields.add("心电图结果");
}
if (isEffectivelyBlank(entity.getEcgExaDoctor())) { // 心电图检查医生
auxiliaryExamEmptyFields.add("心电图检查医生");
}
// 腹部B超检查
if (isEffectivelyBlank(entity.getAbusResult())) { // 腹部B超结果
auxiliaryExamEmptyFields.add("腹部B超结果");
}
if (isEffectivelyBlank(entity.getAbusExaDoctor())) { // 腹部B超检查医生
auxiliaryExamEmptyFields.add("腹部B超检查医生");
}
if (!generalConditionEmptyFields.isEmpty()) {
result.setGeneralCondition("一般状况:" + String.join("、", generalConditionEmptyFields) + "未填写");
}
if (!lifestyleEmptyFields.isEmpty()) {
result.setLifestyle("生活方式:" + String.join("、", lifestyleEmptyFields) + "未填写");
}
if (!symptomEmptyFields.isEmpty()) {
result.setSymptoms("症状:" + String.join("、", symptomEmptyFields) + "未填写");
}
if (!organFunctionEmptyFields.isEmpty()) {
result.setOrganFunction("脏器功能:" + String.join("、", organFunctionEmptyFields) + "未填写");
}
if (!physicalExamEmptyFields.isEmpty()) {
result.setPhysicalExamination("查体:" + String.join("、", physicalExamEmptyFields) + "未填写");
}
if (!existingProblemsEmptyFields.isEmpty()) {
result.setExistingHealthIssues("现存健康问题:" + String.join("、", existingProblemsEmptyFields) + "未填写");
}
if (!moreInfoEmptyFields.isEmpty()) {
result.setAdditionalInfo("更多:" + String.join(",", moreInfoEmptyFields));
}
if (!auxiliaryExamEmptyFields.isEmpty()) {
result.setAuxiliaryExam("辅助检查:" + String.join("、", auxiliaryExamEmptyFields));
}
return result;
// return gson.toJson(result);
}
// 计算年龄
private static int calculateAge(String birthDate) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date birth = sdf.parse(birthDate);
Calendar birthCalendar = Calendar.getInstance();
birthCalendar.setTime(birth);
Calendar now = Calendar.getInstance();
int age = now.get(Calendar.YEAR) - birthCalendar.get(Calendar.YEAR);
if (now.get(Calendar.MONTH) < birthCalendar.get(Calendar.MONTH) ||
(now.get(Calendar.MONTH) == birthCalendar.get(Calendar.MONTH) &&
now.get(Calendar.DAY_OF_MONTH) < birthCalendar.get(Calendar.DAY_OF_MONTH))) {
age--;
}
return age;
} catch (java.text.ParseException e) {
return -1;
}
}
private static boolean isEffectivelyBlank(String str) {
return StringUtils.isBlank(str) || "null".equalsIgnoreCase(str.trim());
}
}
2.3 监控保存按钮
java
binding.llTitleBar.tvSave.setOnClickListener(view -> {```
//提示未保存项
showHealthSaveDialog(view,paramDiffPanKong);
}
public void showHealthSaveDialog(View view, HealthCheckUpSaveParam paramDiffPanKong) {
// 创建 AlertDialog.Builder
AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());
// 通过 LayoutInflater 加载自定义布局
LayoutInflater inflater = LayoutInflater.from(view.getContext());
View dialogView = inflater.inflate(R.layout.dialog_health_save, null);
// 绑定控件
TextView tvDialogTitle = dialogView.findViewById(R.id.tvDialogTitle);
Button btnDialogCancel = dialogView.findViewById(R.id.btnDialogCancel);
TextView tvGeneralConditionContext = dialogView.findViewById(R.id.tvGeneralConditionContext);
View tvGeneralConditionContextLine = dialogView.findViewById(R.id.tvGeneralConditionContextLine);
View tvGeneralConditionContextEdit = dialogView.findViewById(R.id.tvGeneralConditionContextEdit);
TextView tvLifestyleContext = dialogView.findViewById(R.id.tvLifestyleContext);
View tvLifestyleContextLine = dialogView.findViewById(R.id.tvLifestyleContextLine);
View tvLifestyleContextEdit = dialogView.findViewById(R.id.tvLifestyleContextEdit);
TextView tvSymptomContent = dialogView.findViewById(R.id.tvSymptomContent);
View tvSymptomContentLine = dialogView.findViewById(R.id.tvSymptomContentLine);
View tvSymptomEdit = dialogView.findViewById(R.id.tvSymptomEdit);
TextView tvOrganFunctionContext = dialogView.findViewById(R.id.tvOrganFunctionContext);
View tvOrganFunctionContextLine = dialogView.findViewById(R.id.tvOrganFunctionContextLine);
View tvOrganFunctionContextEdit = dialogView.findViewById(R.id.tvOrganFunctionContextEdit);
TextView physicalExaminationView = dialogView.findViewById(R.id.physicalExamination);
View physicalExaminationLine = dialogView.findViewById(R.id.physicalExaminationLine);
View physicalExaminationEdit = dialogView.findViewById(R.id.physicalExaminationEdit);
TextView auxiliaryExamView = dialogView.findViewById(R.id.auxiliaryExam);
View auxiliaryExamLine = dialogView.findViewById(R.id.auxiliaryExamLine);
View auxiliaryExamEdit = dialogView.findViewById(R.id.auxiliaryExamEdit);
TextView tvCurrentHealthIssuesContext = dialogView.findViewById(R.id.tvCurrentHealthIssuesContext);
View tvCurrentHealthIssuesContextLine = dialogView.findViewById(R.id.tvCurrentHealthIssuesContextLine);
View tvCurrentHealthIssuesContextEdit = dialogView.findViewById(R.id.tvCurrentHealthIssuesContextEdit);
TextView tvmore = dialogView.findViewById(R.id.tvMoreContext); // 新增现存健康问题
View tvMoreContextLine = dialogView.findViewById(R.id.tvMoreContextLine);
View tvMoreContextEdit = dialogView.findViewById(R.id.tvMoreContextEdit);
HealthSaveEmptyVo healthSaveEmptyVo = HealthJiaoYanUtils.getHealthSaveEmptyVo(paramDiffPanKong);
if (healthSaveEmptyVo == null ||
((healthSaveEmptyVo.generalCondition ==null || healthSaveEmptyVo.generalCondition.isBlank()) &&
(healthSaveEmptyVo.lifestyle ==null || healthSaveEmptyVo.lifestyle.isBlank()) &&
(healthSaveEmptyVo.symptoms ==null || healthSaveEmptyVo.symptoms.isBlank()) &&
(healthSaveEmptyVo.organFunction ==null || healthSaveEmptyVo.organFunction.isBlank()) &&
(healthSaveEmptyVo.physicalExamination ==null || healthSaveEmptyVo.physicalExamination.isBlank()) &&
(healthSaveEmptyVo.existingHealthIssues ==null || healthSaveEmptyVo.existingHealthIssues.isBlank()) &&
(healthSaveEmptyVo.auxiliaryExam ==null || healthSaveEmptyVo.auxiliaryExam.isBlank()) &&
(healthSaveEmptyVo.additionalInfo ==null || healthSaveEmptyVo.additionalInfo.isBlank())
)
){
return;
}
//-般状况 generalCondition tvGeneralConditionContext tvGeneralConditionContextLine
if (healthSaveEmptyVo.generalCondition != null && !healthSaveEmptyVo.generalCondition.isBlank()){
tvGeneralConditionContext.setText(healthSaveEmptyVo.generalCondition);
tvGeneralConditionContextLine.setVisibility(View.VISIBLE);
}
//生活方式 lifestyle tvLifestyleContext tvLifestyleContextLine
if (healthSaveEmptyVo.lifestyle != null && !healthSaveEmptyVo.lifestyle.isBlank()){
tvLifestyleContext.setText(healthSaveEmptyVo.lifestyle);
tvLifestyleContextLine.setVisibility(View.VISIBLE);
}
//症状 symptoms tvSymptom tvSymptomContentLine
if (healthSaveEmptyVo.symptoms != null && !healthSaveEmptyVo.symptoms.isBlank()){
tvSymptomContent.setText(healthSaveEmptyVo.symptoms);
tvSymptomContentLine.setVisibility(View.VISIBLE);
}
//脏器功能 organFunction tvOrganFunctionContext tvOrganFunctionContextLine
if (healthSaveEmptyVo.organFunction != null && !healthSaveEmptyVo.organFunction.isBlank()){
tvOrganFunctionContext.setText(healthSaveEmptyVo.organFunction);
tvOrganFunctionContextLine.setVisibility(View.VISIBLE);
}
//查体 physicalExamination physicalExaminationView physicalExaminationLine
if (healthSaveEmptyVo.physicalExamination != null && !healthSaveEmptyVo.physicalExamination.isBlank()){
physicalExaminationView.setText(healthSaveEmptyVo.physicalExamination);
physicalExaminationLine.setVisibility(View.VISIBLE);
}
//辅助检查 auxiliaryExam auxiliaryExamView auxiliaryExamLine
if (healthSaveEmptyVo.auxiliaryExam != null && !healthSaveEmptyVo.auxiliaryExam.isBlank()){
auxiliaryExamView.setText(healthSaveEmptyVo.auxiliaryExam);
auxiliaryExamLine.setVisibility(View.VISIBLE);
}
//现存健康问题 existingHealthIssues tvCurrentHealthIssuesContext tvCurrentHealthIssuesContextLine
if (healthSaveEmptyVo.existingHealthIssues != null && !healthSaveEmptyVo.existingHealthIssues.isBlank()){
tvCurrentHealthIssuesContext.setText(healthSaveEmptyVo.existingHealthIssues);
tvCurrentHealthIssuesContextLine.setVisibility(View.VISIBLE);
}
//更多 additionalInfo tvmore tvMoreContextLine
if (healthSaveEmptyVo.additionalInfo != null && !healthSaveEmptyVo.additionalInfo.isBlank()){
tvmore.setText(healthSaveEmptyVo.additionalInfo);
tvMoreContextLine.setVisibility(View.VISIBLE);
}
// 设置标题和内容
tvDialogTitle.setText("未填写内容:请检查");
// 配置对话框
builder.setView(dialogView);
AlertDialog dialog = builder.create();
// 显示对话框
dialog.show();
// **手动调整对话框大小**
if (dialog.getWindow() != null) {
dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
// 监听按钮点击事件
btnDialogCancel.setOnClickListener(v -> dialog.dismiss());
//一般状态
tvGeneralConditionContextEdit.setOnClickListener(v -> {
dialog.dismiss(); // 关闭对话框
// 跳转到 "一般状况" 页面
int targetPosition = 1; // "一般状况" Fragment 在 fragmentList 的索引
binding.viewPager.setCurrentItem(targetPosition, true); // true 表示带动画滑动
// 刷新 "一般状况" Fragment
// updateHealthBaseFragment();
});
//生活方式 2 tvLifestyleContextEdit
tvLifestyleContextEdit.setOnClickListener(v -> {
dialog.dismiss(); // 关闭对话框
// 跳转到 "一般状况" 页面
int targetPosition = 2; // "一般状况" Fragment 在 fragmentList 的索引
binding.viewPager.setCurrentItem(targetPosition, true); // true 表示带动画滑动
// 刷新 "一般状况" Fragment
// updateHealthBaseFragment();
});
//症状 3 tvSymptomEdit
tvSymptomEdit.setOnClickListener(v -> {
dialog.dismiss(); // 关闭对话框
// 跳转到 "一般状况" 页面
int targetPosition = 3; // "一般状况" Fragment 在 fragmentList 的索引
binding.viewPager.setCurrentItem(targetPosition, true); // true 表示带动画滑动
// 刷新 "一般状况" Fragment
// updateHealthBaseFragment();
});
//脏器功能 4 tvOrganFunctionContextEdit
tvOrganFunctionContextEdit.setOnClickListener(v -> {
dialog.dismiss(); // 关闭对话框
// 跳转到 "一般状况" 页面
int targetPosition = 4; // "一般状况" Fragment 在 fragmentList 的索引
binding.viewPager.setCurrentItem(targetPosition, true); // true 表示带动画滑动
// 刷新 "一般状况" Fragment
// updateHealthBaseFragment();
});
//查体 5 physicalExaminationEdit
physicalExaminationEdit.setOnClickListener(v -> {
dialog.dismiss(); // 关闭对话框
// 跳转到 "一般状况" 页面
int targetPosition = 5; // "一般状况" Fragment 在 fragmentList 的索引
binding.viewPager.setCurrentItem(targetPosition, true); // true 表示带动画滑动
// 刷新 "一般状况" Fragment
// updateHealthBaseFragment();
});
//辅助检查 6 pauxiliaryExamEdit
auxiliaryExamEdit.setOnClickListener(v -> {
dialog.dismiss(); // 关闭对话框
// 跳转到 "一般状况" 页面
int targetPosition = 6; // "一般状况" Fragment 在 fragmentList 的索引
binding.viewPager.setCurrentItem(targetPosition, true); // true 表示带动画滑动
// 刷新 "一般状况" Fragment
// updateHealthBaseFragment();
});
//现存健康问题 7 tvCurrentHealthIssuesContextEdit
tvCurrentHealthIssuesContextEdit.setOnClickListener(v -> {
dialog.dismiss(); // 关闭对话框
// 跳转到 "一般状况" 页面
int targetPosition = 7; // "一般状况" Fragment 在 fragmentList 的索引
binding.viewPager.setCurrentItem(targetPosition, true); // true 表示带动画滑动
// 刷新 "一般状况" Fragment
// updateHealthBaseFragment();
});
//更多 8 tvMoreContextEdit
tvMoreContextEdit.setOnClickListener(v -> {
dialog.dismiss(); // 关闭对话框
// 跳转到 "一般状况" 页面
int targetPosition = 8; // "一般状况" Fragment 在 fragmentList 的索引
binding.viewPager.setCurrentItem(targetPosition, true); // true 表示带动画滑动
// 刷新 "一般状况" Fragment
// updateHealthBaseFragment();
});
}
2.3 测试的数据JSON
java
{"cavitiesList":[],"cerebrovascularDiseaseList":["无"],"cerebrovascularDiseaseOther":"","denturesList":[],"dietPartOne":"荤素均衡","dietPartTwo":[],"dorsalPulsePartList":[],"drinkTypeList":[],"drinkTypeListOther":"","elderlySelfCareScoreEnt":{"activityId":"25","eatId":"9","toiletId":"21","washId":"13","wearId":"17"},"eyeDiseaseList":["无"],"eyeDiseaseOther":"","familyBedHistoryList":[],"heartDiseaseList":["无"],"heartDiseaseOther":"","hospitalHistoryList":[],"kidneyDiseaseList":["无"],"kidneyDiseaseOther":"","lymphNodesList":[],"lymphNodesOther":"","medicationList":[],"missingTeethList":[],"nervousSystemDiseaseOther":"","nonVaccineHistoryList":[],"otherSystemDiseaseList":["无"],"otherSystemDiseaseOther":"","ralesList":[],"ralesListOther":"","symptomList":[],"symptomOther":"","teethAlignmentList":[],"throatList":[],"vascularDiseaseList":["无"],"vascularDiseaseOther":"","visionList":[],"address":"广东省肇庆市端州区睦岗街道棠下社区居民委员会","birthday":"1955-11-16","contacts":"无","contactsNumber":"18476157184","domicileAddress":"广东省肇庆市端州区睦岗街道棠下社区居民委员会","drinkingFrequency":"从不","drunkInPastYear":"否","elderlySelfCareScore":"不能自理(≥19分)","exaTime":"2025-04-09","exerciseType":"骑车、跑步、跳舞、健身、打球","exerciseYears":"","hospitalHistoryListStatus":"无","inputPerson":"admin","jobHazards":"无","name":"欧苏虾","nervousSystemDisease":"无","nextYearExaTime":"2026-04-09","nonVaccineHistoryListStatus":"无","otherHazards":"","otherSystemDisease":"无","phoneNumber":"13534938556","quitDrinking":"否","smoking":"从不吸烟","specificJob":"","vision":""}
三、 总结
本文主要介绍了在实际安卓项目开发中,自定义弹窗的概念、应用场景以及具体的实现步骤。通过对系统默认弹窗的局限性分析,进一步阐述了为何需要自定义弹窗,以及如何根据项目需求进行灵活设计与开发。
在 Android 应用中,弹窗(Dialog)是一种非常常见的用户交互方式,常用于提示信息、警告、确认操作、输入数据等场景。Android 系统虽然为我们提供了如 AlertDialog
、ProgressDialog
等标准弹窗组件,但这些弹窗的样式和功能相对固定,难以满足一些复杂或个性化的需求。
为了提升用户体验并实现更丰富的界面交互效果,开发者通常会选择使用自定义弹窗。所谓自定义弹窗,指的是开发者使用自己设计的 XML 布局文件和逻辑代码,替代系统默认样式,创建具有个性化风格、动画效果、复杂结构等特点的弹出窗口。这种方式不仅可以高度还原设计稿,还能更好地适配不同品牌、主题和业务场景。