You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin RecyclerView添加条目异常:新条目覆盖旧条目而非追加

问题分析与解决方案

核心问题

每次添加新收支条目时,旧条目被覆盖的原因是:

  1. MainActivity每次启动都会重新创建空的data列表,之前的记录未被保存
  2. 跳转回MainActivity时启动了新实例,而非复用原有实例
  3. 余额直接用新金额替换,未做累加/扣除计算

一、修复MainActivity的数据逻辑与跳转方式

将数据列表改为全局变量,用Activity Result API接收返回数据,维护全局余额变量:

package com.example.financestation

import android.content.Intent
import android.os.Bundle
import android.widget.ImageButton
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView

class MainActivity : AppCompatActivity() {

    // 全局存储所有收支记录
    private val data = ArrayList<ItemsViewModel>()
    private lateinit var adapter: CustomAdapter
    private var currentBalance = 0.0 // 维护当前余额

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val addBtn = findViewById<ImageButton>(R.id.add_button)
        val balanceTv = findViewById<TextView>(R.id.current_balance_number)
        val settingsBtn = findViewById<ImageButton>(R.id.settings_button)
        val recyclerView = findViewById<RecyclerView>(R.id.recyclerview)

        // 初始化RecyclerView
        recyclerView.layoutManager = LinearLayoutManager(this)
        adapter = CustomAdapter(data)
        recyclerView.adapter = adapter

        // 初始化余额显示
        updateBalanceDisplay(balanceTv)

        // 跳转到添加页面
        addBtn.setOnClickListener {
            val intent = Intent(this, AddingSpendings::class.java)
            startActivityForResult(intent, 1001)
        }

        settingsBtn.setOnClickListener {
            // 补充设置页面跳转逻辑
        }
    }

    // 接收添加页面返回的数据
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 1001 && resultCode == RESULT_OK) {
            val costStr = data?.getStringExtra("COST") ?: return
            val category = data.getStringExtra("CATEGORY") ?: return
            val type = data.getStringExtra("TYPE") ?: return

            val cost = costStr.toDoubleOrNull() ?: return

            // 更新余额与数据列表
            when (type) {
                "Income" -> {
                    currentBalance += cost
                    this.data.add(ItemsViewModel(R.drawable.add_icon, "$${"%.2f".format(cost)}", category))
                }
                "Outcome" -> {
                    currentBalance -= cost
                    this.data.add(ItemsViewModel(R.drawable.substract_icon, "-$${"%.2f".format(cost)}", category))
                }
            }

            // 更新UI
            updateBalanceDisplay(findViewById(R.id.current_balance_number))
            adapter.notifyItemInserted(this.data.size - 1)
        }
    }

    // 统一更新余额显示
    private fun updateBalanceDisplay(textView: TextView) {
        textView.text = "$${"%.2f".format(currentBalance)}"
    }
}

二、修改CustomAdapter支持可变列表

将构造参数改为MutableList,确保数据可修改:

package com.example.financestation

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

class CustomAdapter(private val mList: MutableList<ItemsViewModel>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.card_view_design, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = mList[position]
        holder.imageView.setImageResource(item.image)
        holder.textView.text = item.cost
        holder.textView2.text = item.category
    }

    override fun getItemCount(): Int {
        return mList.size
    }

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val imageView: ImageView = itemView.findViewById(R.id.imageview)
        val textView: TextView = itemView.findViewById(R.id.textView)
        val textView2: TextView = itemView.findViewById(R.id.categories)
    }
}

三、修复AddingSpendings的返回逻辑

用setResult返回数据,关闭当前页面而非启动新的MainActivity:

package com.example.financestation

import android.content.Intent
import android.os.Bundle
import android.widget.*
import androidx.appcompat.app.AppCompatActivity

class AddingSpendings : AppCompatActivity() {

    private lateinit var costEt: EditText
    private var selectedCategory = ""
    private var selectedType = ""

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.addingspending)

        val homeBtn = findViewById<ImageButton>(R.id.home_button)
        val addItemBtn = findViewById<Button>(R.id.add_item_button)
        val categorySpinner = findViewById<Spinner>(R.id.categoryspinner)
        val typeSpinner = findViewById<Spinner>(R.id.typespinner)
        val categories = resources.getStringArray(R.array.Category)
        val types = resources.getStringArray(R.array.Income_Outcome)
        costEt = findViewById(R.id.cost)

        // 初始化类别Spinner
        val categoryAdapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, categories)
        categorySpinner.adapter = categoryAdapter
        categorySpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
                selectedCategory = categories[p2]
            }

            override fun onNothingSelected(p0: AdapterView<*>?) {
                Toast.makeText(this@AddingSpendings, "未选择类别", Toast.LENGTH_SHORT).show()
            }
        }

        // 初始化类型Spinner
        val typeAdapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, types)
        typeSpinner.adapter = typeAdapter
        typeSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
            override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
                selectedType = types[p2]
            }

            override fun onNothingSelected(p0: AdapterView<*>?) {
                Toast.makeText(this@AddingSpendings, "未选择类型", Toast.LENGTH_SHORT).show()
            }
        }

        // 添加条目按钮逻辑
        addItemBtn.setOnClickListener {
            val costStr = costEt.text.toString().trim()
            when {
                costStr.isEmpty() -> Toast.makeText(this, "请输入金额", Toast.LENGTH_SHORT).show()
                selectedCategory.isEmpty() -> Toast.makeText(this, "请选择类别", Toast.LENGTH_SHORT).show()
                selectedType.isEmpty() -> Toast.makeText(this, "请选择类型", Toast.LENGTH_SHORT).show()
                else -> {
                    // 返回数据给MainActivity
                    val resultIntent = Intent().apply {
                        putExtra("COST", costStr)
                        putExtra("CATEGORY", selectedCategory)
                        putExtra("TYPE", selectedType)
                    }
                    setResult(RESULT_OK, resultIntent)
                    finish()
                }
            }
        }

        // 返回主页按钮逻辑
        homeBtn.setOnClickListener {
            finish()
        }
    }
}

额外优化建议

  1. 数据持久化:当前数据仅存于内存,重启App会丢失,可使用SharedPreferences或Room数据库保存记录
  2. 输入校验:增加金额非负校验、格式校验
  3. 状态恢复:在MainActivity的onSaveInstanceState中保存余额和数据列表,避免横竖屏切换丢失数据

内容的提问来源于stack exchange,提问作者Adam Bołoz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 21:57:11