Android安卓实战项目(8)---自行车fitting计算软件(源码在文末)可用于比赛项目或者作业参考中

news2024/12/23 19:30:15

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.bikefitter

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

class MainActivity : AppCompatActivity() {

    companion object {
        /**
         * 骑行风格常量 - 休闲
         */
        const val TYPE_RELAX = 0

        /**
         * 骑行风格常量 - 普通
         */
        const val TYPE_NORMAL = 1

        /**
         * 骑行风格常量 - 竞赛
         */
        const val TYPE_RACE = 2
    }

    private lateinit var mEtEggHeight: EditText
    private lateinit var mBtnCalculate: Button
    private lateinit var mRadioGroup: RadioGroup
    private lateinit var mTvSeatHeight: TextView
    private lateinit var mTvStackHeight: TextView
    private lateinit var mTvReachHeight: TextView

    @RidingStyleTypeDef
    private var mRidingStyle = TYPE_NORMAL

    override 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 InputMethodManager
                manager.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_RELAX
                R.id.type_normal -> mRidingStyle = TYPE_NORMAL
                R.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.0
        return eggHeight * Constant.SEAT_HEIGHT_RATIO
    }

    /**
     * 计算 STACK 值
     */
    private fun calculateStack(eggHeight: Int): Double {
        if (eggHeight <= 0) return 0.0
        return when (mRidingStyle) {
            TYPE_RELAX -> eggHeight * Constant.STACK_RATIO + 3
            TYPE_NORMAL -> eggHeight * Constant.STACK_RATIO
            TYPE_RACE -> eggHeight * Constant.STACK_RATIO - 2
            else -> 0.0
        }
    }

    /**
     * 计算 REACH 值
     */
    private fun calculateReach(stack: Double): Double {
        if (stack <= 0) return 0.0
        return when (mRidingStyle) {
            TYPE_RELAX -> stack / 1.6
            TYPE_NORMAL -> stack / 1.48
            TYPE_RACE -> stack / 1.36
            else -> 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 = 0
        const val TYPE_NORMAL = 1
        const 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 InputMethodManager
                manager.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_RELAX
                R.id.type_normal -> mRidingStyle = TYPE_NORMAL
                R.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.0
        return eggHeight * Constant.SEAT_HEIGHT_RATIO
    }
    

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

  8. calculateStack 方法:

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

_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.0
    return when (mRidingStyle) {
        TYPE_RELAX -> stack / 1.6
        TYPE_NORMAL -> stack / 1.48
        TYPE_RACE -> stack / 1.36
        else -> 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.coloradmin.cn/o/842925.html

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

相关文章

常用抓包工具

Fiddler Fiddler 是一个很好用的抓包工具&#xff0c;可以用于抓取http/https的数据包&#xff0c;常用于Windows系统的抓包&#xff0c;它有个优势就是免费 Charles Charles是由JAVA开发的&#xff0c;可以运行在window Linux MacOS&#xff0c;但它是收费的&#xff0c;和…

大厂容器云实践之路(一)

1-华为CCE容器云实践 华为企业云 | CCE容器引擎实践 ——从IaaS到PaaS到容器集群 容器部署时代的来临 IaaS服务如日中天 2014-2015年&#xff0c;大家都在安逸的使用IaaS服务&#xff1b; 亚马逊AWS的部署能力方面比所有竞争对手…

有血有肉的PPT

1、PPT是Powerpoint缩写 2、引申的含义是Powerpoint Power(力量/能量&#xff09; Point(观点/要点) 3、用PPT做的文档是讲演稿&#xff0c;讲演的内容要有力度&#xff0c;之所以要去演讲是为了能够影响受众 4、其次演讲稿上的内容要列出要点、表明观点&#xff0c;所以一般P…

Java 并发容器和框架Fork/Join详解

目 录 一 使用场景 1 大规模数据处理 2 复杂计算 3 并行搜索 4 并行排序 二 Fork/Join框架介绍 三 Fork/Join框架模块 四 Fork/Join框架核心思想 1分治思想(Divide-and-Conquer) 2 work-stealing(工作窃取)算法 五 Fork/Join框架执行流程 1 实现原理&#xff1a; 2…

Vue3 第二节 Vue3的响应式

1.Vue3的响应式原理 2.ref函数和reactive函数的对比 3.setup注意点 一.Vue3的响应式原理 1.Vue2.x中的响应式原理 ① 实现原理 对象类型&#xff1a;通过Object.defineProperty() 对属性的读取&#xff0c;修改进行拦截&#xff08;数据劫持&#xff09;数组类型&#xf…

上位机是什么?有什么实际用途?

上位机是指控制、监测或管理下位机的计算机系统&#xff0c;也可以称为主机。它通常用于工业自动化、机器人控制、数据采集和处理等领域。在工业自动化中&#xff0c;上位机负责向下位机下发指令并获取反馈信息&#xff0c;以控制生产流程。在机器人控制中&#xff0c;上位机负…

详细教程:如何搭建废品回收小程序

废品回收是一项环保举措&#xff0c;通过回收和再利用废弃物品&#xff0c;可以减少资源浪费和环境污染。近年来&#xff0c;随着智能手机的普及&#xff0c;小程序成为了推广和运营的重要工具。本文将详细介绍如何搭建一个废品回收小程序。 1. 进入乔拓云网后台 首先&#xf…

Maven: ‘mvn‘ is not recognized as an internal or external command

下载并配置好Maven之后&#xff0c;CMD测试安装是否成功&#xff1a;mvn -v 提示&#xff1a; mvn is not recognized as an internal or external command, operable program or batch file. 检查环境变量&#xff1a; MAVEN_HOME: %MAVEN_HOME%\bin: 看上去没问题&#x…

pinctrl设备树节点映射详细分析imx_dt_node_to_map

pinctrl设备树节点映射详细分析imx_dt_node_to_map 文章目录 pinctrl设备树节点映射详细分析imx_dt_node_to_mapstruct pinctrl_mapreally_probepinctrl_bind_pinscreate_pinctrlpinctrl_dt_to_mapdt_to_map_one_configdt_remember_or_free_mappinctrl_register_map add_settin…

Linux网络服务之部署yum仓库

yum &#xff1f; yum ! 一、YUM概述1.1 yum简介1.2 yum工作原理 二、yum 配置文件2.1 yum主配置文件2.2 yum仓库设置文件2.2.1 配置文件主要格式2.2.2 软件仓库的提供方式2.2.3 日志文件 三、yum命令详解3.1 安装和升级3.2 查询3.2.1 显示可用的安装包 ----- yum list3.2.2 显…

数据结构笔记--归并排序及其拓展题(小和问题、逆序对问题)

目录 1--归并排序 2--小和问题 3--逆序对问题 1--归并排序 归并排序的核心思想&#xff1a;将一个无序的序列归并排序为一个有序的系列&#xff1b;通过递归将无序的序列二分&#xff0c;从底层开始将二分的序列归并排序为有序序列&#xff1b; #include <iostream> #…

手工测试VS自动化测试到底那个更胜一筹?

手工与自动化只是一种形式&#xff0c;真正的核心是测试用例、业务模型和测试分析。当企业的产品规模开始膨胀的时候&#xff0c;尤其是产品迭代加快是不是能及时得到测试验证支持是很重要的。这些靠手工测试是基本无法实现的&#xff0c;手工测试会严重的拖慢产品进度&#xf…

快速排序和qsort函数详解详解qsort函数

&#x1f495;是非成败转头空&#xff0c;青山依旧在&#xff0c;几度夕阳红&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;快速排序和qsort函数详解 前言&#xff1a; 我们之前学习过冒泡排序&#xff0c;冒泡排序尽管很方便&#xff0c;但也存在一些局限性…

类图的6种关系和golang应用

文章目录 1. 依赖和关联1.1 依赖&#xff08;Dependency&#xff09;概念类图示例代码示例 1.2 关联&#xff08;Association&#xff09;概念类图示例代码示例 2. 组合和聚合&#xff08;特殊的关联关系&#xff09;2.1 聚合&#xff08;Aggregation&#xff09;概念类图示例代…

nginx编译以及通过自定义生成证书配置https

1. 环境准备 1.1 软件安装 nginx安装编译安装以及配置https&#xff0c;需要gcc-c pcre-devel openssl openssl-devel软件。因此需要先安装相关软件。 yum -y install gcc-c pcre-devel openssl openssl-devel wgetopenssl/openssl-devel&#xff1a;主要用于nginx编译的htt…

Unity限制在一个范围内移动

Unity限制在一个范围内移动 这个例子中&#xff0c;我们学习Vector3.ClampMagnitude的用法&#xff0c;限制小球在范围内移动。 在地图上放了一个小球&#xff0c;让他移动&#xff0c;但是不想让他掉下去&#xff0c;限制在一个球星范围内&#xff0c;就好像绳子拴住了一样&…

MySQL流程控制(二十八)

二八佳人体似酥&#xff0c;腰悬利剑斩愚夫&#xff0c;虽然不见人头落,暗里教君骨髓枯。 上一章简单介绍了MySQL变量(二十七) ,如果没有看过,请观看上一章 一. 定义条件与处理程序 定义条件是事先定义程序执行过程中可能遇到的问题&#xff0c;处理程序定义了在遇到问题时应…

跨境B2B2C多用户购物网站源码快速部署

​ 搭建跨境B2B2C多用户购物网站需要以下步骤&#xff1a; 1. 确定业务模式和定位&#xff1a;确定网站的业务模式&#xff0c;包括跨境B2B2C的商业模式以及目标用户定位。 2. 营业执照和域名注册&#xff1a;根据当地法律要求&#xff0c;注册一家具有法人资格的公司&#xff…

java Springboot02--Controller,文件上传,拦截器

因为前后端分离了&#xff0c;所以这个项目基本用不到controller 这句话意思&#xff1a; controller只能用get接受前端的请求 RequestMapping(value "/hello",method RequestMethod.GET) GetMapping("/hello") 这两句等价的 前段传递参数&#xff0…

炸裂,靠“吹牛”过京东一面,月薪40K

说在前面 在40岁老架构师尼恩的&#xff08;50&#xff09;读者社区中&#xff0c;经常有小伙伴&#xff0c;需要面试美团、京东、阿里、 百度、头条等大厂。 下面是一个5年小伙伴成功拿到通过了京东一面面试&#xff0c;并且最终拿到offer&#xff0c;月薪40K。 现在把面试…