图像被分配给ListView的另一个行项目

mac*_*Jun 2 c# android xamarin.android async-await xamarin

我知道async await非常方便在设置适配器之前为ListView/GridView准备数据.例如:

// in Activity.cs
async void OnCreate(Bundle SavedInstanceState)
{
    SetContentView(...);
    ListView listView = FindViewById<ListView>(...);
    AdapterData data = await Task.Run(() => GetDataFromWorkerThread);
    listView.SetAdapter(data);
}
Run Code Online (Sandbox Code Playgroud)

但我需要的是:

// in ListViewAdapter.cs
public override View GetView (int position, View convertView, ViewGroup parent) 
{
    if(convertView == null) 
    {
        // create new view
    }
    ImageView imgView = convertView.FindViewById<ImageView>(Resouce.Id.img_view_id);
    ResizeImageAsync(imgView, imgResId);
    return convertView; // convertView is returned before ImageView is assigned with resized image
}

private async void ResizeImageAsync(ImageView imageView, int imgResId)
{
    Bitmap bmp = await Task<Bitmap>.Run(() => ResizeImage(imgResId, 50, 50));
    imageView.SetImageBitmap(bmp);
}
Run Code Online (Sandbox Code Playgroud)

所以有时convertView在完成GetView()之前返回ResizeImageAsync().image01应该在第一行中分配的问题ImageView现在改为分配给第二行或第三行ImageView.谁知道如何解决这个问题?

Mih*_*kic 8

问题不在于返回视图,而是它退出屏幕并被重用.然后你启动另一个异步方法并最后完成胜利.那说你必须取消第一个在同一个视图上启动另一个.一种解决方案是使用您存储在View.Tag中的CancellationTokenSource.这为您提供了一种取消以前未完成的异步方法的方法.

这是一个没有经过严格测试的语法错误代码.请注意,将.net对象存储到View.Tag中需要一个包装器.另请注意,此代码可能不完美,但应说明要执行的操作.

public override View GetView(int position, View convertView, ViewGroup parent)
    {
        CancellationTokenSource cts;
        if (convertView == null)
        {
            // create new view
        }
        else
        {
            if (convertView.Tag != null)
            {
                var wraper = convertView.Tag.JavaCast<Wrapper<CancellationTokenSource>>();
                if (wraper != null)
                    wraper.Data.Cancel();
            }
        }
        ImageView imgView = convertView.FindViewById<ImageView>(Resource.Id.img_view_id);
        cts = new CancellationTokenSource();
        ResizeImageAsync(imgView, imgResId, cts.Token);
        convertView.Tag = new Wrapper<CancellationTokenSource> { Data = cts };
        return convertView; // convertView is returned before ImageView is assigned with resized image
    }

    private async Task ResizeImageAsync(ImageView imageView, int imgResId, CancellationToken ct)
    {
        Bitmap bmp = await Task<Bitmap>.Run(() => ResizeImage(imgResId, 50, 50), ct);
        if (!ct.IsCancellationRequested)
        {
            imageView.SetImageBitmap(bmp);
        }
    }

    public class Wrapper<T>: Java.Lang.Object
    {
        public T Data;
    }
Run Code Online (Sandbox Code Playgroud)