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

Xamarin中如何获取RecyclerView屏幕外TextView的数据?

问题

在Xamarin Android应用中,需要给RecyclerView内的可修改字段添加保存功能。尝试通过直接访问TextView收集数据,但无法获取屏幕外的TextView内容,之前用_recipieEditRecyclerView.ChildCount和FindViewHolderForAdapterPosition的方法也失败了。

解决方案

核心思路是让数据源与UI双向同步:不要从RecyclerView的View中提取数据,而是在用户修改UI时实时更新对应的数据源,保存时直接读取数据源即可(RecyclerView的回收机制导致屏幕外View无法可靠访问,这是标准解决方案)。

步骤1:重构为单一数据源实体

把原来分散的三个List(名称、数量、单位)整合成一个实体类,方便维护和同步:

public class RecipieIngredientEditModel
{
    public int IngredientId { get; set; }
    public string Name { get; set; }
    public double Quantity { get; set; }
    public string Measurement { get; set; }
}

步骤2:修改Adapter,添加实时同步逻辑

更新Adapter,使用整合后的数据源,并在绑定ViewHolder时添加输入监听,实时同步用户修改到数据源:

using Android.Views;
using AndroidX.RecyclerView.Widget;
using Recipy.DataLayer;
using Recipy.Enums;
using Recipy.Models;
using Recipy.ViewHolders;
using System;
using System.Collections.Generic;
using System.IO;

namespace Recipy.Adapters
{
    internal class RecipieEditAdapter : RecyclerView.Adapter
    {
        // 对外暴露修改后的数据源
        public List<RecipieIngredientEditModel> EditIngredients { get; private set; }
        private readonly Database _database;

        public RecipieEditAdapter(int recipieId)
        {
            _database = new Database(Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), "RecipyDataBase.db"));
            var recipieIngredients = _database.GetAllRecipieIngredients(recipieId);
            
            EditIngredients = new List<RecipieIngredientEditModel>();
            foreach (var ingredient in recipieIngredients)
            {
                EditIngredients.Add(new RecipieIngredientEditModel
                {
                    IngredientId = ingredient.IngredientId,
                    Name = _database.GetIngredientNameById(ingredient.IngredientId),
                    Quantity = _database.GetIngredientQuantityById(recipieId, ingredient.IngredientId),
                    Measurement = _database.GetIngredientMeasurementById(recipieId, ingredient.IngredientId)
                });
            }
        }

        public override int ItemCount => EditIngredients.Count;

        public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
        {
            if (holder is RecipieEditIngridientsViewHolder viewHolder)
            {
                var currentModel = EditIngredients[position];
                
                // 绑定初始数据
                viewHolder.IngredientName.Text = currentModel.Name;
                viewHolder.IngredientQuantity.Text = currentModel.Quantity.ToString();
                int measurementId = (int)(MeasurementEnum)Enum.Parse(typeof(MeasurementEnum), currentModel.Measurement);
                viewHolder.IngredientMeasurement.SetSelection(measurementId);

                // 名称输入监听:实时更新数据源
                viewHolder.IngredientName.TextChanged += (s, e) =>
                {
                    currentModel.Name = viewHolder.IngredientName.Text;
                };

                // 数量输入监听:实时更新数据源(做格式校验)
                viewHolder.IngredientQuantity.TextChanged += (s, e) =>
                {
                    if (double.TryParse(viewHolder.IngredientQuantity.Text, out var validQuantity))
                    {
                        currentModel.Quantity = validQuantity;
                    }
                };

                // 单位选择监听:实时更新数据源
                viewHolder.IngredientMeasurement.ItemSelected += (s, e) =>
                {
                    currentModel.Measurement = viewHolder.IngredientMeasurement.SelectedItem.ToString();
                };

                // 删除按钮逻辑:更新数据源并通知RecyclerView刷新
                viewHolder.DeleteButton.Click += (s, e) =>
                {
                    EditIngredients.RemoveAt(position);
                    NotifyItemRemoved(position);
                    NotifyItemRangeChanged(position, EditIngredients.Count);
                };
            }
        }

        public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
        {
            View itemView = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.recipieEditIngredients_viewholder, parent, false);
            return new RecipieEditIngridientsViewHolder(itemView);
        }
    }
}

步骤3:完善ViewHolder控件绑定

确保ViewHolder正确获取布局中的所有可交互控件:

using Android.Views;
using AndroidX.RecyclerView.Widget;
using Google.Android.Material.TextField;

namespace Recipy.ViewHolders
{
    public class RecipieEditIngridientsViewHolder : RecyclerView.ViewHolder
    {
        public TextInputEditText IngredientName { get; }
        public TextInputEditText IngredientQuantity { get; }
        public Spinner IngredientMeasurement { get; }
        public Button DeleteButton { get; }

        public RecipieEditIngridientsViewHolder(View itemView) : base(itemView)
        {
            IngredientName = itemView.FindViewById<TextInputEditText>(Resource.Id.editRecipieNameTextField);
            IngredientQuantity = itemView.FindViewById<TextInputEditText>(Resource.Id.editRecipieQuantityTextField);
            IngredientMeasurement = itemView.FindViewById<Spinner>(Resource.Id.editRecipieMeasurementDropDown);
            DeleteButton = itemView.FindViewById<Button>(Resource.Id.deleteRowButton);
        }
    }
}

步骤4:在页面中实现保存逻辑

在保存按钮的点击事件中,直接从Adapter获取修改后的数据源,执行数据库更新:

// 初始化Adapter和RecyclerView(假设recipieId是当前食谱ID)
var editAdapter = new RecipieEditAdapter(recipieId);
_recipieEditIngredients.SetAdapter(editAdapter);

// 绑定保存按钮点击事件
recipieConfirmChangesButton.Click += (s, e) =>
{
    // 获取所有修改后的食材数据
    var updatedIngredients = editAdapter.EditIngredients;
    
    // 遍历数据,更新到数据库
    foreach (var ingredient in updatedIngredients)
    {
        // 调用你的数据库更新方法,示例:
        editAdapter._database.UpdateRecipieIngredient(recipieId, ingredient.IngredientId, ingredient.Name, ingredient.Quantity, ingredient.Measurement);
    }
    
    // 保存完成后返回上一页或提示用户
    Finish();
};

为什么之前的方法无效?

RecyclerView的核心机制是视图回收复用,屏幕外的Item View会被销毁或重新绑定到其他位置的数据,此时通过FindViewHolderForAdapterPosition获取的要么是null,要么是已复用的错误数据,无法可靠获取原始位置的输入内容。只有维护与UI同步的数据源,才能确保保存时拿到所有修改后的内容。

内容的提问来源于stack exchange,提问作者Kiril

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 17:19:50