个人作业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>
posted @ 2025-03-31 20:31  vivi_vimi  阅读(18)  评论(0)    收藏  举报