Android安卓实战项目(8)---自行车fitting计算软件(源码在文末)

Android安卓实战项目(8)—自行车fitting计算软件(源码在文末🐕🐕🐕)

【bilibili演示地址】 https://www.bilibili.com/video/BV1eu4y1B7yA/?share_source=copy_web&vd_source=b2e9b9ed746acda34f499009647748ed
介绍:
在这个数字化时代,自行车运动在越来越多的人中变得流行。然而,要达到最佳的骑行体验,确保您的自行车尺寸与个人需求相匹配至关重要。为了解决这个问题,我开发了一个自行车骑行风格计算工具应用程序,它可以帮助您找到最适合您的自行车尺寸,并提供更加舒适和高效的骑行体验。

功能和目的:
该应用程序旨在帮助骑行爱好者确定适合自己的自行车尺寸,以获得更好的骑行体验。用户只需要输入脚踏板到坐垫的距离(鸭蛋高度),然后选择骑行风格(休闲、普通、竞赛),应用程序将根据一些计算公式自动计算出坐垫高度、STACK 值和 REACH 值。

工作原理:

  1. 用户输入鸭蛋高度:用户首先在应用程序中输入他们的鸭蛋高度,即脚踏板到坐垫的距离。
  2. 选择骑行风格:用户可以从休闲、普通和竞赛中选择一个骑行风格,以适应不同的骑行需求。
  3. 计算自行车尺寸:应用程序使用输入的鸭蛋高度和所选骑行风格,通过预定义的计算公式来计算出坐垫高度、STACK 值和 REACH 值。
  4. 显示结果:计算完成后,应用程序将结果显示在界面上,用户可以清楚地了解哪种自行车尺寸最适合他们的需求。

界面和交互:
该应用程序具有简洁直观的用户界面。用户可以在输入框中输入鸭蛋高度,通过单选按钮选择骑行风格,并点击“计算”按钮执行计算操作。计算结果将即时显示在界面上,包括坐垫高度、STACK 值和 REACH 值。

代码结构和关键函数:
该应用程序使用了 Kotlin 语言和 Android 开发框架。主要包含以下几个关键函数:

  1. initView():用于初始化界面,设置监听器和初始值。
  2. calculateData():用于进行数据计算,并将计算结果显示在界面上。
  3. calculateSeatHeight():计算坐垫高度的函数。
  4. calculateStack():计算 STACK 值的函数。
  5. calculateReach():计算 REACH 值的函数。

代码可扩展性:
该应用程序具有很好的可扩展性。您可以根据需要添加更多骑行风格选项或者调整计算公式,以满足不同骑行者的需求。同时,您也可以通过进一步优化界面设计和交互方式,提升用户体验。

总结:
该自行车骑行风格计算工具是一款实用的应用程序,它能够帮助骑行爱好者找到最适合自己的自行车尺寸,从而提高骑行的舒适性和效率。无论是新手还是资深骑手,使用该应用程序都能够获得更好的骑行体验。欢迎您下载并尝试这个实用的自行车骑行风格计算工具,祝您在骑行中获得更多乐趣和成就。

一.项目运行介绍

image-20230807103722425

image-20230807103736044

image-20230807103747306

二.具体实现

MainActivity.java
package io.github.laomao1997.bikefitterimport android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import android.view.inputmethod.InputMethodManager
import android.widget.Button
import android.widget.EditText
import android.widget.RadioGroup
import android.widget.TextView
import androidx.annotation.IntDef
import androidx.appcompat.app.AppCompatActivity
import java.math.BigDecimalclass MainActivity : AppCompatActivity() {companion object {/*** 骑行风格常量 - 休闲*/const val TYPE_RELAX = 0/*** 骑行风格常量 - 普通*/const val TYPE_NORMAL = 1/*** 骑行风格常量 - 竞赛*/const val TYPE_RACE = 2}private lateinit var mEtEggHeight: EditTextprivate lateinit var mBtnCalculate: Buttonprivate lateinit var mRadioGroup: RadioGroupprivate lateinit var mTvSeatHeight: TextViewprivate lateinit var mTvStackHeight: TextViewprivate lateinit var mTvReachHeight: TextView@RidingStyleTypeDefprivate var mRidingStyle = TYPE_NORMALoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)initView()}private fun initView() {mEtEggHeight = findViewById(R.id.et_egg_height)mBtnCalculate = findViewById(R.id.btn_calculate)mRadioGroup = findViewById(R.id.radio_group)mTvSeatHeight = findViewById(R.id.tv_seat_height)mTvStackHeight = findViewById(R.id.tv_stack_height)mTvReachHeight = findViewById(R.id.tv_reach_height)//点击软键盘外部,收起软键盘mEtEggHeight.setOnFocusChangeListener { v, hasFocus ->if (!hasFocus) {val manager: InputMethodManager =applicationContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManagermanager.hideSoftInputFromWindow(v.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)}}mBtnCalculate.setOnClickListener {mEtEggHeight.clearFocus()calculateData()}mRadioGroup.check(R.id.type_normal)mRadioGroup.setOnCheckedChangeListener { _, checkedId ->when (checkedId) {R.id.type_relax -> mRidingStyle = TYPE_RELAXR.id.type_normal -> mRidingStyle = TYPE_NORMALR.id.type_race -> mRidingStyle = TYPE_RACE}calculateData()}}@SuppressLint("SetTextI18n")private fun calculateData() {val eggHeight = getEggHeight()val seatHeight = calculateSeatHeight(eggHeight)val stack = calculateStack(eggHeight)val reach = calculateReach(stack)mTvSeatHeight.text = if (seatHeight == 0.0) {getString(R.string.double_line)} else {BigDecimal(seatHeight).setScale(1, BigDecimal.ROUND_HALF_UP).toString()}mTvStackHeight.text = if (stack == 0.0) {getString(R.string.double_line)} else {BigDecimal(stack).setScale(1, BigDecimal.ROUND_HALF_UP).toString()}mTvReachHeight.text = if (reach == 0.0) {getString(R.string.double_line)} else {BigDecimal(reach).setScale(1, BigDecimal.ROUND_HALF_UP).toString()}}private fun getEggHeight(): Int {val eggHeightText: String = mEtEggHeight.text.toString()return try {eggHeightText.toInt()} catch (e: Exception) {0}}/*** 计算坐垫高度*/private fun calculateSeatHeight(eggHeight: Int): Double {if (eggHeight <= 0) return 0.0return eggHeight * Constant.SEAT_HEIGHT_RATIO}/*** 计算 STACK 值*/private fun calculateStack(eggHeight: Int): Double {if (eggHeight <= 0) return 0.0return when (mRidingStyle) {TYPE_RELAX -> eggHeight * Constant.STACK_RATIO + 3TYPE_NORMAL -> eggHeight * Constant.STACK_RATIOTYPE_RACE -> eggHeight * Constant.STACK_RATIO - 2else -> 0.0}}/*** 计算 REACH 值*/private fun calculateReach(stack: Double): Double {if (stack <= 0) return 0.0return when (mRidingStyle) {TYPE_RELAX -> stack / 1.6TYPE_NORMAL -> stack / 1.48TYPE_RACE -> stack / 1.36else -> 0.0}}/*** 骑行风格类型注解*/@Retention(AnnotationRetention.SOURCE)@IntDef(TYPE_RELAX, TYPE_NORMAL, TYPE_RACE)internal annotation class RidingStyleTypeDef
}
  1. 导入相关的 Android 类:

    import android.annotation.SuppressLint
    import android.content.Context
    import android.os.Bundle
    import android.view.inputmethod.InputMethodManager
    import android.widget.Button
    import android.widget.EditText
    import android.widget.RadioGroup
    import android.widget.TextView
    import androidx.annotation.IntDef
    import androidx.appcompat.app.AppCompatActivity
    import java.math.BigDecimal
    
  2. 定义 MainActivity 类,继承自 AppCompatActivity

    class MainActivity : AppCompatActivity() {// ...
    }
    
  3. 定义了一些常量和变量:

    companion object {const val TYPE_RELAX = 0const val TYPE_NORMAL = 1const val TYPE_RACE = 2
    }@RidingStyleTypeDef
    private var mRidingStyle = TYPE_NORMAL
    

    这里定义了三个常量 TYPE_RELAXTYPE_NORMALTYPE_RACE 分别表示不同的骑行风格,以及一个变量 mRidingStyle 用于存储当前所选择的骑行风格,默认为 TYPE_NORMAL

  4. onCreate 方法:

    override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)initView()
    }
    

    onCreate 方法中,首先调用了 super.onCreate(savedInstanceState),然后通过 setContentView(R.layout.activity_main) 设置布局文件 activity_main 作为该 Activity 的视图内容。接着调用 initView() 方法来初始化视图组件。

  5. initView 方法:

    private fun initView() {// 获取并设置各个视图组件mEtEggHeight = findViewById(R.id.et_egg_height)mBtnCalculate = findViewById(R.id.btn_calculate)mRadioGroup = findViewById(R.id.radio_group)mTvSeatHeight = findViewById(R.id.tv_seat_height)mTvStackHeight = findViewById(R.id.tv_stack_height)mTvReachHeight = findViewById(R.id.tv_reach_height)// 设置点击软键盘外部时收起软键盘的功能mEtEggHeight.setOnFocusChangeListener { v, hasFocus ->if (!hasFocus) {val manager: InputMethodManager =applicationContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManagermanager.hideSoftInputFromWindow(v.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)}}// 设置计算按钮的点击事件监听器mBtnCalculate.setOnClickListener {mEtEggHeight.clearFocus()calculateData()}// 设置初始选中的骑行风格为 "普通"mRadioGroup.check(R.id.type_normal)mRadioGroup.setOnCheckedChangeListener { _, checkedId ->when (checkedId) {R.id.type_relax -> mRidingStyle = TYPE_RELAXR.id.type_normal -> mRidingStyle = TYPE_NORMALR.id.type_race -> mRidingStyle = TYPE_RACE}calculateData()}
    }
    

    initView 方法中,首先获取了布局文件中的视图组件,并赋值给对应的成员变量。然后为 mEtEggHeight 设置了一个焦点变化监听器,在失去焦点时隐藏软键盘。接着设置了计算按钮 mBtnCalculate 的点击事件监听器,在点击时清除 mEtEggHeight 的焦点并调用 calculateData() 方法进行数据计算。最后,通过 mRadioGroup 设置了选中骑行风格的监听器,并根据选中的 RadioButton 来更新 mRidingStyle 的值,并调用 calculateData() 方法进行数据计算。

  6. calculateData 方法:

    private fun calculateData() {// 获取鸭蛋高度val eggHeight = getEggHeight()// 计算坐垫高度、STACK 值和 REACH 值val seatHeight = calculateSeatHeight(eggHeight)val stack = calculateStack(eggHeight)val reach = calculateReach(stack)// 设置计算结果显示在对应的 TextView 上mTvSeatHeight.text = if (seatHeight == 0.0) {getString(R.string.double_line)} else {BigDecimal(seatHeight).setScale(1, BigDecimal.ROUND_HALF_UP).toString()}mTvStackHeight.text = if (stack == 0.0) {getString(R.string.double_line)} else {BigDecimal(stack).setScale(1, BigDecimal.ROUND_HALF_UP).toString()}mTvReachHeight.text = if (reach == 0.0) {getString(R.string.double_line)} else {BigDecimal(reach).setScale(1, BigDecimal.ROUND_HALF_UP).toString()}
    }
    

    calculateData 方法用于进行数据计算,并将计算结果显示在对应的 TextView 上。首先,获取鸭蛋高度(骑行者脚踏板到坐垫的距离),然后分别调用 calculateSeatHeight 方法、calculateStack 方法和 calculateReach 方法进行坐垫高度、STACK 值和 REACH 值的计算。最后,将计算结果显示在对应的 TextView 上,并根据结果进行格式化显示。

  7. calculateSeatHeight 方法:

    private fun calculateSeatHeight(eggHeight: Int): Double {if (eggHeight <= 0) return 0.0return eggHeight * Constant.SEAT_HEIGHT_RATIO
    }
    

    calculateSeatHeight 方法用于计算坐垫高度,根据给定的鸭蛋高度 eggHeight 和常量 Constant.SEAT_HEIGHT_RATIO 进行计算,并返回结果。

  8. calculateStack 方法:

    private fun calculateStack(eggHeight: Int): Double {if (eggHeight <= 0) return 0.0return when (mRidingStyle) {TYPE_RELAX -> eggHeight * Constant.STACK_RATIO + 3TYPE_NORMAL -> eggHeight * Constant.STACK_RATIOTYPE
    

_RACE -> eggHeight * Constant.STACK_RATIO - 2
else -> 0.0
}
}

`calculateStack` 方法用于计算 STACK 值(前叉与地面垂直时,前叉顶端至车把水平距离)。根据给定的鸭蛋高度 `eggHeight` 和当前选择的骑行风格 `mRidingStyle`,通过常量 `Constant.STACK_RATIO` 进行计算,并返回结果。9. `calculateReach` 方法:
```kotlin
private fun calculateReach(stack: Double): Double {if (stack <= 0) return 0.0return when (mRidingStyle) {TYPE_RELAX -> stack / 1.6TYPE_NORMAL -> stack / 1.48TYPE_RACE -> stack / 1.36else -> 0.0}
}

calculateReach 方法用于计算 REACH 值(前叉与地面垂直时,车把至鞍座水平距离)。根据给定的 STACK 值 stack 和当前选择的骑行风格 mRidingStyle 进行计算,并返回结果。

  1. 自定义注解 @RidingStyleTypeDef

    @Retention(AnnotationRetention.SOURCE)
    @IntDef(TYPE_RELAX, TYPE_NORMAL, TYPE_RACE)
    internal annotation class RidingStyleTypeDef
    

    这是一个自定义的注解,用于限制某个整数值只能在指定的范围内取值。在这里,它用于限制骑行风格常量的取值只能是 TYPE_RELAXTYPE_NORMALTYPE_RACE 中的一个。


三.项目源码

百度网盘:

链接:https://pan.baidu.com/s/19nSterWen-JMVOko7120UQ?pwd=jynl
提取码:jynl

有软件开发请私信作者
或+vx:15135757306

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

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

相关文章

0101docker mysql8镜像主从复制-运维-mysql

1 概述 主从复制是指将主数据库的DDL和DML操作通过二进制日志传到从库服务器&#xff0c;然后在从库上对这些日志重新执行&#xff08;也叫重做&#xff09;&#xff0c;从而使得从库和主库的数据保持同步。 Mysql支持一台主库同时向多台从库进行复制&#xff0c;从库同时可以…

ubuntu调整路由顺序

Ubuntu系统跳转路由顺序 1、安装ifmetric sudo apt install ifmetric2、查看路由 route -n3、把Iface下面的eth1调到第一位 sudo ifmetric eth1 0命令中eth1是网卡的名称&#xff0c;更改网卡eth1的跃点数&#xff08;metric值&#xff09;为0&#xff08;数值越小&#xf…

读发布!设计与部署稳定的分布式系统(第2版)笔记29_控制层下

1. 配置服务 1.1. 配置服务本身就是分布式数据库 1.1.1. 像ZooKeeper和etcd这样的配置服务 1.1.2. 受CAP定理和亚光速通信的限制 1.1.3. 可实现容量扩展&#xff0c;但不具备资源可伸缩性 1.1.4. 也会遭受相同的网络创伤 1.2. 信息并不仅仅从服务流向客户端实例&#xff…

mysql统计近7天数据量,,按时间戳分组

可以使用以下 SQL 语句来统计近7天的数据量&#xff0c;并按时间戳分组。如果某一天没有数据&#xff0c;则将其填充为0。 SELECT DATE_FORMAT(FROM_UNIXTIME(timestamp), %Y-%m-%d) AS date,COUNT(*) AS count FROM table_name WHERE timestamp > UNIX_TIMESTAMP(DATE_SUB…

python调用pytorch的clip模型时报错

使用python调用pytorch中的clip模型时报错&#xff1a;AttributeError: partially initialized module ‘clip’ has no attribute ‘load’ (most likely due to a circular import) 目录 现象解决方案一、查看项目中是否有为clip名的文件二、查看clip是否安装成功 现象 clip…

乍得ECTN(BESC)申请流程

根据TCHAD/CHAD乍得法令&#xff0c;自2013年4月1日起&#xff0c;所有运至乍得的货物都必须申请ECTN(BESC)电子货物跟踪单。如果没有申请&#xff0c;将被视为触犯乍得的条例&#xff0c;并在目的地受到严厉惩罚。ECTN是英语ELECTRONIC CARGO TRACKING NOTE的简称&#xff1b;…

基于Java+SpringBoot+Vue的人事管理系统设计与实现(源码+LW+部署文档等)

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

Ubuntu 传输文件方法

Ubuntu 传输文件方法 文章目录 Ubuntu 传输文件方法1 scpusage跨越跳板机传输 2 rsync&#xff08;remote sync&#xff09;特性installusage本地拷贝同步将文件从远程机器复制到本地机器将文件从本地机器复制到远程机器通过ssh使用rsync 3 SSHFSusage通过 SSHFS 从远程系统访问…

深挖 Threads App 帖子布局,我进一步加深了对CSS网格布局的理解

当我遇到一个新产品时&#xff0c;我首先想到的是他们如何实现CSS。当我遇到Meta的Threads时也不例外。我很快就探索了移动应用程序&#xff0c;并注意到我可以在网页上预览公共帖子。 这为我提供了一个深入挖掘的机会。我发现了一些有趣的发现&#xff0c;我将在本文中讨论。 …

使用node.js 搭建一个简单的HelloWorld Web项目

文档结构 config.ini #将本文件放置于natapp同级目录 程序将读取 [default] 段 #在命令行参数模式如 natapp -authtokenxxx 等相同参数将会覆盖掉此配置 #命令行参数 -config 可以指定任意config.ini文件 [default] authtokencc83c08d73357802 #对应一条隧…

LeetCode 周赛上分之旅 # 37 多源 BFS 与连通性问题

⭐️ 本文已收录到 AndroidFamily&#xff0c;技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 和 BaguTree Pro 知识星球提问。 学习数据结构与算法的关键在于掌握问题背后的算法思维框架&#xff0c;你的思考越抽象&#xff0c;它能覆盖的问题域就越广&#xff0c;理解难度…

python高阶技巧

目录 设计模式 单例模式 具体用法 工厂模式 优点 闭包 案例 修改闭包外部变量 闭包优缺点 装饰器 装饰器原理 装饰器写法 递归 递归的调用过程 递归的优缺点 用递归计算阶乘 设计模式 含义&#xff1a;设计模式是一种编程套路&#xff0c;通过这种编程套路可…

AQL品质抽样标准

AQL抽样标准 - 百度文库 Acceptance Quality Limit 接收质量限的缩写&#xff0c;即当一个连续系列批被提交验收时&#xff0c;可允许的最差过程平均质量水平。 AQL普遍应用于各行业产品的质量检验&#xff0c;不同的AQL标准应用于不同物质的检验上。在AQL 抽样时&#xff0c;…

【开源三方库】Aki:一行代码极简体验JSC++跨语言交互

开源项目 OpenHarmony 是每个人的 OpenHarmony 一、简介 OpenAtom OpenHarmony&#xff08;以下简称“OpenHarmony”&#xff09;的前端开发语言是ArkTS&#xff0c;在TypeScript&#xff08;简称TS&#xff09;生态基础上做了进一步扩展&#xff0c;继承了TS的所有特性&#x…

SpringBoot开发环境热部署

目录 开发热部署 添加dev-tools依赖 在application.properties中配置devtools 在IDEA中添加设置 开发热部署 在实际的项目开发调试过程中会频繁地修改后台类文件&#xff0c;导致需要重新编译、 重新启动&#xff0c;整个过程非常麻烦&#xff0c;影响开发…

STM32 低功耗-待机模式

STM32 待机模式 文章目录 STM32 待机模式第1章 低功耗模式简介第2章 待机模式简介2.1 进入待机模式2.1 退出待机模式 第3章 待机模式代码部分总结 第1章 低功耗模式简介 在 STM32 的正常工作中&#xff0c;具有四种工作模式&#xff1a;运行、睡眠、停止和待机模式。 在系统或…

【Spring Cloud 六】Hystrix熔断

这里写目录标题 系列文章目录背景一、Hystrix是什么服务雪崩服务容错的相关概念熔断器降级超时控制限流 二、会什么要有Hystrix三、如何使用Hystrix进行熔断处理整体项目代码服务提供者pom文件yml配置文件启动类controller 服务消费者pom文件yml配置文件启动类feignhystrixcont…

信息安全技术工业控制系统安全控制应用指南学习笔记

工业控制系统安全控制基线 根据工业控制系统在国家安全、经济建设、社会生活中的重要程度&#xff0c;遭到破坏后对国家安全、社会秩序、公共利益以及公民、法人和其他组织的合法权益的危害程度等&#xff0c;结合信息安全等级保护标准划分及实施效果分析&#xff0c;结合工业…

前端主题切换方案——CSS变量

前言 主题切换是前端开发中老生常谈的问题&#xff0c;本文将介绍主流的前端主题切换实现方案——CSS变量 CSS变量 简介 编写CSS样式时&#xff0c;为了避免代码冗余&#xff0c;降低维护成本&#xff0c;一些CSS预编译工具&#xff08;Sass/Less/Stylus&#xff09;等都支…

JVM面试题

JVM理论 #JVM内存模型# Java内存模型&#xff08;JMM&#xff09;&#xff1f; Java的内存模型决定了线程间的通信方式&#xff0c;JMM的模型是由主存和工作内存构成&#xff0c;两个线程想要正常通信需要将工作内存中的变量刷到主存中&#xff0c;另一个线程才能正确读取得…