- 创建MainActivity
package org.lzy.shop.activity
import android.view.KeyEvent
import androidx.core.view.get
import androidx.fragment.app.Fragment
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.navigation.NavigationBarView
import org.lzy.shop.R
import org.lzy.shop.adapter.CommonsViewPageAdapter
import org.lzy.shop.base.BaseActivity
import org.lzy.shop.cart.FragmentCart
import org.lzy.shop.databinding.ActivityMainBinding
import org.lzy.shop.fragment.ClassifyFragment
import org.lzy.shop.fragment.HomeFragment
import org.lzy.shop.fragment.MineFragment
class MainActivity : BaseActivity<ActivityMainBinding>({ ActivityMainBinding.inflate(it) }) {
private val fragments: ArrayList<Fragment> = ArrayList()
private var lastTimeBackPressed: Long = 0
override fun initView() {
// 初始化Fragment列表
fragments.add(HomeFragment())
fragments.add(ClassifyFragment())
fragments.add(FragmentCart())
fragments.add(MineFragment())
// 设置ViewPager
binding.viewPager.adapter = CommonsViewPageAdapter(this, fragments)
binding.viewPager.offscreenPageLimit = fragments.size
binding.viewPager.isUserInputEnabled = false // 禁用滑动切换
// 设置底部导航
binding.bottomNavigation.setOnItemSelectedListener {
when (it.itemId) {
R.id.home -> binding.viewPager.currentItem = 0
R.id.category -> binding.viewPager.currentItem = 1
R.id.cart -> binding.viewPager.currentItem = 2
R.id.mine -> binding.viewPager.currentItem = 3
}
true
}
}
override fun initData() {
// 初始化数据
}
override fun allClick() {
// 处理点击事件
}
// 双击返回键退出应用
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if (keyCode == KeyEvent.KEYCODE_BACK) {
val currentTime = System.currentTimeMillis()
if (currentTime - lastTimeBackPressed < 2000) {
finish()
return true
}
lastTimeBackPressed = currentTime
showToast(getString(R.string.double_click_exit))
return true
}
return super.onKeyDown(keyCode, event)
}
}
ViewPager适配器
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
class CommonsViewPageAdapter(fragmentActivity: FragmentActivity, private val fragments: ArrayList<Fragment>) :
FragmentStateAdapter(fragmentActivity) {
override fun getItemCount(): Int {
return fragments.size
}
override fun createFragment(position: Int): Fragment {
return fragments[position]
}
}
实现首页模块
class HomeFragment : BaseFragment<FragmentHomeBinding>({ FragmentHomeBinding.inflate(it) }) {
private val viewModel by lazy { getViewModel(HomeViewModel::class.java) }
private lateinit var bannerAdapter: HomeBannerAdapter
private lateinit var categoryAdapter: HomeCategoryAdapter
private lateinit var goodsAdapter: HomeGoodsAdapter
override fun initView() {
// 初始化轮播图
bannerAdapter = HomeBannerAdapter()
binding.vpBanner.adapter = bannerAdapter
// 初始化分类列表
categoryAdapter = HomeCategoryAdapter()
binding.rvCategory.layoutManager = androidx.recyclerview.widget.GridLayoutManager(context, 5)
binding.rvCategory.adapter = categoryAdapter
// 初始化商品列表
goodsAdapter = HomeGoodsAdapter()
binding.rvGoods.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context)
binding.rvGoods.adapter = goodsAdapter
// 设置刷新监听
binding.refreshLayout.setOnRefreshListener {
loadData()
}
}
override fun initData() {
loadData()
// 设置商品点击事件
setupCategoryAllClickListener()
}
private fun loadData() {
lifecycleScope.launch {
// 加载轮播图数据
viewModel.loadBannerData()
// 加载分类数据
viewModel.loadCategoryData()
// 加载推荐商品数据
viewModel.loadRecommendGoods()
}
}
/**
* 设置全部商品点击事件
*/
private fun setupCategoryAllClickListener() {
goodsAdapter.setOnItemClickListener { adapter, _, position ->
val goods = adapter.getItem(position)
goods?.let {
navigateToGoodsDetail(it.id, it.mainPic)
}
}
}
private fun navigateToGoodsDetail(goodsId: Int, goodsPic: String) {
val bundle = Bundle()
bundle.putInt("GOODID", goodsId)
bundle.putString("GOODPIC", goodsPic)
startActivity(GoodInfoActivity::class.java, bundle)
}
override fun allClick() {
// 搜索框点击
binding.searchBar.setOnClickListener {
startActivity(SearchActivity::class.java)
}
}
}
购物车:
// CartItem.kt
import kotlinx.serialization.Serializable
import org.lzy.shop.response.GoodsSpecResponse
@Serializable
data class CartItem(
// 商品ID
val goodsId: Int,
// 商品名称
val goodsName: String,
// 商品主图
val goodsMainPic: String,
// 规格列表
val spec: List<GoodsSpecResponse> = mutableListOf(),
// 添加数量字段,默认为1
var quantity: Int = 1,
// 是否选中,用于购物车结算
var isSelected: Boolean = false
)
第五步:实现首页模块
1. 创建HomeFragment
Kotlin
// HomeFragment.kt
package org.lzy.shop.fragment
import android.os.Bundle
import android.view.View
import androidx.lifecycle.lifecycleScope
import com.scwang.smart.refresh.layout.api.RefreshLayout
import com.scwang.smart.refresh.layout.constant.RefreshState
import kotlinx.coroutines.launch
import org.lzy.shop.R
import org.lzy.shop.adapter.HomeBannerAdapter
import org.lzy.shop.adapter.HomeCategoryAdapter
import org.lzy.shop.adapter.HomeGoodsAdapter
import org.lzy.shop.base.BaseFragment
import org.lzy.shop.databinding.FragmentHomeBinding
import org.lzy.shop.viewmodel.HomeViewModel
class HomeFragment : BaseFragment<FragmentHomeBinding>({
FragmentHomeBinding.inflate(it) }) {
private val viewModel by lazy { getViewModel
(HomeViewModel::class.java) }
private lateinit var bannerAdapter: HomeBannerAdapter
private lateinit var categoryAdapter: HomeCategoryAdapter
private lateinit var goodsAdapter: HomeGoodsAdapter
override fun initView() {
// 初始化轮播图
bannerAdapter = HomeBannerAdapter()
binding.vpBanner.adapter = bannerAdapter
// 初始化分类列表
categoryAdapter = HomeCategoryAdapter()
binding.rvCategory.layoutManager = androidx.recyclerview.
widget.GridLayoutManager(context, 5)
binding.rvCategory.adapter = categoryAdapter
// 初始化商品列表
goodsAdapter = HomeGoodsAdapter()
binding.rvGoods.layoutManager = androidx.recyclerview.widget.
LinearLayoutManager(context)
binding.rvGoods.adapter = goodsAdapter
// 设置刷新监听
binding.refreshLayout.setOnRefreshListener {
loadData()
}
}
override fun initData() {
loadData()
// 设置商品点击事件
setupCategoryAllClickListener()
}
private fun loadData() {
lifecycleScope.launch {
// 加载轮播图数据
viewModel.loadBannerData()
// 加载分类数据
viewModel.loadCategoryData()
// 加载推荐商品数据
viewModel.loadRecommendGoods()
}
}
/**
* 设置全部商品点击事件
*/
private fun setupCategoryAllClickListener() {
goodsAdapter.setOnItemClickListener { adapter, _, position ->
val goods = adapter.getItem(position)
goods?.let {
navigateToGoodsDetail(it.id, it.mainPic)
}
}
}
private fun navigateToGoodsDetail(goodsId: Int, goodsPic:
String) {
val bundle = Bundle()
bundle.putInt("GOODID", goodsId)
bundle.putString("GOODPIC", goodsPic)
startActivity(GoodInfoActivity::class.java, bundle)
}
override fun allClick() {
// 搜索框点击
binding.searchBar.setOnClickListener {
startActivity(SearchActivity::class.java)
}
}
}
第六步:实现购物车功能
1. 创建购物车数据模型
Kotlin
// CartItem.kt
package org.lzy.shop.cart
import kotlinx.serialization.Serializable
import org.lzy.shop.response.GoodsSpecResponse
@Serializable
data class CartItem(
// 商品ID
val goodsId: Int,
// 商品名称
val goodsName: String,
// 商品主图
val goodsMainPic: String,
// 规格列表
val spec: List<GoodsSpecResponse> = mutableListOf(),
// 添加数量字段,默认为1
var quantity: Int = 1,
// 是否选中,用于购物车结算
var isSelected: Boolean = false
)
2. 创建购物车管理器
class ShoppingCartManager private constructor(context: Context) {
private val database: CartDatabase = CartDatabase.getDatabase(context)
private val cartItemDao: CartItemDao = database.cartItemDao()
private val goodsSpecDao: GoodsSpecDao = database.goodsSpecDao()
companion object {
// 单例模式
@Volatile
private var instance: ShoppingCartManager? = null
fun getInstance(context: Context): ShoppingCartManager {
if (instance == null) {
synchronized(ShoppingCartManager::class.java) {
if (instance == null) {
instance = ShoppingCartManager(context)
}
}
}
return instance!!
}
}
/**
* 添加商品到购物车
*/
suspend fun addToCart(cartItem: CartItem, quantity: Int = 1): Boolean {
return try {
withContext(Dispatchers.IO) {
// 检查是否已存在相同商品
val existingItem = cartItemDao.getCartItemByGoodsId(cartItem.goodsId)
if (existingItem != null) {
// 更新数量
cartItemDao.updateQuantity(existingItem.id, existingItem.quantity + quantity)
} else {
// 新建购物车项
val cartItemEntity = CartItemEntity(
goodsId = cartItem.goodsId,
goodsName = cartItem.goodsName,
goodsMainPic = cartItem.goodsMainPic,
quantity = quantity,
isSelected = true
)
val cartItemId = cartItemDao.insert(cartItemEntity).toInt()
// 添加规格信息
for (spec in cartItem.spec) {
val goodsSpecEntity = GoodsSpecEntity(
goodsId = spec.goodsId,
name = spec.name,
price = spec.price,
stock = spec.stock,
sortNum = spec.sortNum,
images = spec.images,
cartItemId = cartItemId
)
goodsSpecDao.insert(goodsSpecEntity)
}
}
}
true
} catch (e: Exception) {
e.printStackTrace()
false
}
}
/**
* 从购物车移除商品
*/
suspend fun removeFromCart(position: Int): Boolean {
return try {
withContext(Dispatchers.IO) {
val cartItems = cartItemDao.getAllCartItems()
if (position >= 0 && position < cartItems.size) {
val cartItem = cartItems[position]
// 先删除关联的规格信息
goodsSpecDao.deleteByCartItemId(cartItem.id)
// 再删除购物车项
cartItemDao.delete(cartItem)
}
}
true
} catch (e: Exception) {
e.printStackTrace()
false
}
}
/**
* 获取所有购物车商品
*/
suspend fun getCartItems(): List<CartItem> {
return withContext(Dispatchers.IO) {
val cartItemEntities = cartItemDao.getAllCartItems()
val cartItems = mutableListOf<CartItem>()
for (entity in cartItemEntities) {
val specs = goodsSpecDao.getSpecsByCartItemId(entity.id)
cartItems.add(entity.toCartItem(specs))
}
cartItems
}
}
- 创建购物车Fragment
// FragmentCart.kt
class FragmentCart : BaseFragment<FragmentCartBinding>({ FragmentCartBinding.inflate(it) }) {
private val cartAdapter by lazy { CartAdapter() }
private val cartManager by lazy { ShoppingCartManager.getInstance(requireContext()) }
private var isEditMode = false
override fun initView() {
// 初始化购物车列表
binding.rvCartItems.layoutManager = LinearLayoutManager(requireContext())
binding.rvCartItems.adapter = cartAdapter
// 设置适配器回调
cartAdapter.setCartItemCallback(
onQuantityChanged = { originalPosition, quantity ->
lifecycleScope.launch {
cartManager.updateCartItemQuantity(originalPosition, quantity)
updateCheckoutInfo()
}
},
onItemDeleted = { originalPosition ->
lifecycleScope.launch {
// 从数据库中删除商品
cartManager.removeFromCart(originalPosition)
// 更新结算信息
updateCheckoutInfo()
// 如果购物车为空,更新界面状态
if (cartManager.isCartEmpty()) {
updateCartList()
}
}
}
)
// 全选按钮点击事件
binding.cbSelectAll.setOnCheckedChangeListener { _, isChecked ->
lifecycleScope.launch {
cartManager.selectAllItems(isChecked)
updateCartList()
updateCheckoutInfo()
}
}
// 编辑按钮点击事件
binding.tvEdit.setOnClickListener {
isEditMode = !isEditMode
updateEditMode()
}
// 结算/删除按钮点击事件
binding.btnCheckout.setOnClickListener {
lifecycleScope.launch {
val selectedCount = cartManager.getSelectedItemCount()
if (selectedCount <= 0) {
return@launch
}
if (isEditMode) {
// 删除选中的商品
val selectedItems = cartManager.getSelectedCartItems()
if (selectedItems.isEmpty()) {
return@launch
}
// 实现删除逻辑
} else {
// 结算逻辑
val selectedItems = cartManager.getSelectedCartItems()
if (selectedItems.isNotEmpty()) {
val bundle = Bundle()
// 传递选中的商品信息
bundle.putString("SELECTED_ITEMS", Gson().toJson(selectedItems))
// 启动订单提交页面
startActivity(SubmitOrderActivity::class.java, bundle)
}
}
}
}
// 去逛逛按钮点击事件
binding.btnGoShopping.setOnClickListener {
// 跳转到首页
val mainActivity = activity as? MainActivity
mainActivity?.let {
it.binding.viewPager.currentItem = 0 // 切换到HomeFragment
}
}
}
override fun onResume() {
super.onResume()
// 刷新购物车数据
lifecycleScope.launch {
updateCartList()
updateCheckoutInfo()
}
}
private suspend fun updateCartList() {
val cartItems = cartManager.getCartItems()
if (cartItems.isEmpty()) {
// 显示空购物车界面
binding.llEmptyCart.visibility = View.VISIBLE
binding.rvCartItems.visibility = View.GONE
binding.bottomLayout.visibility = View.GONE
} else {
// 显示购物车列表
binding.llEmptyCart.visibility = View.GONE
binding.rvCartItems.visibility = View.VISIBLE
binding.bottomLayout.visibility = View.VISIBLE
cartAdapter.setList(cartItems)
}
}
private suspend fun updateCheckoutInfo() {
val selectedCount = cartManager.getSelectedItemCount()
val totalPrice = cartManager.getTotalPrice()
binding.tvTotalPrice.text = getString(R.string.price_format, totalPrice)
binding.btnCheckout.text = if (isEditMode) {
getString(R.string.delete_count, selectedCount)
} else {
getString(R.string.checkout_count, selectedCount)
}
}
private fun updateEditMode() {
if (isEditMode) {
binding.tvEdit.text = getString(R.string.complete)
binding.btnCheckout.text = getString(R.string.delete)
} else {
binding.tvEdit.text = getString(R.string.edit)
lifecycleScope.launch {
updateCheckoutInfo()
}
}
}
}
个人中心
class MineFragment : BaseFragment<FragmentMineBinding>({ FragmentMineBinding.inflate(it) }) {
private val viewModel by lazy { getViewModel(MineModel::class.java) }
override fun initView() {
// 设置头像
setUrlCircle(DataStoreUtils.getString(Constant.USER_AVATAR), binding.ivAvatar)
// 设置用户名
val nickname = DataStoreUtils.getString(Constant.USER_NICKNAME)
binding.tvUserName.text = if (TextUtils.isEmpty(nickname)) {
getString(R.string.please_click_login)
} else {
nickname
}
}
override fun initData() {
// 检查登录状态
checkLoginStatus()
}
private fun checkLoginStatus() {
val token = DataStoreUtils.getString(Constant.TOKEN)
if (!TextUtils.isEmpty(token)) {
// 已登录状态,加载用户信息
lifecycleScope.launch {
viewModel.getUserInfo()
}
}
}
override fun allClick() {
// 用户信息区域点击
binding.userInfoContainer.setOnClickListener {
if (isTokenValid()) {
startActivity(PersonInfoActivity::class.java)
} else {
startActivity(LoginActivity::class.java)
}
}
// 待付款订单点击
binding.orderItem1.setOnClickListener {
navigateWithTokenCheck(OrderListActivity::class.java, Bundle().apply {
putInt("ORDER_TYPE", 0)
})
}
// 待发货订单点击
binding.orderItem2.setOnClickListener {
navigateWithTokenCheck(OrderListActivity::class.java, Bundle().apply {
putInt("ORDER_TYPE", 1)
})
}
// 设置按钮点击
binding.settingItem.setOnClickListener {
navigateWithTokenCheck(SettingActivity::class.java)
}
}
override fun onResume() {
super.onResume()
// 刷新用户信息
initView()
}
}
登录注册功能
class LoginActivity : BaseMvvmActivity<ActivityLoginBinding, LoginViewModel>({ ActivityLoginBinding.inflate(it) }) {
override fun initView() {
}
override fun initData() {
// 观察登录结果
mViewModel.loginLiveData.observeWithLifecycle(this) {\ loginResult ->
if (loginResult != null) {
// 登录成功,保存token
DataStoreUtils.setString(Constant.TOKEN, loginResult.token)
DataStoreUtils.setString(Constant.MOBILE, binding.etPhone.text.toString())
showToast(getString(R.string.login_success))
finish()
}
}
}
override fun allClick() {
// 登录按钮点击
binding.btnLogin.setOnClickListener {
val phone = binding.etPhone.text.toString()
val password = binding.etPassword.text.toString()
if (TextUtils.isEmpty(phone)) {
showToast(getString(R.string.please_input_phone))
return@setOnClickListener
}
if (TextUtils.isEmpty(password)) {
showToast(getString(R.string.please_input_password))
return@setOnClickListener
}
// 执行登录
mViewModel.login(phone, password)
}
// 注册按钮点击
binding.tvRegister.setOnClickListener {
startActivity(RegisterActivity::class.java)
}
}
}