我正在用 Kotlin + XML 制作一个应用程序,但 RecyclerView 有问题:
我有两个字段 etPerson 和 etContribution,还有一个 btnAdd 按钮将该人添加到参与者列表中,并更新 rvParticipants 视图以显示更新后的列表。当第 7 个参与者进入时,问题就出现了; rvParticipants 列表通过重复第一行被覆盖。
MainActivity.kt
MainActivity 类:AppCompatActivity() {
// Variables for payment calculation
private var totalPayment = 0.0
private var paymentPerPerson = 0.0
// List of participants
private val participantList = mutableListOf<Participant>()
// Views
private lateinit var etExpenditure: EditText
private lateinit var etNPersons: EditText
private lateinit var tvPayment: TextView
private lateinit var etPersonName: EditText
private lateinit var etContribution: EditText
private lateinit var tvOwe: TextView
private lateinit var rvParticipants: RecyclerView
// Adapter for the participant list
private lateinit var participantAdapter: ParticipantAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Initialize views
initializeViews()
// Setup RecyclerView and adapter
setupRecyclerView()
// Configure "Add" button
findViewById<Button>(R.id.btnAdd).setOnClickListener {
addParticipant()
}
// Configure "Calculate" button
findViewById<Button>(R.id.btnCalculate).setOnClickListener {
calculatePayment()
}
// Configure TextWatcher for etExpenditure
etExpenditure.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
// No implementation required
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
// No implementation required
}
override fun afterTextChanged(s: Editable?) {
calculatePayment()
}
})
// Configure TextWatcher for etNPersons
etNPersons.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
// No implementation required
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
// No implementation required
}
override fun afterTextChanged(s: Editable?) {
calculatePayment()
}
})
}
// Method to initialize views
private fun initializeViews() {
etExpenditure = findViewById(R.id.etExpenditure)
etNPersons = findViewById(R.id.etNPersons)
tvPayment = findViewById(R.id.tvPayment)
etPersonName = findViewById(R.id.etPersonName)
etContribution = findViewById(R.id.etContribution)
tvOwe = findViewById(R.id.tvOwe)
rvParticipants = findViewById(R.id.rvParticipants)
}
// Method to setup RecyclerView and adapter
private fun setupRecyclerView() {
participantAdapter = ParticipantAdapter(participantList, paymentPerPerson)
rvParticipants.adapter = participantAdapter
rvParticipants.layoutManager = LinearLayoutManager(this)
}
// Method to add a participant
private fun addParticipant() {
val name = etPersonName.text.toString()
val contribution = etContribution.text.toString().toDoubleOrNull()
if (name.isNotEmpty() && contribution != null) {
val participant = Participant(name, contribution)
participantList.add(participant)
participantAdapter.notifyItemInserted(participantList.size - 1)
etPersonName.text.clear()
etContribution.text.clear()
calculateDebts()
} else {
// Show error if the name is empty or the contribution is not valid
}
}
// Method to calculate payments and debts
private fun calculatePayment() {
val expenditure = etExpenditure.text.toString().toDoubleOrNull()
val persons = etNPersons.text.toString().toIntOrNull()
if (expenditure != null && persons != null) {
totalPayment = expenditure
paymentPerPerson = totalPayment / persons
tvPayment.text = "$" + String.format("%.2f", paymentPerPerson)
calculateDebts()
} else {
// Show error if the expenditure or the number of persons is not valid
tvPayment.text = ""
participantList.forEach { participant ->
participant.totalContribution = 0.0
}
participantAdapter.updatePaymentPerPerson(0.0)
participantAdapter.notifyDataSetChanged()
}
}
// Method to calculate debts for each participant
private fun calculateDebts() {
val nPersons = etNPersons.text.toString().toInt()
participantList.forEach { participant ->
participant.totalContribution = paymentPerPerson - participant.contribution
}
participantAdapter.updatePaymentPerPerson(paymentPerPerson)
participantAdapter.notifyDataSetChanged()
}
}
**ParticipantAdapter.kt**
类 ParticipantAdapter(
私人 val 参与者:MutableList,
private var paymentPerPerson:双倍
) : RecyclerView.Adapter
// ViewHolder class to hold references to the views of each list item
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val tvName: TextInputEditText = itemView.findViewById(R.id.tvName)
val tvContribution: TextInputEditText = itemView.findViewById(R.id.tvContribution)
val tvOwe: TextInputEditText = itemView.findViewById(R.id.tvOwe)
val btnDelete: Button = itemView.findViewById(R.id.btnDelete)
val tvOweLayout: TextInputLayout = itemView.findViewById(R.id.tvOweLayout)
val tvNameLayout: TextInputLayout = itemView.findViewById(R.id.tvNameLayout)
}
// Method called when a new ViewHolder is created
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
// Inflate the layout of the list item
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_participant, parent, false)
return ViewHolder(view)
}
// Method called when a ViewHolder is bound to the data at a specific position
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val participant = participants[position]
// Set the values of the TextViews
holder.tvName.setText(participant.name)
holder.tvContribution.setText(participant.contribution.toString())
holder.tvOwe.setText(participant.totalContribution.toString())
// Set the font type to bold
holder.tvName.setTypeface(null, Typeface.BOLD)
holder.tvContribution.setTypeface(null, Typeface.BOLD)
holder.tvOwe.setTypeface(null, Typeface.BOLD)
// Get the text color and background color of tvOwe
val textColor = getOweTextColor(participant.totalContribution)
val backgroundColor = getOweBackgroundColor(participant.totalContribution)
// Set the text color and background color of tvOwe
holder.tvOwe.setTextColor(textColor)
holder.tvOweLayout.boxBackgroundColor = backgroundColor
// Set the background color of tvName
updateNameBackgroundColor(holder, backgroundColor)
// Configure the delete button
holder.btnDelete.setOnClickListener {
participants.removeAt(position)
notifyItemRemoved(position)
}
// Configure the TextWatcher for tvContribution
holder.tvContribution.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
// No implementation required
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
// No implementation required
}
override fun afterTextChanged(s: Editable?) {
val contribution = s.toString().toDoubleOrNull() ?: 0.0
participant.contribution = contribution
participant.totalContribution = calculateTotalContribution(participant)
holder.tvOwe.setText(participant.totalContribution.toString())
val textColor = getOweTextColor(participant.totalContribution)
val backgroundColor = getOweBackgroundColor(participant.totalContribution)
holder.tvOwe.setTextColor(textColor)
holder.tvOweLayout.boxBackgroundColor = backgroundColor
updateNameBackgroundColor(holder, backgroundColor)
}
})
// Configure the TextWatcher for tvName
holder.tvName.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
// No implementation required
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
// No implementation required
}
override fun afterTextChanged(s: Editable?) {
val name = s.toString()
participant.name = name
}
})
}
// Method that returns the number of items in the list
override fun getItemCount(): Int {
return participants.size
}
// Method to get the text color of tvOwe based on the total contribution
private fun getOweTextColor(totalContribution: Double): Int {
return when {
totalContribution == 0.0 -> Color.BLUE
totalContribution > 0.0 -> Color.RED
totalContribution < 0.0 -> Color.parseColor("#004D00") // Change the green color value here
else -> Color.TRANSPARENT
}
}
// Method to get the background color of tvOwe based on the total contribution
private fun getOweBackgroundColor(totalContribution: Double): Int {
return when {
totalContribution == 0.0 -> Color.parseColor("#CCCCCC")
totalContribution > 0.0 -> Color.parseColor("#FFD4D4")
totalContribution < 0.0 -> Color.parseColor("#E2FFD7")
else -> Color.TRANSPARENT
}
}
// Method to update the background color of tvName based on the background color of tvOwe
private fun updateNameBackgroundColor(holder: ViewHolder, backgroundColor: Int) {
holder.tvNameLayout.boxBackgroundColor = backgroundColor
}
// Method to add a participant to the list
fun addParticipant(participant: Participant) {
participants.add(participant)
notifyDataSetChanged()
}
// Method to update the value of paymentPerPerson
fun updatePaymentPerPerson(payment: Double) {
paymentPerPerson = payment
notifyDataSetChanged()
}
// Method to calculate the total contribution of a participant
private fun calculateTotalContribution(participant: Participant): Double {
return paymentPerPerson - participant.contribution
}
}
在 addParticipant 方法中,您可以替换
participantAdapter.notifyItemInserted(participantList.size - 1)
到
participantAdapter.notifyDataSetChanged()
这只是使其工作的简单方法,使用带有大列表的notifyDataSetChanged将使您的代码变慢。要使用notifyItemInserted(),您需要更多地控制索引位置