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

Xamarin ListView切换屏幕外项背景崩溃及状态保留问题咨询

解决Xamarin ListView点击按钮崩溃及滚动保持选中状态的问题

嘿,这个问题我太熟悉了!你遇到的崩溃完全是因为直接操作ListView的子视图踩了坑,而且滚动时状态丢失也是没利用好ListView的视图复用机制导致的。我来帮你一步步彻底解决:

问题根源

你代码里的listView.GetChildAt(index)只能获取当前屏幕可见的item视图,当item滚出屏幕外时,这个方法会返回null,直接调用SetBackgroundResource就会触发空引用异常,这就是崩溃的原因。另外,直接操作子视图的方式也没法在滚动时保持状态,因为ListView会复用视图,滚回来的item会被重新绘制,之前的背景设置会丢失。

正确解决方案:数据驱动UI

ListView的设计核心就是通过Adapter绑定数据与视图,正确的做法是只修改数据状态,让Adapter根据数据自动更新UI。这样既避免空引用,又能在滚动时稳定保持选中状态。

步骤1:让数据类支持属性变化通知

首先确保你的ItemUnit类实现INotifyPropertyChanged接口,这样当IsSelected属性变化时,能通知Adapter刷新对应的item:

public class ItemUnit : INotifyPropertyChanged
{
    private bool _isSelected;
    public bool IsSelected
    {
        get => _isSelected;
        set
        {
            if (_isSelected != value)
            {
                _isSelected = value;
                OnPropertyChanged(nameof(IsSelected));
            }
        }
    }

    // 这里添加你的其他属性,比如玩家名称、回合数等

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

步骤2:修改Adapter,根据数据状态渲染UI

重构你的ListView Adapter,在GetView方法里根据IsSelected属性设置背景色。这样不管item是否可见,只要数据状态变了,滚动时Adapter会自动更新视图:

public class ItemUnitAdapter : BaseAdapter<ItemUnit>
{
    private readonly Context _context;
    private readonly List<ItemUnit> _itemList;

    public ItemUnitAdapter(Context context, List<ItemUnit> itemList)
    {
        _context = context;
        _itemList = itemList;
    }

    public override ItemUnit this[int position] => _itemList[position];
    public override int Count => _itemList.Count;
    public override long GetItemId(int position) => position;

    public override View GetView(int position, View convertView, ViewGroup parent)
    {
        // 复用视图,提升性能
        View itemView = convertView ?? LayoutInflater.From(_context)
            .Inflate(Resource.Layout.your_item_layout, parent, false);

        var currentItem = _itemList[position];

        // 根据IsSelected设置背景色
        itemView.SetBackgroundResource(
            currentItem.IsSelected ? Resource.Color.colorAccent : Resource.Color.colorPrimary);

        // 这里绑定其他UI元素,比如玩家名称文本等
        // var playerNameTxt = itemView.FindViewById<TextView>(Resource.Id.playerNameTxt);
        // playerNameTxt.Text = currentItem.PlayerName;

        return itemView;
    }
}

步骤3:重构按钮点击事件,只修改数据

现在把按钮点击逻辑改成只操作数据,然后通知Adapter刷新,彻底抛弃直接操作子视图的写法:

Button _rotateButton = FindViewById<Button>(Resource.Id.rotateButton); 
int rotatePrevious = -1; 
int rotateCurrent = 0; 

// 初始化Adapter并绑定到ListView
var itemAdapter = new ItemUnitAdapter(this, ItemUnits);
listView.Adapter = itemAdapter;

_rotateButton.Click += delegate { 
    int rotateCount = ItemUnits.Count; 
    if (rotateCount == 0) return;

    if (rotateCurrent == 0)
    {
        _rotateButton.Text = "End Turn"; 
    }

    // 重置上一个选中的item
    if (rotatePrevious != -1 && rotatePrevious < rotateCount)
    {
        ItemUnits[rotatePrevious].IsSelected = false;
    }

    if (rotateCurrent == rotateCount) 
    { 
        // 重置所有item的选中状态
        foreach(ItemUnit iu in ItemUnits) 
        { 
            iu.IsSelected = false; 
        } 
        rotateCurrent = 0; 
        rotatePrevious = -1; 
        _rotateButton.Text = "New Round"; 
    } 
    else 
    { 
        // 设置当前回合的item为选中状态
        ItemUnits[rotateCurrent].IsSelected = true; 

        rotateCurrent++; 
        rotatePrevious = rotateCurrent - 1; 

        if (rotateCurrent == rotateCount) 
        { 
            _rotateButton.Text = "End Round"; 
        } 
    }

    // 通知Adapter数据变化,刷新UI
    itemAdapter.NotifyDataSetChanged();
};

关于ListView的支持情况

完全支持!这种数据驱动UI的方式正是ListView设计的核心逻辑,利用Adapter的视图复用机制,既能避免空引用崩溃,又能在滚动时完美保持选中状态——不管item滚到屏幕外再滚回来,Adapter都会根据最新的IsSelected状态重新渲染背景色。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 15:37:51