app/src/main/java/com/example/firstapp/MainActivity.kt
@@ -66,10 +66,10 @@ // logout() // } // 在此位置初始化 homeViewModel homeViewModel = ViewModelProvider(this).get(HomeViewModel::class.java) val navView: BottomNavigationView = binding.navView // homeViewModel = ViewModelProvider(this).get(HomeViewModel::class.java) // // val navView: BottomNavigationView = binding.navView val navView = binding.navView val navController = findNavController(R.id.nav_host_fragment_activity_main) // Passing each menu ID as a set of Ids because each // menu should be considered as top level destinations. @@ -91,34 +91,34 @@ registerSmsReceiver() syncRecentSms() } val recyclerView = findViewById<RecyclerView>(R.id.recyclerView) recyclerView.layoutManager = LinearLayoutManager(this) // val recyclerView = findViewById<RecyclerView>(R.id.recyclerView) // recyclerView.layoutManager = LinearLayoutManager(this) // // // 初始化适配器 // adapter = MyAdapter() // recyclerView.adapter = adapter // // // 观察 LiveData 数据 // homeViewModel.codeList.observe(this) { codeList -> // // 如果 codeList 为 null,避免闪退 // if (codeList != null) { // adapter.submitList(codeList) // // 滚动到顶部 // recyclerView.scrollToPosition(0) // } else { // // 如果数据为空,可以显示空列表或其他处理 // Toast.makeText(this, "No data available", Toast.LENGTH_SHORT).show() // } // } // 初始化适配器 adapter = MyAdapter() recyclerView.adapter = adapter // 观察 LiveData 数据 homeViewModel.codeList.observe(this) { codeList -> // 如果 codeList 为 null,避免闪退 if (codeList != null) { adapter.submitList(codeList) // 滚动到顶部 recyclerView.scrollToPosition(0) } else { // 如果数据为空,可以显示空列表或其他处理 Toast.makeText(this, "No data available", Toast.LENGTH_SHORT).show() } } // 注册广播接收器来监听数据更新 val filter = IntentFilter("com.example.firstapp.DATA_UPDATED") registerReceiver(object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { // 数据已更新,刷新 LiveData homeViewModel.loadData() } }, filter) // // 注册广播接收器来监听数据更新 // val filter = IntentFilter("com.example.firstapp.DATA_UPDATED") // registerReceiver(object : BroadcastReceiver() { // override fun onReceive(context: Context, intent: Intent) { // // 数据已更新,刷新 LiveData // homeViewModel.loadData() // } // }, filter) } app/src/main/java/com/example/firstapp/adapter/ExpressAdapter.kt
对比新文件 @@ -0,0 +1,87 @@ package com.example.firstapp.adapter import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.ListAdapter import com.example.firstapp.databinding.ItemExpressGroupBinding import com.example.firstapp.databinding.ItemExpressPackageBinding import androidx.recyclerview.widget.DiffUtil import com.example.firstapp.model.ExpressGroup import com.example.firstapp.model.ExpressPackage class ExpressAdapter : ListAdapter<ExpressGroup, ExpressAdapter.ViewHolder>(ExpressGroupDiffCallback()) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val binding = ItemExpressGroupBinding.inflate( LayoutInflater.from(parent.context), parent, false ) return ViewHolder(binding) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val group = getItem(position) holder.bind(group) } class ViewHolder(private val binding: ItemExpressGroupBinding) : RecyclerView.ViewHolder(binding.root) { private val packagesAdapter = ExpressPackageAdapter() init { binding.rvPackages.apply { layoutManager = LinearLayoutManager(context) adapter = packagesAdapter } } fun bind(group: ExpressGroup) { binding.tvStationName.text = group.stationName binding.tvPackageCount.text = "共${group.packages.size}个包裹" packagesAdapter.submitList(group.packages) } } } class ExpressPackageAdapter : ListAdapter<ExpressPackage, ExpressPackageAdapter.ViewHolder>(ExpressPackageDiffCallback()) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val binding = ItemExpressPackageBinding.inflate( LayoutInflater.from(parent.context), parent, false ) return ViewHolder(binding) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val pack = getItem(position) holder.bind(pack) } class ViewHolder(private val binding: ItemExpressPackageBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(pack: ExpressPackage) { binding.tvCompany.text = pack.company binding.tvTrackingNumber.text = pack.trackingNumber binding.tvDate.text = pack.date } } } private class ExpressGroupDiffCallback : DiffUtil.ItemCallback<ExpressGroup>() { override fun areItemsTheSame(oldItem: ExpressGroup, newItem: ExpressGroup): Boolean { return oldItem.stationName == newItem.stationName } override fun areContentsTheSame(oldItem: ExpressGroup, newItem: ExpressGroup): Boolean { return oldItem == newItem } } private class ExpressPackageDiffCallback : DiffUtil.ItemCallback<ExpressPackage>() { override fun areItemsTheSame(oldItem: ExpressPackage, newItem: ExpressPackage): Boolean { return oldItem.trackingNumber == newItem.trackingNumber } override fun areContentsTheSame(oldItem: ExpressPackage, newItem: ExpressPackage): Boolean { return oldItem == newItem } } app/src/main/java/com/example/firstapp/adapter/ReminderAdapter.kt
@@ -5,7 +5,6 @@ import androidx.recyclerview.widget.RecyclerView import com.example.firstapp.database.entity.Reminder import com.example.firstapp.databinding.ItemReminderBinding import com.sun.mail.imap.protocol.FetchResponse.getItem class ReminderAdapter(private val onDelete: (Reminder) -> Unit) : ListAdapter<Reminder, ReminderAdapter.ReminderViewHolder>(ReminderDiffCallback()) { app/src/main/java/com/example/firstapp/database/dao/CodeDao.kt
@@ -41,4 +41,11 @@ @Query("SELECT * FROM Code order by time desc") abstract fun getAllCodesDesc(): List<Code> @Query(""" SELECT * FROM Code WHERE type LIKE '%' || :keyword || '%' ORDER BY time DESC """) fun getByKeyword(keyword: String): List<Code> } app/src/main/java/com/example/firstapp/database/dao/ReminderDao.kt
@@ -15,4 +15,7 @@ @Delete fun delete(reminder: Reminder) @Query("SELECT * FROM reminders WHERE type = :type") fun getByType(type: String): List<Reminder> } app/src/main/java/com/example/firstapp/database/repository/CodeRepository.kt
@@ -17,5 +17,9 @@ fun getAllDesc() = codeDao.getAllCodesDesc() @WorkerThread fun getByKeyword(keyword: String): List<Code> { return codeDao.getByKeyword(keyword) } } app/src/main/java/com/example/firstapp/database/repository/ReminderRepository.kt
@@ -18,4 +18,9 @@ fun delete(reminder: Reminder) { reminderDao.delete(reminder) } @WorkerThread fun getByType(type: String): List<Reminder> { return reminderDao.getByType(type) } } app/src/main/java/com/example/firstapp/model/ExpressGroup.kt
对比新文件 @@ -0,0 +1,12 @@ package com.example.firstapp.model data class ExpressGroup( val stationName: String, val packages: List<ExpressPackage> ) data class ExpressPackage( val company: String, val trackingNumber: String, val date: String ) app/src/main/java/com/example/firstapp/ui/home/HomeFragment.kt
@@ -10,7 +10,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.example.firstapp.R import com.example.firstapp.adapter.MyAdapter import com.example.firstapp.adapter.ExpressAdapter import com.example.firstapp.core.Core import com.example.firstapp.databinding.FragmentHomeBinding @@ -23,45 +23,73 @@ private val binding get() = _binding!! private lateinit var homeViewModel: HomeViewModel private lateinit var adapter: MyAdapter private lateinit var expressAdapter: ExpressAdapter // private lateinit var financeAdapter: FinanceAdapter // private lateinit var memorialAdapter: MemorialAdapter //onCreateView这个方法创建后被调用,通常是初始化视图组件和观察者 override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { homeViewModel = ViewModelProvider(this).get(HomeViewModel::class.java) _binding = FragmentHomeBinding.inflate(inflater, container, false) val root: View = binding.root return binding.root } // val textView: TextView = binding.textHome // homeViewModel.text.observe(viewLifecycleOwner) { // textView.text = it // } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) //通过 ViewModelProvider 获取 HomeViewModel 的实例,以便在视图中使用。 homeViewModel = ViewModelProvider(this).get(HomeViewModel::class.java) // 初始化适配器 adapter = MyAdapter() //调用这个方法来设置 RecyclerView用于设置 RecyclerView 的布局和适配器。 setupRecyclerViews() //调用这个方法来观察 ViewModel 中的数据变化 observeViewModelData() } // 获取数据 // val codeList = Core.code.getAllDesc() // 使用 binding 来访问 RecyclerView val recyclerView: RecyclerView = binding.recyclerView recyclerView.layoutManager = LinearLayoutManager(requireContext()) // 使用 requireContext() 获取上下文 recyclerView.adapter = adapter // 观察 LiveData,当数据发生变化时,更新 RecyclerView 的内容 homeViewModel.codeList.observe(viewLifecycleOwner) { codeList -> adapter.submitList(codeList) // 更新 RecyclerView 的数据 // 滚动到顶部 recyclerView.scrollToPosition(0) private fun setupRecyclerViews() { // 快递列表 //layoutManager = LinearLayoutManager(context):设置 RecyclerView 的布局管理器为线性布局管理器,表示列表是垂直排列的。 //创建一个 ExpressAdapter 的实例,用于提供 RecyclerView 的数据。 //将适配器设置给 RecyclerView,以便显示数据。 binding.expressRecycler.apply { layoutManager = LinearLayoutManager(context) expressAdapter = ExpressAdapter() adapter = expressAdapter } return root // // 财务列表 // binding.financeRecycler.apply { // layoutManager = LinearLayoutManager(context) // financeAdapter = FinanceAdapter() // adapter = financeAdapter // } // // // 纪念日列表 // binding.memorialRecycler.apply { // layoutManager = LinearLayoutManager(context) // memorialAdapter = MemorialAdapter() // adapter = memorialAdapter // } } //这个方法用于观察 homeViewModel 中的 expressItems 数据。 private fun observeViewModelData() { //当 expressItems 数据发生变化时,更新 RecyclerView 的数据。 homeViewModel.expressItems.observe(viewLifecycleOwner) { items -> //将新的数据列表提交给适配器,以更新 RecyclerView 的显示内容。 expressAdapter.submitList(items) } // homeViewModel.financeItems.observe(viewLifecycleOwner) { items -> // financeAdapter.submitList(items) // } // // homeViewModel.memorialItems.observe(viewLifecycleOwner) { items -> // memorialAdapter.submitList(items) // } } override fun onDestroyView() { app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt
@@ -3,34 +3,66 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.firstapp.core.Core import com.example.firstapp.database.entity.Code import com.example.firstapp.model.ExpressGroup import com.example.firstapp.model.ExpressPackage import kotlinx.coroutines.launch class HomeViewModel : ViewModel() { private val _text = MutableLiveData<String>().apply { value = "短信主页面" } val text: LiveData<String> = _text // private val _text = MutableLiveData<String>().apply { // value = "短信主页面" // } // val text: LiveData<String> = _text // // private val _codeList = MutableLiveData<List<Code>>() // // val codeList: LiveData<List<Code>> get() = _codeList private val _codeList = MutableLiveData<List<Code>>() val codeList: LiveData<List<Code>> get() = _codeList private val _expressItems = MutableLiveData<List<ExpressGroup>>() val expressItems: LiveData<List<ExpressGroup>> = _expressItems init { // 初始化时加载数据 loadData() // loadData() loadExpressData() } // 加载数据的方法 fun loadData() { // 获取数据,并更新 LiveData _codeList.value = Core.code.getAllDesc() // 假设这是获取最新的 data 的方法 } // fun loadData() { // // 获取数据,并更新 LiveData // _codeList.value = Core.code.getAllDesc() // 假设这是获取最新的 data 的方法 // } // // // 如果需要手动更新数据,可以调用这个方法 // fun updateData() { // _codeList.value = Core.code.getAllDesc() // 重新获取并更新数据 // } // 如果需要手动更新数据,可以调用这个方法 fun updateData() { _codeList.value = Core.code.getAllDesc() // 重新获取并更新数据 fun loadExpressData() { viewModelScope.launch { // 1. 获取所有驿站类型的提醒设置 val stations = Core.reminder.getByType("驿站") // 2. 按驿站分组获取包裹信息 val groups = stations.map { station -> val packages = Core.code.getByKeyword(station.nickname).map { code -> ExpressPackage( company = code.name, //快递公司 trackingNumber = code.code, // 取件码 date = code.overtime //时间 ) } ExpressGroup( stationName = station.nickname, packages = packages ) } _expressItems.postValue(groups) } } } app/src/main/res/drawable/location.png
app/src/main/res/layout/fragment_home.xml
@@ -1,30 +1,112 @@ <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView 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" tools:context=".ui.home.HomeFragment"> android:layout_height="match_parent"> <!-- <TextView--> <!-- android:id="@+id/text_home"--> <!-- android:layout_width="match_parent"--> <!-- android:layout_height="wrap_content"--> <!-- android:layout_marginStart="8dp"--> <!-- android:layout_marginTop="8dp"--> <!-- android:layout_marginEnd="8dp"--> <!-- android:textAlignment="center"--> <!-- android:textSize="20sp"--> <!-- app:layout_constraintBottom_toBottomOf="parent"--> <!-- app:layout_constraintEnd_toEndOf="parent"--> <!-- app:layout_constraintStart_toStartOf="parent"--> <!-- app:layout_constraintTop_toTopOf="parent" />--> <androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/recyclerView" <!-- LinearLayout的作用是按照垂直或者水平方向排列其子视图--> <!-- CardView组件是用于实现卡片式布局--> <!-- RecyclerView 回收商视图 它使用适配器(Adapter)来管理数据的显示,--> <!-- 开发者可以根据自己的需求实现适配器的方法,将数据与视图进行绑定。--> <!-- 这使得 RecyclerView 能够轻松地处理各种类型的数据,并按照自定义的布局方式展示。--> <!-- 支持局部刷新 通知数据集变化--> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:padding="8dp"/> android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> </androidx.constraintlayout.widget.ConstraintLayout> <!-- 我的快递板块 --> <androidx.cardview.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp" app:cardCornerRadius="8dp" app:cardElevation="2dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="我的快递" android:textColor="#333333" android:textSize="16sp" android:textStyle="bold"/> <androidx.recyclerview.widget.RecyclerView android:id="@+id/express_recycler" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" /> </LinearLayout> </androidx.cardview.widget.CardView> <!-- 我的财务板块 --> <androidx.cardview.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp" app:cardCornerRadius="8dp" app:cardElevation="2dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp" android:background="#E8F5E9"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/finance_recycler" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="我的财务" android:textColor="#333333" android:textSize="16sp" android:textStyle="bold"/> </LinearLayout> </androidx.cardview.widget.CardView> <!-- 纪念日板块 --> <androidx.cardview.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="16dp" app:cardCornerRadius="8dp" app:cardElevation="2dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp" android:background="#FFF3E0"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/memorial_recycler" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="纪念日" android:textColor="#333333" android:textSize="16sp" android:textStyle="bold"/> </LinearLayout> </androidx.cardview.widget.CardView> </LinearLayout> </ScrollView> app/src/main/res/layout/item_express_group.xml
对比新文件 @@ -0,0 +1,54 @@ <?xml version="1.0" encoding="utf-8"?> <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="12dp" android:layout_marginVertical="6dp" app:cardCornerRadius="8dp" app:cardElevation="2dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="16dp"> <!-- 驿站名称和包裹数量 --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_marginBottom="12dp"> <ImageView android:id="@+id/iv_station_icon" android:layout_width="20dp" android:layout_height="20dp" android:layout_gravity="center_vertical" android:src="@drawable/location"/> <TextView android:id="@+id/tv_station_name" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:layout_marginStart="8dp" android:textSize="16sp" android:textColor="#333333"/> <TextView android:id="@+id/tv_package_count" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="14sp" android:textColor="#666666"/> </LinearLayout> <!-- 包裹列表 --> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv_packages" android:layout_width="match_parent" android:layout_height="wrap_content"/> </LinearLayout> </androidx.cardview.widget.CardView> app/src/main/res/layout/item_express_package.xml
对比新文件 @@ -0,0 +1,46 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="12dp"> <ImageView android:id="@+id/iv_company_logo" android:layout_width="24dp" android:layout_height="24dp" android:layout_gravity="center_vertical" /> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="12dp" android:layout_weight="1" android:orientation="vertical"> <TextView android:id="@+id/tv_company" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#333333" android:textSize="15sp" /> <TextView android:id="@+id/tv_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textColor="#999999" android:textSize="12sp" /> </LinearLayout> <TextView android:id="@+id/tv_tracking_number" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:textColor="#666666" android:textSize="14sp" /> </LinearLayout> app/src/main/res/navigation/mobile_navigation.xml
@@ -5,6 +5,8 @@ android:id="@+id/mobile_navigation" app:startDestination="@+id/navigation_home"> <!-- 对应的多个片段--> <!-- tools:layout="@layout/fragment_home"表示设计视图中显示的应该是fragment_home.xml的内容,而不是当前布局的内容--> <fragment android:id="@+id/navigation_home" android:name="com.example.firstapp.ui.home.HomeFragment"