简介这是一份面向Android初学者的BMI健康计算器完整源码基于Java实现适合用来练习界面搭建、Activity生命周期、按钮事件与输入校验等基础技能。资源共23个文件包含2个Java源文件、8个class编译产物、3个XML布局与配置文件、3个PNG图标资源同时附有可直接安装的APK及dex、ap_等构建文件整包仅47KB结构轻量而完整。已有887人学习下载可对照运行效果逐步理解代码逻辑。通过阅读源码能学到用XML编写用户界面、在Activity中绑定事件监听器、对身高体重做合法性验证、按BMI公式完成计算并在TextView中格式化展示还可了解AndroidManifest权限声明、资源目录划分及项目构建方式项目也针对除零、空值等非正常输入做了异常处理让读者明白如何让小程序更健壮。总体上这是一份很适合课程设计、期末综合作业或自学参考的轻量代码包。1. 一份BMI健康计算器源码带你看清安卓开发从界面到计算的全部环节你下载的“安卓Android源码——BMI健康计算器.zip”不是Android Studio的Gradle工程而是带.project、.classpath、default.properties的Eclipse ADT老工程思路。解压后先看AndroidManifest.xml和res/layout再进src整个App就是从Activity里读EditText、算一个公式、写回TextView。这个源码虽然不是大型项目但把安卓开发最基本的界面布局、生命周期、事件监听、输入校验、异常处理、资源打包覆盖全了。适合刚学安卓开发的人跟着代码走一遍也适合需要把旧工程迁移到新构建系统的工程师用来练手。2. 项目结构拆解从Eclipse ADT工程到Android Studio Gradle工程拿到源码先别急着改功能先搞清楚每个文件夹和配置文件的职责。这个源码里许多文件在今天的Android Studio工程里已经看不到了但理解它们能帮你在迁移时不踩坑。比如bin目录下的Health.apk、resources.ap_都是旧构建系统生成的中间产物手动改源码时留着它们只会干扰判断。2.1 老式Android工程目录逐个拆解先看完整的目录职责表这是迁移前必须做的一步路径/文件作用迁移后处理src/Java源码Activity和计算逻辑都在这里移到app/src/main/javares/layout/XML布局文件描述界面控件移到app/src/main/res/layoutres/values/字符串、颜色、尺寸等常量资源移到app/src/main/res/valuesres/drawable-ldpi/hdpi/mdpi不同屏幕密度下的图标资源按密度目录保留尽量补xhdpiassets/不参与编译的原始文件通过AssetManager读取移到app/src/main/assetsgen/自动生成的R.java属于构建产物迁移后自动生成手工删除AndroidManifest.xml应用包名、Activity、权限声明保留到app/src/main/AndroidManifest.xmldefault.properties / project.properties指定Android target版本由build.gradle中的compileSdk替代proguard.cfgProGuard混淆规则在build.gradle中配置proguardFiles.classpathEclipse的Java构建路径删除.projectEclipse项目描述文件删除bin/编译输出包括Health.apk、classes.dex删除Gradle会重新生成这里最容易踩坑的是gen目录。很多新手迁移时发现R.java报错尝试手动编辑它实际上R类是资源ID的映射每次构建时自动生成改了也会在下次编译时被覆盖。正确做法是把gen目录整个删掉让Android Studio从res和src重新生成。assets目录在这个BMI计算器里一般不放业务数据但要知道它的存在。如果后续想扩展成健康记录应用把离线参考表放到assets里用AssetManager资源管理读取比硬编码在Java里更干净。2.2 为什么老工程要先转Gradle再动手在Android Studio里打开这份源码时常见做法是直接File - New - Import Project交给IDE自动转换。不过我一般会手工创建app模块把src和res拷到对应目录再补一份build.gradle。这样可控性更高因为老工程的default.properties里写着旧targetSdk自动转换后的资源目录可能保持旧结构后续加依赖时反而麻烦。一份适用于这个BMI计算器的build.gradle可以这样写plugins { id com.android.application } android { namespace com.example.bmi compileSdk 33 defaultConfig { applicationId com.example.bmi minSdk 16 targetSdk 33 versionCode 1 versionName 1.0 } buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile(proguard-android.txt), proguard-rules.pro } } }关键参数说明namespace是AGP 7.0以后对包名的要求对应老AndroidManifest.xml里的package属性compileSdk决定用哪个版本的API编译minSdk这里设成16基本覆盖近几年的设备targetSdk设成33适配Android 13的运行时行为。release里的minifyEnabled对应老工程的proguard.cfg开启后需要确认MainActivity没被混淆掉否则启动时会报ClassNotFoundException。如果源码里没有build.gradleAndroid Studio打开时也不会自动识别成可运行项目。手动建好Gradle文件后用Sync Now让IDE完成构建环境配置。这里需要注意Android Studio安装时的SDK版本如果只有API 34没有API 33compileSdk可以用34但最好保持targetSdk与实际测试设备一致。2.3 构建命令与参数含义转换完成后的常用命令./gradlew assembleDebug ./gradlew installDebug ./gradlew lintassembleDebug负责编译并生成app-debug.apk产物路径一般在app/build/outputs/apk/debug/。installDebug会把debug包安装到当前连接的设备或模拟器上比在IDE里手动拖拽安装快很多。lint做静态检查能找出漏掉的权限、硬编码字符串、旧API调用等隐性问题。遇到编译失败时加参数排查./gradlew assembleDebug --stacktrace只看第一行错误往往不够--stacktrace会把异常链展开到具体文件和方法资源编译失败时能定位到哪个layout第几行出错。比如老项目里常常出现drawable-ldpi和drawable-hdpi中同名图片尺寸不一致lint会在资源合并阶段给出提醒。3. UI布局与Activity生命周期BMI计算器的交互链路BMI计算器的界面很简单但界面层的组织方式决定了代码好不好扩展。接下来从res/layout/activity_main.xml和src目录下的MainActivity入手看看这个安卓源码是怎么把控件串起来的。3.1 从res/layout/activity_main.xml看控件层级老Eclipse工程里常见的是LinearLayout和RelativeLayout组合这份源码大概率用LinearLayout做垂直排列因为控件数量不多。一个标准的布局长这样?xml version1.0 encodingutf-8? LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/android android:layout_widthmatch_parent android:layout_heightmatch_parent android:orientationvertical android:padding16dp TextView android:idid/tv_label_weight android:layout_widthwrap_content android:layout_heightwrap_content android:text体重kg / EditText android:idid/et_weight android:layout_widthmatch_parent android:layout_heightwrap_content android:inputTypenumberDecimal android:hint请输入体重 / TextView android:idid/tv_label_height android:layout_widthwrap_content android:layout_heightwrap_content android:text身高m / EditText android:idid/et_height android:layout_widthmatch_parent android:layout_heightwrap_content android:inputTypenumberDecimal android:hint请输入身高 / Button android:idid/btn_calculate android:layout_widthmatch_parent android:layout_heightwrap_content android:text计算BMI / TextView android:idid/tv_result android:layout_widthmatch_parent android:layout_heightwrap_content android:paddingTop8dp android:textSize16sp / /LinearLayout逻辑说明LinearLayout在垂直方向从上到下排列适合这种表单式界面。EditText里用inputTypenumberDecimal后软键盘会切到数字面板用户没法输入字母和中文数据校验压力小很多。hint是空态提示不会影响实际输入内容。Button的宽度设为match_parent扩大点击热区减少误触。如果你从老的RelativeLayout迁移过来会发现这种写法更直白。RelativeLayout适合复杂对齐但控件一旦多了容易嵌套LinearLayout在这里已经够用。如果后续想改成更现代的方式可以换成ConstraintLayout用约束关系把布局级数压到一层。3.2 Activity生命周期里初始化了哪些东西主Activity在onCreate里完成界面加载和事件绑定。这段代码在很多老安卓源码里都是一个套路public class MainActivity extends Activity { private EditText etWeight; private EditText etHeight; private TextView tvResult; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); etWeight findViewById(R.id.et_weight); etHeight findViewById(R.id.et_height); tvResult findViewById(R.id.tv_result); Button btnCalculate findViewById(R.id.btn_calculate); btnCalculate.setOnClickListener(new View.OnClickListener() { Override public void onClick(View v) { calculateBmi(); } }); } }参数说明setContentView必须在findViewById之前调用否则控件还没加载到视图树里。这里把控件引用提升为成员变量后续在calculateBmi()里直接复用。onCreate是Activity生命周期的入口每次进程被系统回收后重新创建都会走一遍临时数据可以从savedInstanceState里恢复。监听器绑定用匿名内部类逻辑紧挨着按钮适合这种只处理一个事件的场景。如果MainActivity实现了OnClickListener接口就可以在MainActivity里写一个onClick(View v)来接收所有按钮点击再用v.getId()判断来源。这里选用匿名内部类代码内聚性更高不会把多个按钮的响应逻辑堆在一个方法里。3.3 点击事件绑定方式对比不同项目对点击事件的处理方式差别很大这里做一个对比方便你看别人的源码时快速识别风格方式写法适用场景匿名内部类setOnClickListener(new View.OnClickListener(){...})单个按钮逻辑内聚Activity接口实现implements View.OnClickListener多个按钮共用处理逻辑XML属性绑定android:onClickcalculateBmi最简洁但方法必须public需要留意的是XML属性绑定在开启混淆时会有反射调用风险。如果proguard.cfg里没有对MainActivity做keepR8重命名方法名后XML里的onClick字符串和实际方法名对不上会直接崩溃。所以迁移到Android Studio后建议把android:onClick改成匿名内部类这也是安卓开发中更稳的写法。4. 计算逻辑输入解析、BMI公式与异常处理界面层只是把数据收进来真正体现代码功底的是calculateBmi()里的计算逻辑。这块内容要处理好输入校验、公式计算和异常分支否则用户随手输入一个0程序就可能显示Infinity或者直接崩溃。4.1 BMI公式与参考分类BMI的核心公式是体重除以身高的平方单位固定为kg和m。项目源码里如果身高输入框的提示是“身高m”就直接用double计算如果提示是“厘米”必须先把height除以100很多改过这个项目的人都在这里翻车。常见的国内参考分类如下表范围状态 18.5偏瘦18.5 ~ 24正常24 ~ 28偏胖 28肥胖国际标准里18.525算正常2530算超重各国略有差异。写代码时建议把这些阈值定义成常量例如private static final double UNDERWEIGHT_THRESHOLD 18.5;后续调整分类逻辑时只改常量不用到处找数字。4.2 输入解析和合理范围验证老项目里常见的做法是直接getText().toString()然后parseDouble但没有校验会导致很多边界问题。下面这段是补全之后的可运行版本private void calculateBmi() { String weightStr etWeight.getText().toString().trim(); String heightStr etHeight.getText().toString().trim(); if (weightStr.isEmpty() || heightStr.isEmpty()) { tvResult.setText(请输入体重和身高); return; } double weight; double height; try { weight Double.parseDouble(weightStr); height Double.parseDouble(heightStr); } catch (NumberFormatException e) { tvResult.setText(输入不是有效数字); return; } if (height 0.5 || height 2.5 || weight 20 || weight 300) { tvResult.setText(请输入合理范围); return; } double bmi weight / (height * height); String level; if (bmi 18.5) { level 偏瘦; } else if (bmi 24) { level 正常; } else if (bmi 28) { level 偏胖; } else { level 肥胖; } tvResult.setText(String.format(BMI%.1f%s, bmi, level)); }这里每一步都有对应的防护。trim()去掉首尾空格避免用户在体重后多打了空格导致parseDouble抛异常。isEmpty()判断在解析前拦截空输入不进入后续逻辑。Double.parseDouble包在try-catch里保证非数字输入不会让Activity闪退。范围验证用的是“合理范围”而不是“逻辑范围”身高低于0.5米或高于2.5米、体重低于20公斤或高于300公斤都在常识之外直接提示用户。把这些条件放在计算之前还能顺手解决除零问题因为height最小是0.5不可能为0。4.3 异常处理与边界情况Java浮点运算中除以0不会像整数那样抛ArithmeticException而是返回Infinity所以单纯try-catch救不了这个场景。必须把height的范围校验放在除法之前这也是这个BMI计算器源码里最值得学的一点。如果parseDouble传入“12.3.4”这种字符串会抛NumberFormatException。更隐蔽的是全角数字“”parseDouble能认出全角数字但不同Android版本的Keyboard行为不一致建议在解析后额外用小范围校验兜底。比如身高解析出来是2.7虽然能被parseDouble接受但raw值明显不对范围校验会把它拦截掉。提示如果项目要扩展到体脂率计算建议使用BigDecimal处理中间结果避免double在不同机型上出现细微误差。但BMI本体只保留一位小数double精度完全够用不需要过度设计。5. 打包资源适配与adb冒烟验证技巧最后这部分不讲怎么看源码而是讲拿到这份安卓源码后怎么在Android Studio里真正跑起来并验证功能。重点放在资源适配和命令行调试上这些技巧在大型项目里同样适用。5.1 多密度drawable目录与加载规则老工程里的drawable-ldpi、drawable-mdpi、drawable-hdpi分别对应低、中、高密度屏幕。Android系统加载资源时会按当前设备dpi选择最接近的目录优先级从高到低。常用对应关系目录代表密度缩放比例drawable-ldpi120dpi0.75xdrawable-mdpi160dpi1xdrawable-hdpi240dpi1.5xdrawable-xhdpi320dpi2x实际开发中常见做法是至少准备hdpi和xhdpi两套启动图标。如果源码里只有ldpi和mdpi的图标在高分屏上会模糊。可以先用adb shell wm density查看当前设备密度再决定补资源到哪个目录。5.2 用adb快速做冒烟测试界面功能简单没必要每次都手动敲一遍。构建成功后用这几条命令就能完成基本的安装、启动、模拟点击和截图adb install -r app-debug.apk adb shell am start -n com.example.bmi/.MainActivity adb shell input tap 540 1200 adb shell screencap -p /sdcard/bmi_screen.png adb pull /sdcard/bmi_screen.png命令说明install -r是覆盖安装保留应用数据am start拉起MainActivityinput tap的坐标参数依赖屏幕分辨率代码动态换屏时会变screencap截图后在本地目录下拉出来可以直接看计算按钮是否被点击到了。如果App启动后立刻闪退先查日志adb logcat -s AndroidRuntime:E这条命令过滤掉所有非Error日志闪退堆栈会直接输出到终端。看到ClassNotFoundException就检查混淆规则看到NumberFormatException就检查输入框的数据解析基本覆盖MIUI、EMUI等系统上常见的安装问题。5.3 验证资源加载的实用技巧处理老工程时我习惯在提交前跑一遍lint资源检查重点看两条一是R.java里是否有废弃引用二是drawable目录里有没有不存在的密度资源引用。lint会直接给出资源缺失的位置比如error: resource drawable/ic_launcher not found比运行时崩溃定位快得多。实际验证时还可以用adb shell dumpsys package com.example.bmi查看安装包信息确认targetSdk和minSdk和build.gradle一致。BMI计算器虽然简单但如果某个设备上输入框无法弹出数字键盘第一反应应该是检查inputType是否被系统覆盖而不是怀疑计算逻辑。这个排查思路在做具体机型适配时很实用拿到老工程先看资源和manifest再动代码能省下大量时间。本文还有配套的精品资源点击获取
