You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Xamarin.Android:定时刷新ListView中ImageView,无需重启Activity

解决方案:定时刷新ListView状态(无需重启Activity)

你的问题核心在于只在ListView初始化时获取一次状态,而且同步网络请求还会阻塞UI。下面是完整的修改方案,兼顾性能和实时性:


1. 先重构网络请求为异步(避免UI卡顿)

原来的Get_Status是同步方法,会卡住主线程,改成异步版本,用HttpClient替代HttpWebRequest(更适合现代异步编程):

class ListAdapter : ArrayAdapter<Geraet>
{
    public List<Geraet> List { get; private set; }
    private string Serverip { get; }

    public ListAdapter(Context context, int listId, List<Geraet> list, string serverip) : base(context, listId, list)
    {
        List = list;
        Serverip = serverip;
    }

    // 改为公开的异步方法,方便外部调用
    public async Task<int> Get_StatusAsync(Geraet geraet)
    {
        try
        {
            using (var httpClient = new HttpClient())
            {
                var url = string.Format(Constants.StatusPath, Serverip, Constants.WebservicePort, geraet.Ip);
                var content = await httpClient.GetStringAsync(url);
                var result = System.Text.RegularExpressions.Regex.Replace(content, @"[^0-9]", "");
                return string.IsNullOrEmpty(result) ? -2 : int.Parse(result);
            }
        }
        catch (Exception ex)
        {
            // 捕获网络异常,返回错误标记
            Console.WriteLine($"获取设备状态失败: {ex.Message}");
            return -1;
        }
    }

    // ... 其他代码
}

2. 给Geraet类添加状态属性(缓存状态,避免重复请求)

给你的Geraet实体类加一个Status字段,用来保存最新的状态值,这样不用每次刷新UI都去发网络请求:

public class Geraet
{
    // 你原来的属性,比如Ip
    public string Ip { get; set; }
    // 新增状态属性,用来缓存最新状态
    public int Status { get; set; } = -2; // 默认初始状态
}

3. 修改ListAdapter的GetView方法(复用View+读取缓存状态)

现在GetView直接读取Geraet的缓存状态,不用再发起网络请求,提升性能:

public override View GetView(int position, View convertView, ViewGroup parent)
{
    View v = convertView;
    if (v == null)
    {
        LayoutInflater inflater = (LayoutInflater)Context.GetSystemService(Context.LayoutInflaterService);
        v = inflater.Inflate(Resource.Layout.List, parent, false);
    }

    var geraet = List[position];
    var statusImageView = v.FindViewById<ImageView>(Resource.Id.ImageView);

    // 根据缓存的状态更新图片
    if (geraet.Status == 1)
    {
        statusImageView.SetImageResource(Resource.Drawable.green);
    }
    else
    {
        statusImageView.SetImageResource(Resource.Drawable.red);
    }

    return v;
}

4. 在Activity中添加定时任务(定期刷新状态)

用Xamarin的Device.StartTimer来实现定时刷新,它会自动在主线程回调,方便更新UI:

public class YourActivity : Activity
{
    private ListAdapter _listAdapter;
    private CancellationTokenSource _refreshCts; // 用来取消定时任务

    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);
        SetContentView(Resource.Layout.Activity);

        var lv = FindViewById<ListView>(Resource.Id.ListView);
        _listAdapter = new ListAdapter(this, Resource.Layout.List, list.CurrentList, Intent.GetStringExtra("ServerIP"));
        lv.Adapter = _listAdapter;

        // 启动定时刷新,比如每5秒一次(可根据需求调整)
        StartStatusRefresh(TimeSpan.FromSeconds(5));
    }

    private void StartStatusRefresh(TimeSpan interval)
    {
        _refreshCts = new CancellationTokenSource();
        var token = _refreshCts.Token;

        Device.StartTimer(interval, () =>
        {
            // 如果任务被取消,停止定时
            if (token.IsCancellationRequested)
                return false;

            // 异步刷新所有设备状态
            _ = RefreshAllDeviceStatusesAsync();
            return true; // 返回true继续定时,返回false停止
        });
    }

    private async Task RefreshAllDeviceStatusesAsync()
    {
        try
        {
            // 遍历所有设备,更新状态
            foreach (var geraet in _listAdapter.List)
            {
                geraet.Status = await _listAdapter.Get_StatusAsync(geraet);
            }

            // 必须在主线程通知适配器刷新UI
            RunOnUiThread(() =>
            {
                _listAdapter.NotifyDataSetChanged();
            });
        }
        catch (Exception ex)
        {
            Console.WriteLine($"批量刷新状态失败: {ex.Message}");
        }
    }

    // 页面销毁时取消定时任务,防止内存泄漏
    protected override void OnDestroy()
    {
        base.OnDestroy();
        _refreshCts?.Cancel();
        _refreshCts?.Dispose();
    }
}

关键注意点

  • 异步网络请求:必须用async/await,否则会阻塞主线程导致UI卡顿甚至ANR。
  • 定时任务清理:在OnDestroy中取消并释放CancellationTokenSource,避免Activity销毁后任务还在运行,造成内存泄漏。
  • 状态缓存:把状态存在Geraet对象里,避免GetView每次都发起网络请求,提升ListView的滚动流畅度。
  • 主线程更新UINotifyDataSetChanged必须在主线程调用,这里用RunOnUiThread保证线程安全。

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

火山引擎 最新活动