Android开发实战设计并实现一个汽车销售APP,要求至少有汽车列表页、汽车详情页、贷款计算页3个界面。
已开源:https://github.com/yan123666/wlf
实现结果:
1.计算金额界面
package com.car.car
import android.os.Bundle
import android.view.View
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
/**
* 计算金额界面
*/
class CalculateActivity : AppCompatActivity() {
private var dividel:Boolean=true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_calculate_wlf)
//获取传递过来车辆信息
var carBean:CarBean = intent.getSerializableExtra("car") as CarBean
//显示车辆价格
var et_price = findViewById<TextView>(R.id.et_price)
et_price.setText("¥"+(carBean.price!!.toDouble()*10000))
//初始化布局
var seek_DisCount_Percent = findViewById<SeekBar>(R.id.seek_DisCount_Percent)
var tv_DisCount_Account = findViewById<TextView>(R.id.tv_DisCount_Account)
var tv_Total = findViewById<TextView>(R.id.tv_Total)
var tv_percent = findViewById<TextView>(R.id.tv_percent)
var rg = findViewById<RadioGroup>(R.id.rg)
var rb_no = findViewById<RadioButton>(R.id.rb_no)
var rb_yes = findViewById<RadioButton>(R.id.rb_yes)
var tv_DownPayment = findViewById<TextView>(R.id.tv_DownPayment)
var tv_Loan = findViewById<TextView>(R.id.tv_Loan)
var sp_Periods = findViewById<Spinner>(R.id.sp_Periods)
var tv_period = findViewById<TextView>(R.id.tv_period)
//给下拉框添加监听事件 选择分期时间
sp_Periods.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(adapterView: AdapterView<*>, view: View, i: Int, l: Long) {
//判断是否分期 ,如果分期 计算
if (dividel){
//首付费用 分期费用各一半
tv_DownPayment.setText("¥"+(carBean.price!!.toDouble()*10000/2))
//分期费用
tv_Loan.setText("¥"+(carBean.price!!.toDouble()*10000/2))
//6 个月 12个月 24个月 每期的费用
if (i==0){
tv_period.setText("¥"+(carBean.price!!.toDouble()*10000/2/6))
}else if (i==1){
tv_period.setText("¥"+(carBean.price!!.toDouble()*10000/2/12))
}else if (i==2){
tv_period.setText("¥"+(carBean.price!!.toDouble()*10000/2/24))
}
}
}
override fun onNothingSelected(adapterView: AdapterView<*>?) {}
}
//是否分期切换监听
rg.setOnCheckedChangeListener { group, checkedId ->
//分期
if (checkedId == R.id.rb_yes) {
dividel = true
tv_DownPayment.setText("¥"+(carBean.price!!.toDouble()*10000/2))
tv_Loan.setText("¥"+(carBean.price!!.toDouble()*10000/2))
val i=sp_Periods.selectedItemPosition //当前选中期数
//每期费用
if (i==0){
tv_period.setText("¥"+(carBean.price!!.toDouble()*10000/2/6))
}else if (i==1){
tv_period.setText("¥"+(carBean.price!!.toDouble()*10000/2/12))
}else if (i==2){
tv_Loan.setText("¥"+(carBean.price!!.toDouble()*10000/2/24))
}
} else {
//不分期
dividel = false
tv_DownPayment.setText("")
tv_Loan.setText("")
//每期费用
tv_period.setText("")
}
}
/**
* 监听优惠力度 当优惠力度发生改变的时候 实际总的金额也变化
*/
seek_DisCount_Percent.setOnSeekBarChangeListener(object :SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
tv_percent.setText(""+(progress)+"%")
tv_Total.setText("¥"+(carBean.price!!.toDouble()*10000-carBean.price!!.toDouble()*10000*progress/100))
tv_DisCount_Account.setText("¥"+(carBean.price!!.toDouble()*10000*progress/100))
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
}
})
}
}
2.车辆列表适配器
package com.car.car
import android.content.Context
import android.view.View
import android.widget.BaseAdapter
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import java.util.ArrayList
/**
* 车辆列表适配器
*/
class CarAdapter(context: Context, mData: MutableList<CarBean>) : BaseAdapter() {
private var data = mData
private var mContext=context;
override fun getCount(): Int {
return data.size
}
override fun getItem(position: Int): Any {
return data[position]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
val convertView = View.inflate(mContext, R.layout.item_layout_wlf, null)
var car = this.data[position]
/**
* 初始化控件
*/
val img=convertView.findViewById<ImageView>(R.id.iv_img) //车辆图片
val tv_title=convertView.findViewById<TextView>(R.id.tv_title) //车辆名
val tv_mileage=convertView.findViewById<TextView>(R.id.tv_mileage) //公里数
val tv_price=convertView.findViewById<TextView>(R.id.tv_price) //车辆价格
//车辆图片
img.setImageResource(car.img)
//车辆名字
tv_title.text = car.name
//车辆公里数
tv_mileage.text = car.describe;
//车辆价格
tv_price.text = car.price+"万";
return convertView
}
}
3.车辆类
package com.car.car
import java.io.Serializable
/**
* 车辆类
*/
class CarBean(id: Int, type: String, name: String, price: String, describe: String, pic: Int) : Serializable {
var id = id //车辆ID
var type: String? = type //车辆类型
var name: String? = name //车辆名
var price: String? = price //车辆价格
var describe: String? = describe //公里数描述
var img = pic //车辆图片
}
4.数据库操作类
package com.car.car
import android.annotation.SuppressLint
import android.content.ContentValues
import android.content.Context
import android.database.Cursor
import android.database.SQLException
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
/**
* sqlite 数据库操作类
*/
class DatabaseUtil
(
private val mCtx: Context
) {
private var mDbHelper: DatabaseHelper? = null
private var mDb: SQLiteDatabase? = null
private class DatabaseHelper constructor(context: Context?) :
SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
override fun onCreate(db: SQLiteDatabase) {
//创建表
db.execSQL(CREATE_TABLE)
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
}
}
/**
* 打开数据库
*
* @return instance of DatabaseUtil
* @throws SQLException
*/
@Throws(SQLException::class)
fun open(): DatabaseUtil {
mDbHelper = DatabaseHelper(mCtx)
mDb = mDbHelper!!.writableDatabase
return this
}
/**
* This method is used for closing the connection.
*/
fun close() {
mDbHelper!!.close()
}
/**
* 插入数据
*
* @param name
* @param grade
* @return long
*/
fun insert(type: String?, name: String?, price: String?, describe: String?, img:Int): Long {
val initialValues = ContentValues()
initialValues.put("type", type);
initialValues.put("name", name);
initialValues.put("price", price);
initialValues.put("describe", describe);
initialValues.put("img", img);
return mDb!!.insert(DATABASE_TABLE, null, initialValues)
}
/**
* 查询全部数据
*/
@SuppressLint("Range")
fun queryAllPersonData(): List<CarBean>? {
//查询全部数据
val cursor: Cursor =
mDb!!.query(DATABASE_TABLE, null, null, null, null, null, null, null)
val list: MutableList<CarBean> = ArrayList()
if (cursor.count > 0) {
//移动到首位
cursor.moveToFirst()
for (i in 0 until cursor.count) {
val id = cursor.getInt(cursor.getColumnIndex("id"))
val type = cursor.getString(cursor.getColumnIndex("type"))
val name = cursor.getString(cursor.getColumnIndex("name"))
val price = cursor.getString(cursor.getColumnIndex("price"))
val describe = cursor.getString(cursor.getColumnIndex("describe"))
val pic = cursor.getInt(cursor.getColumnIndex("img"))
val model = CarBean(id,type,name,price,describe,pic)
list.add(model)
//移动到下一位
cursor.moveToNext()
}
}
cursor.close()
mDb!!.close()
return list
}
/**
* This method will deleteAll record.
*
* @return
*/
fun deleteAll(): Boolean {
return mDb!!.delete(DATABASE_TABLE, " 1 ", null) > 0
}
companion object {
private const val TAG = "DatabaseUtil"
/**
* Database Name 数据库名
*/
private const val DATABASE_NAME = "car_data"
/**
* Database Version 数据库版本
*/
private const val DATABASE_VERSION = 1
/**
* Table Name 数据库表名
*/
private const val DATABASE_TABLE = "tb_car"
/**
* Table columns 数据库字段名
*/
const val KEY_ID = "id"
const val KEY_TYPE = "type"
const val KEY_NAME = "name"
const val KEY_PRICE = "price"
const val KEY_DESCRIBE = "describe"
const val KEY_IMG = "img"
/**
* Database creation sql statement
*/
private const val CREATE_TABLE =
("create table " + DATABASE_TABLE + " (" + KEY_ID + " integer primary key autoincrement, "
+ KEY_TYPE + " text not null, "
+ KEY_NAME + " text not null, "
+ KEY_PRICE + " text not null, "
+ KEY_DESCRIBE + " text not null, "
+ KEY_IMG + " integer not null);")
}
}
5.车辆详情界面
package com.car.car
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
/**
* 车辆详情界面
*/
class DetailsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_details_wlf)
//获取传递数据
var carBean:CarBean = intent.getSerializableExtra("car") as CarBean
//初始化布局
var iv_img = findViewById<ImageView>(R.id.iv_img)
var tv_detail_msg = findViewById<TextView>(R.id.tv_detail_msg)
var bt_buy = findViewById<Button>(R.id.bt_buy)
//显示数据 图片显示和详情
iv_img.setImageResource(carBean.img)
tv_detail_msg.setText(carBean.name+carBean.describe+carBean.price+"万")
//跳转到计算界面
bt_buy.setOnClickListener {
val intent = Intent(this, CalculateActivity::class.java);
intent.putExtra("car", carBean)
startActivity(intent)
}
}
}
6.车辆列表界面
package com.car.car
import android.content.Intent
import android.os.Bundle
import android.widget.AdapterView
import android.widget.ListView
import androidx.appcompat.app.AppCompatActivity
/**
* 车辆列表界面
*/
class MainActivity : AppCompatActivity() {
private lateinit var listView: ListView
private var carAdapter: CarAdapter?=null
private var data= mutableListOf<CarBean>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main_wlf)
listView = findViewById<ListView>(R.id.lv)
initData()
//跳转到详情界面
listView.onItemClickListener =
AdapterView.OnItemClickListener { adapterView, view, i, l ->
//传递车辆信息
val intent = Intent(this, DetailsActivity::class.java);
intent.putExtra("car", data[i])
startActivity(intent)
}
}
/**
* 初始化数据
*/
private fun initData() {
//数据库开启
val dbUtil=DatabaseUtil(this);
dbUtil.open()
//查询数据库里是否存在数据
var queryAllPersonData = dbUtil.queryAllPersonData()
//存在数据
if (null!=queryAllPersonData&&queryAllPersonData.size>0){
data= queryAllPersonData as MutableList<CarBean>
//初始化是脾气
carAdapter = CarAdapter(this,data)
//设置适配器
listView.adapter=carAdapter
}else{
//不存在 插入3个默认数据
dbUtil.open()
//插入数据
dbUtil.insert("轿车","小鹏汽车P7 2022款","18.5","2023年(700公里)",R.mipmap.img1);
dbUtil.insert("轿车","保时捷 2021款","158.5","2023年(2700公里)",R.mipmap.img2);
dbUtil.insert("轿车","别克E5 2023款","20.5","2023年(500公里)",R.mipmap.img3);
//再次查询数据库 确认查询成功 设置适配器现实数据
var queryAllPersonData1 = dbUtil.queryAllPersonData()
data= queryAllPersonData1 as MutableList<CarBean>
carAdapter = CarAdapter(this,data)
listView.adapter=carAdapter
}
}
}
界面设计:
1.金额计算页面:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
android:padding="20sp"
tools:context=".CalculateActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center_vertical"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:text="Price"
android:layout_height="wrap_content"></TextView>
<EditText
android:id="@+id/et_price"
android:layout_width="0dp"
android:gravity="right|center_vertical"
android:layout_weight="1"
android:layout_height="match_parent"></EditText>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center_vertical"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:text="DisCount Percent"
android:layout_height="wrap_content"></TextView>
<SeekBar
android:id="@+id/seek_DisCount_Percent"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"></SeekBar>
<TextView
android:id="@+id/tv_percent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></TextView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center_vertical"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:text="DisCount Account"
android:layout_height="wrap_content"></TextView>
<TextView
android:id="@+id/tv_DisCount_Account"
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="right|center_vertical"
android:layout_height="match_parent"></TextView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center_vertical"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:text="Total"
android:layout_height="wrap_content"></TextView>
<TextView
android:id="@+id/tv_Total"
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="right|center_vertical"
android:layout_height="match_parent"></TextView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_marginTop="40dp"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:text="Dividel?"
android:layout_gravity="center_vertical"
android:layout_height="wrap_content"></TextView>
<RadioGroup
android:id="@+id/rg"
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/rb_no"
android:layout_width="wrap_content"
android:text="No"
android:layout_height="wrap_content"></RadioButton>
<RadioButton
android:id="@+id/rb_yes"
android:layout_width="wrap_content"
android:text="Yes"
android:checked="true"
android:layout_height="wrap_content"></RadioButton>
</RadioGroup>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center_vertical"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:text="DownPayment"
android:layout_height="wrap_content"></TextView>
<TextView
android:id="@+id/tv_DownPayment"
android:layout_width="0dp"
android:gravity="right|center_vertical"
android:layout_weight="1"
android:layout_height="match_parent"></TextView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center_vertical"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:text="Loan"
android:layout_height="wrap_content"></TextView>
<TextView
android:id="@+id/tv_Loan"
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="right|center_vertical"
android:layout_height="match_parent"></TextView>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center_vertical"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:text="Periods"
android:layout_height="wrap_content"></TextView>
<Spinner
android:id="@+id/sp_Periods"
android:layout_width="0dp"
android:gravity="right"
android:layout_marginLeft="20sp"
android:entries="@array/phones_array"
android:layout_weight="1"
android:layout_height="match_parent"></Spinner>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:gravity="center_vertical"
android:orientation="horizontal"
>
<TextView
android:layout_width="wrap_content"
android:text="Per period"
android:layout_height="wrap_content"></TextView>
<TextView
android:id="@+id/tv_period"
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="right|center_vertical"
android:layout_height="match_parent"></TextView>
</LinearLayout>
</LinearLayout>
2.立即购买控件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20dp"
tools:context=".DetailsActivity">
<ImageView
android:id="@+id/iv_img"
android:layout_width="match_parent"
android:layout_height="wrap_content"></ImageView>
<TextView
android:id="@+id/tv_detail_msg"
android:layout_width="match_parent"
android:textColor="#DD306B"
android:layout_marginTop="20sp"
android:textSize="14sp"
android:layout_height="wrap_content"></TextView>
<Button
android:id="@+id/bt_buy"
android:layout_width="wrap_content"
android:text="立即购买"
android:layout_marginTop="20dp"
android:layout_gravity="center_horizontal|bottom"
android:layout_height="wrap_content"></Button>
</LinearLayout>
3.车辆展示页面
<?xml version="1.0" encoding="utf-8"?>
<ListView 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:layout_width="match_parent"
android:id="@+id/lv"
android:layout_height="match_parent"
tools:context=".MainActivity">
</ListView>
4.布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv_img"
android:layout_width="120dp"
android:layout_height="120dp"></ImageView>
<LinearLayout
android:layout_width="match_parent"
android:orientation="vertical"
android:paddingLeft="10dp"
android:layout_height="120dp">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:gravity="center_vertical"
android:textColor="#1677C5"
android:textSize="16sp"
android:layout_height="45dp"></TextView>
<TextView
android:id="@+id/tv_mileage"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:textColor="#505151"
android:gravity="center_vertical"
android:textSize="12sp"
></TextView>
<TextView
android:id="@+id/tv_price"
android:layout_width="match_parent"
android:layout_height="0dp"
android:textColor="#131313"
android:gravity="center_vertical"
android:textSize="18sp"
android:layout_weight="1"
></TextView>
</LinearLayout>
</LinearLayout>
配置文件:
strings.xml
<resources>
<string name="app_name">汽车销售APP</string>
<string-array name = "phones_array">
<item>6 months</item>
<item>12 months</item>
<item>24 months</item>
</string-array>
</resources>
themes.xml
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.Car" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>
themes.xml
详细项目正在发布到开源社区ing