个人作业Android学习系统开发日志五
Android实现:
DailySummary:
entity:
package com.example.learningmanagement.entity
data class DailySummary(
val id: Int,
val userId: Int,
val url: String = ""
)
adapter:
package com.example.learningmanagement.adapter
import android.content.Intent
import android.net.Uri
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.learningmanagement.R
import com.example.learningmanagement.entity.DailySummary
class DailySummaryAdapter : ListAdapter<DailySummary, DailySummaryAdapter.ViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_daily_summary, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val dailySummary = getItem(position)
holder.bind(dailySummary)
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val tvUrl: TextView = itemView.findViewById(R.id.tvUrl)
fun bind(dailySummary: DailySummary) {
tvUrl.text = dailySummary.url
// 设置点击事件,打开URL
itemView.setOnClickListener {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(dailySummary.url))
itemView.context.startActivity(intent)
}
}
}
class DiffCallback : DiffUtil.ItemCallback<DailySummary>() {
override fun areItemsTheSame(oldItem: DailySummary, newItem: DailySummary): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: DailySummary, newItem: DailySummary): Boolean {
return oldItem == newItem
}
}
}
service:
package com.example.learningmanagement.service
import com.example.learningmanagement.entity.ApiResponse
import com.example.learningmanagement.entity.DailySummary
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.Path
interface DailySummaryService {
@GET("daily/selectAll/{userId}")
suspend fun getDailySummaries(@Path("userId") userId: String): ApiResponse<List<DailySummary>>
@POST("daily/add")
suspend fun addDailySummary(@Body dailySummary: DailySummary): ApiResponse<DailySummary>
}
fragment:
package com.example.learningmanagement.ui.fragment
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.learningmanagement.R
import com.example.learningmanagement.adapter.DailySummaryAdapter
import com.example.learningmanagement.databinding.FragmentDailySummaryBinding
import com.example.learningmanagement.entity.DailySummary
import com.example.learningmanagement.network.ServiceCreater
import com.example.learningmanagement.service.DailySummaryService
import com.example.learningmanagement.ui.MainActivity
import com.example.learningmanagement.utils.PrefsHelper
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
class DailySummaryFragment : Fragment() {
private var _binding: FragmentDailySummaryBinding? = null
private val binding get() = _binding!!
private val dailySummaryService = ServiceCreater.create<DailySummaryService>()
private val dailySummaryAdapter = DailySummaryAdapter()
private lateinit var prefs: PrefsHelper
override fun onAttach(context: Context) {
super.onAttach(context)
prefs = PrefsHelper(context)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentDailySummaryBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupRecyclerView()
setupFab()
loadDailySummaries()
// 更新标题
(activity as? MainActivity)?.updateWeekTitle("日总结")
}
private fun setupRecyclerView() {
binding.rvDailySummaries.apply {
layoutManager = LinearLayoutManager(context)
adapter = dailySummaryAdapter
}
}
private fun setupFab() {
binding.fabAddDailySummary.setOnClickListener {
showAddDailySummaryDialog()
}
}
private fun showAddDailySummaryDialog() {
val dialogView = layoutInflater.inflate(R.layout.dialog_add_daily_summary, null)
val etUrl = dialogView.findViewById<EditText>(R.id.etUrl)
AlertDialog.Builder(requireContext())
.setTitle("添加日总结")
.setView(dialogView)
.setPositiveButton("保存") { _, _ ->
val url = etUrl.text.toString().trim()
if (url.isEmpty()) {
showToast("URL不能为空")
return@setPositiveButton
}
// 保存日总结
saveDailySummary(url)
}
.setNegativeButton("取消", null)
.show()
}
private fun saveDailySummary(url: String) {
lifecycleScope.launch {
try {
val userId = prefs.getUserId()
userId?.let { id ->
val dailySummary = DailySummary(
id = 0, // 后端会自动生成ID
userId = id.toInt(),
url = url
)
val response = dailySummaryService.addDailySummary(dailySummary)
if (response.code == "200") {
showToast("添加成功")
loadDailySummaries() // 刷新列表
} else {
showToast("添加失败: ${response.msg}")
}
} ?: run {
showToast("用户未登录")
}
} catch (e: Exception) {
showToast("网络错误: ${e.message}")
}
}
}
private fun loadDailySummaries() {
if (!isAdded) return
lifecycleScope.launch {
try {
val userId = prefs.getUserId()
userId?.let { id ->
try {
if (!isActive) return@launch
val response = dailySummaryService.getDailySummaries(id)
if (!isActive || !isAdded || _binding == null) return@launch
if (response.code == "200") {
response.data?.let { summaries ->
if (summaries.isNotEmpty()) {
// 有数据时显示列表
dailySummaryAdapter.submitList(summaries)
binding.rvDailySummaries.visibility = View.VISIBLE
binding.tvEmptyData.visibility = View.GONE
} else {
// 数据为空时显示提示
dailySummaryAdapter.submitList(emptyList())
showEmptyView()
}
} ?: run {
// 响应数据为null
dailySummaryAdapter.submitList(emptyList())
showEmptyView()
}
} else {
// 请求成功但状态码不是200
showToast("获取数据失败: ${response.msg}")
showEmptyView()
}
} catch (e: CancellationException) {
// 协程被取消,正常情况,不需要处理
} catch (e: Exception) {
if (!isActive || !isAdded || _binding == null) return@launch
val errorMessage = "网络请求错误: ${e.javaClass.simpleName} - ${e.message}"
showToast(errorMessage)
showEmptyView()
}
} ?: run {
if (!isActive || !isAdded || _binding == null) return@launch
showToast("用户未登录")
showEmptyView()
}
} catch (e: CancellationException) {
// 协程被取消,正常情况,不需要处理
} catch (e: Exception) {
if (!isActive || !isAdded || _binding == null) return@launch
val errorMessage = "加载失败: ${e.javaClass.simpleName} - ${e.message}"
showToast(errorMessage)
showEmptyView()
}
}
}
private fun showEmptyView() {
if (!isAdded || _binding == null) return
binding.tvEmptyData.visibility = View.VISIBLE
binding.rvDailySummaries.visibility = View.GONE
}
private fun showToast(message: String) {
if (isAdded && activity != null) {
activity?.let {
Toast.makeText(it, message, Toast.LENGTH_SHORT).show()
}
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
fun refreshData() {
if (isAdded && _binding != null) {
loadDailySummaries()
}
}
}
xml:
<?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="vertical"
android:padding="16dp">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp">
<EditText
android:id="@+id/etUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="URL地址"
android:inputType="textUri" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvDailySummaries"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvEmptyData"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="暂无数据"
android:textSize="18sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fabAddDailySummary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:contentDescription="添加日总结"
android:src="@android:drawable/ic_input_add"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<?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_margin="8dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/tvUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:textColor="@color/purple_500"
android:textSize="16sp" />
</LinearLayout>
</androidx.cardview.widget.CardView>

浙公网安备 33010602011771号