Android Kotlin:通过RecyclerView适配器添加并显示新列表项
问题分析与修正方案
我看了你的代码,发现几个核心问题导致添加的数据无法正确显示在RecyclerView中,下面逐一说明并给出修正后的代码:
1. Fragment中的数据与RecyclerView初始化问题
你当前在onActivityResult里每次都新建ArrayList<ItemsItem>(),这会导致之前添加的数据全部丢失,而且RecyclerView的初始化(设置布局管理器、适配器)也不应该每次返回都重复执行。应该把数据列表作为Fragment的成员变量,在onViewCreated里完成RecyclerView的基础配置。
修正后的Fragment代码:
class StepThreeShipmentFragment : Fragment() { private val REQUEST_CODE = 2 internal lateinit var adapter: ShipmentItemAdapter // 把items作为成员变量,持久保存所有数据 private val items = ArrayList<ItemsItem>() override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater!!.inflate(R.layout.fragment_step_three_shipment, container, false) } override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 初始化RecyclerView的基础配置,只执行一次 itemsShipments.layoutManager = LinearLayoutManager(context) itemsShipments.setHasFixedSize(true) adapter = ShipmentItemAdapter(context, items) itemsShipments.adapter = adapter // 初始状态根据数据量显示/隐藏RecyclerView itemsShipments.visibility = if (items.isEmpty()) View.GONE else View.VISIBLE btnAddNewShipmentStep3.setOnClickListener { val intent = Intent(context, AddItemActivity::class.java) startActivityForResult(intent, REQUEST_CODE) } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQUEST_CODE) { if (resultCode == Activity.RESULT_OK) { // 从Intent中获取返回的新数据 val newItem = data?.getParcelableExtra<ItemsItem>("NEW_ITEM") newItem?.let { items.add(it) // 通知适配器插入了新数据,避免刷新整个列表 adapter.notifyItemInserted(items.size - 1) itemsShipments.visibility = View.VISIBLE } } else if (resultCode == Activity.RESULT_CANCELED) { // 只有列表为空时才隐藏RecyclerView if (items.isEmpty()) { itemsShipments.visibility = View.GONE } } } } }
2. AddItemActivity的数据传递问题
你当前把新创建的ItemsItem直接加到了全局变量CreateNewShipmentActivity.mainShipment.shipmentObj.items里,但Fragment并没有监听这个变量的变化,也没有通过Intent把数据返回给Fragment。我们需要让ItemsItem实现Parcelable接口,这样才能在Intent中传递数据,同时拆分数据收集逻辑让代码更清晰。
第一步:让ItemsItem实现Parcelable
// 假设你的ItemsItem类结构如下,添加Parcelable实现以支持Intent传递 data class ItemsItem( var numberOfPackages: Int = 0, var numberOfPieces: String = "", var weightUnit: Int = 0, var height: String = "", var width: String = "", var length: String = "", var description: String = "" ) : Parcelable { constructor(parcel: Parcel) : this( parcel.readInt(), parcel.readString() ?: "", parcel.readInt(), parcel.readString() ?: "", parcel.readString() ?: "", parcel.readString() ?: "", parcel.readString() ?: "" ) override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeInt(numberOfPackages) parcel.writeString(numberOfPieces) parcel.writeInt(weightUnit) parcel.writeString(height) parcel.writeString(width) parcel.writeString(length) parcel.writeString(description) } override fun describeContents(): Int { return 0 } companion object CREATOR : Parcelable.Creator<ItemsItem> { override fun createFromParcel(parcel: Parcel): ItemsItem { return ItemsItem(parcel) } override fun newArray(size: Int): Array<ItemsItem?> { return arrayOfNulls(size) } } }
修正后的AddItemActivity代码:
class AddItemActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_add_item_form) // 保留点击输入框获取焦点的逻辑 linPackageAddShipment.setOnClickListener { edtPackageAddShipment.requestFocus() } linPiecesAddShipment.setOnClickListener { edtPiecesAddShipment.requestFocus() } linWeightAddShipment.setOnClickListener { edtWeightAddShipment.requestFocus() } linDescriptionAddShipment.setOnClickListener { edtDescriptionAddShipment.requestFocus() } btnAddItemAddShipment.setOnClickListener { val errors = collectValidationErrors() if (errors.isEmpty()) { val newItem = collectItemData() // 把新数据通过Intent返回给Fragment val intent = Intent().apply { putExtra("NEW_ITEM", newItem) } setResult(Activity.RESULT_OK, intent) // 如果需要同步更新全局变量,保留这行 CreateNewShipmentActivity.mainShipment.shipmentObj.items?.add(newItem) finish() } else { val errorBody = errors.map { getString(it) }.joinToString("\n") MaterialDialog.Builder(this) .title("Oops") .content(errorBody) .positiveText(R.string.ok) .positiveColor(ContextCompat.getColor(this, R.color.colorPrimary)) .onAny { dialog, _ -> dialog.dismiss() } .show() } } } // 单独提取验证逻辑,只返回错误信息 private fun collectValidationErrors(): ArrayList<Int> { val errors = ArrayList<Int>() if (edtPackageAddShipment.text.toString().isEmpty()) { errors.add(R.string.packages_must_not_be_empty) } if (edtPiecesAddShipment.text.toString().isEmpty()) { errors.add(R.string.pieces_must_not_be_empty) } if (edtWeightAddShipment.text.toString().isEmpty()) { errors.add(R.string.weight_must_not_be_empty) } if (edtHeightAddShipment.text.toString().isEmpty()) { errors.add(R.string.height_must_not_be_empty) } if (edtWidthAddShipment.text.toString().isEmpty()) { errors.add(R.string.width_must_not_be_empty) } if (edtLengthAddShipment.text.toString().isEmpty()) { errors.add(R.string.length_must_not_be_empty) } if (edtDescriptionAddShipment.text.toString().isEmpty()) { errors.add(R.string.description_must_not_be_empty) } return errors } // 单独提取数据收集逻辑,返回构建好的ItemsItem private fun collectItemData(): ItemsItem { val item = ItemsItem() item.numberOfPackages = edtPackageAddShipment.text.toString().toInt() item.numberOfPieces = edtPiecesAddShipment.text.toString() item.weightUnit = edtWeightAddShipment.text.toString().toInt() item.height = edtHeightAddShipment.text.toString() item.width = edtWidthAddShipment.text.toString() item.length = edtLengthAddShipment.text.toString() item.description = edtDescriptionAddShipment.text.toString() return item } override fun onBackPressed() { setResult(Activity.RESULT_CANCELED) super.onBackPressed() } }
3. ShipmentItemAdapter的绑定错误与点击逻辑问题
你当前的适配器存在字段绑定错误(比如使用不存在的desc字段、错误用description显示数量),点击跳转逻辑也没有传递当前item数据,这里一起修正:
修正后的适配器代码:
class ShipmentItemAdapter( private val context: Context, private val items: List<ItemsItem> ) : RecyclerView.Adapter<ShipmentItemAdapter.MyViewHolder>() { private val inflater: LayoutInflater = LayoutInflater.from(context) // 内部ViewHolder类,统一管理布局中的View inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val namePackageShipmentPackage: TextView = itemView.findViewById(R.id.namePackageShipmentPackage) val quantityPackageShipmentPackage: TextView = itemView.findViewById(R.id.quantityPackageShipmentPackage) val dimensionPackageHeightShipmentPackage: TextView = itemView.findViewById(R.id.dimensionPackageHeightShipmentPackage) // 根据你的布局添加其他需要绑定的View,比如宽度、长度等 } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val view = inflater.inflate(R.layout.item_shipment_package, parent, false) val holder = MyViewHolder(view) // 处理item点击事件,可选支持编辑跳转 holder.itemView.setOnClickListener { val position = adapterPosition if (position != RecyclerView.NO_POSITION) { val item = items[position] val intent = Intent(context, AddItemActivity::class.java).apply { putExtra("EDIT_ITEM", item) putExtra("EDIT_POSITION", position) } (context as AppCompatActivity).startActivityForResult(intent, 3) // 用新的requestCode区分编辑请求 } } return holder } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val item = items[position] holder.namePackageShipmentPackage.text = item.description // 数量显示numberOfPieces字段 holder.quantityPackageShipmentPackage.text = item.numberOfPieces // 高度显示height字段 holder.dimensionPackageHeightShipmentPackage.text = item.height // 绑定其他字段示例 // holder.dimensionPackageWidthShipmentPackage.text = item.width // holder.dimensionPackageLengthShipmentPackage.text = item.length } override fun getItemCount(): Int { return items.size } }
额外注意点
- 确保布局文件中的View ID和代码中的调用完全一致
- 如果需要支持编辑功能,可以在Fragment中新增对应的
onActivityResult分支,处理编辑返回的数据并调用adapter.notifyItemChanged(position)更新列表 - 尽量减少全局变量的使用,推荐用ViewModel或LiveData管理跨页面数据,让代码更健壮、更易维护
内容的提问来源于stack exchange,提问作者Abdulrahman
相关产品推荐
相关产品推荐

