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
相关产品推荐
相关产品推荐

