购票系统
实现功能
开发手机购票软件,界面显示内容包括起点站,终点站,购票数量,收费规则:地铁每三站收费一元,总站数除三余站不足三站也按照一元收费,起点站不算。填写信息后显示金额,如果金额正确点击购票按钮并显示购票成功

完成代码:
架构:

具体代码:
Line
点击查看代码
package com.qi.demo.model
data class Line(
val id: Int,
val name: String,
val stations: List<Station>
)
点击查看代码
package com.qi.demo.model
data class Station(
val name: String,
val index: Int,
val lineId: Int,
val transferTo: List<Int> = emptyList()
)
点击查看代码
package com.qi.demo
import android.os.Bundle
import android.widget.*
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.qi.demo.model.Line
import com.qi.demo.model.Station
class MainActivity : AppCompatActivity() {
private lateinit var spinnerStart: Spinner
private lateinit var spinnerEnd: Spinner
private lateinit var editTextTickets: EditText
private lateinit var textViewAmount: TextView
private lateinit var buttonCalculate: Button
private lateinit var buttonBuy: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
initViews()
setupSpinners()
setupListeners()
}
private fun initViews() {
spinnerStart = findViewById(R.id.spinnerStart)
spinnerEnd = findViewById(R.id.spinnerEnd)
editTextTickets = findViewById(R.id.editTextTickets)
textViewAmount = findViewById(R.id.textViewAmount)
buttonCalculate = findViewById(R.id.buttonCalculate)
buttonBuy = findViewById(R.id.buttonBuy)
}
private val line1 = Line(1, "1号线", listOf(
Station("西王", 0, 1),
Station("时光街", 1, 1),
Station("长城桥", 2, 1),
Station("和平医院", 3, 1),
Station("烈士陵园", 4, 1),
Station("新百广场", 5, 1, listOf(3)),
Station("解放广场", 6, 1),
Station("平安大街", 7, 1),
Station("北国商城", 8, 1, listOf(2)),
Station("博物院", 9, 1),
Station("体育场", 10, 1),
Station("北宋", 11, 1),
Station("谈固", 12, 1),
Station("朝晖桥", 13, 1),
Station("白佛", 14, 1),
Station("留村", 15, 1),
Station("火炬广场", 16, 1),
Station("石家庄东站", 17, 1),
Station("南村", 18, 1),
Station("洨河大道", 19, 1),
Station("西庄", 20, 1),
Station("东庄", 21, 1),
Station("会展中心", 22, 1),
Station("商务中心", 23, 1),
Station("园博园", 24, 1),
Station("福泽", 25, 1)
))
private val line2 = Line(2, "2号线", listOf(
Station("柳辛庄", 0, 2),
Station("庄窠·铁道大学", 1, 2),
Station("义堂", 2, 2),
Station("建和桥", 3, 2),
Station("长安公园", 4, 2),
Station("北国商城", 5, 2, listOf(1)),
Station("裕华路", 6, 2),
Station("槐中路", 7, 2),
Station("欧韵公园", 8, 2),
Station("元村", 9, 2),
Station("石家庄", 10, 2, listOf(3)),
Station("塔坛", 11, 2),
Station("仓丰路留村", 12, 2),
Station("南位", 13, 2),
Station("嘉华路", 14, 2)
))
private val line3 = Line(3, "3号线", listOf(
Station("西三庄", 0, 3),
Station("高柱", 1, 3),
Station("柏林庄", 2, 3),
Station("市庄", 3, 3),
Station("市二中", 4, 3),
Station("新百广场", 5, 3, listOf(1)),
Station("东里", 6, 3),
Station("槐安桥", 7, 3),
Station("西三教", 8, 3),
Station("石家庄", 9, 3, listOf(2)),
Station("汇通路", 10, 3),
Station("孙村", 11, 3),
Station("塔冢", 12, 3),
Station("东王", 13, 3),
Station("南王", 14, 3),
Station("位同", 15, 3),
Station("东二环南路", 16, 3),
Station("西仰陵", 17, 3),
Station("中仰陵", 18, 3),
Station("南豆", 19, 3),
Station("太行南大街", 20, 3),
Station("乐乡", 21, 3)
))
private val allLines = listOf(line1, line2, line3)
private val allStations = allLines.flatMap { it.stations }
private fun calculateFare() {
val startStation = allStations[spinnerStart.selectedItemPosition]
val endStation = allStations[spinnerEnd.selectedItemPosition]
val ticketCount = editTextTickets.text.toString().toIntOrNull() ?: 0
if (startStation.name == endStation.name) {
Toast.makeText(this, "起点站和终点站不能相同", Toast.LENGTH_SHORT).show()
return
}
val stationCount = calculateStationCount(startStation, endStation)
val farePerTicket = (stationCount + 2) / 3
val totalFare = farePerTicket * ticketCount
textViewAmount.text = "总金额:${totalFare}元"
buttonBuy.isEnabled = true
}
private fun calculateStationCount(start: Station, end: Station): Int {
// 如果在同一条线路上
if (start.lineId == end.lineId) {
return Math.abs(start.index - end.index)
}
// 需要换乘的情况
var minStations = Int.MAX_VALUE
// 遍历所有可能的换乘路径
for (transfer1 in allStations.filter { it.transferTo.isNotEmpty() && it.lineId == start.lineId }) {
// 直接换乘到终点线路
if (transfer1.transferTo.contains(end.lineId)) {
val count = Math.abs(start.index - transfer1.index) +
Math.abs(end.index - allStations.find {
it.name == transfer1.name && it.lineId == end.lineId
}?.index!! ?: 0)
minStations = minOf(minStations, count)
}
// 需要二次换乘
for (transfer2 in allStations.filter {
it.transferTo.isNotEmpty() &&
it.lineId == end.lineId &&
it.name != transfer1.name
}) {
// 找到中间线路的换乘站
val midTransfer1 = allStations.find {
it.name == transfer1.name &&
transfer1.transferTo.contains(it.lineId)
}
val midTransfer2 = allStations.find {
it.name == transfer2.name &&
transfer2.transferTo.contains(midTransfer1?.lineId ?: -1)
}
if (midTransfer1 != null && midTransfer2 != null) {
val count = Math.abs(start.index - transfer1.index) +
Math.abs(midTransfer1.index - midTransfer2.index) +
Math.abs(end.index - transfer2.index)
minStations = minOf(minStations, count)
}
}
}
return if (minStations == Int.MAX_VALUE) 0 else minStations
}
private fun setupSpinners() {
val adapter = ArrayAdapter(
this,
android.R.layout.simple_spinner_item,
allStations.map { "${it.name}(${allLines[it.lineId-1].name})" }
)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinnerStart.adapter = adapter
spinnerEnd.adapter = adapter
}
private fun setupListeners() {
buttonCalculate.setOnClickListener {
calculateFare()
}
buttonBuy.setOnClickListener {
Toast.makeText(this, "购票成功!", Toast.LENGTH_SHORT).show()
resetForm()
}
}
private fun resetForm() {
spinnerStart.setSelection(0)
spinnerEnd.setSelection(0)
editTextTickets.setText("")
textViewAmount.text = ""
buttonBuy.isEnabled = false
}
}
点击查看代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">
<Spinner
android:id="@+id/spinnerStart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
app:layout_constraintTop_toTopOf="parent" />
<Spinner
android:id="@+id/spinnerEnd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
app:layout_constraintTop_toBottomOf="@id/spinnerStart" />
<EditText
android:id="@+id/editTextTickets"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:hint="请输入购票数量"
android:inputType="number"
app:layout_constraintTop_toBottomOf="@id/spinnerEnd" />
<TextView
android:id="@+id/textViewAmount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/editTextTickets" />
<Button
android:id="@+id/buttonCalculate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="计算金额"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textViewAmount" />
<Button
android:id="@+id/buttonBuy"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:text="购买车票"
android:enabled="false"
app:layout_constraintStart_toEndOf="@id/buttonCalculate"
app:layout_constraintTop_toTopOf="@id/buttonCalculate" />
</androidx.constraintlayout.widget.ConstraintLayout>
浙公网安备 33010602011771号