使用AsyncTask将图片加载到自定义适配器中

8
尽管有很多教程,但我发现在自定义适配器中实现AsyncTask加载来自内容提供者的URI图像非常困难。
基本思路是创建一个包含AsyncTask的类,在“doInBackground”中进行位图创建,然后在“onPostExecute”中设置ImageView。
对于我这个新手,Android和编程,问题在于我不知道如何为每个项目将我的URI传递给AsyncTask,如何创建位图以及如何将其返回到适配器。
我只完成了实际AsyncTask类(ImageLoader)的此部分:
package another.music.player;

import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.widget.ImageView;

public class ImageLoader extends AsyncTask<String, String, Bitmap> {

    private ImageView imageView;
    private Bitmap bitmap = null;

    @Override
    protected Bitmap doInBackground(String... uri) {

        // Create bitmap from passed in Uri here

        return bitmap;

    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (bitmap != null && imageView != null) {
            imageView.setImageBitmap(bitmap);

        }
    }
}

我的自定义适配器如下:

package another.music.player;

import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.BaseColumns;
import android.provider.MediaStore.Audio.AlbumColumns;
import android.provider.MediaStore.Audio.AudioColumns;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.TextView;

class AlbumAdapter extends CursorAdapter {

    public AlbumAdapter(Context context, Cursor c, int flags) {
        super(context, c, flags);
    }

    private static Uri currentSongUri;

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        ImageView albumArt = (ImageView) view.getTag(R.id.albumArt);
        TextView text1 = (TextView) view.getTag(R.id.artistTitle);
        TextView text2 = (TextView) view.getTag(R.id.albumTitle);
        TextView text3 = (TextView) view.getTag(R.id.totalSongs);

        albumArt.setImageBitmap(null);

        text1.setText(cursor.getString(cursor
                .getColumnIndex(AudioColumns.ARTIST)));
        text2.setText(cursor.getString(cursor
                .getColumnIndex(AudioColumns.ALBUM)));
        text3.setText(cursor.getString(cursor
                .getColumnIndex(AlbumColumns.NUMBER_OF_SONGS)));

        String currentAlbumId = cursor.getString(cursor
                .getColumnIndex(BaseColumns._ID));

        Integer currentAlbumIdLong = Integer.parseInt(currentAlbumId);
        Uri artworkUri = Uri.parse("content://media/external/audio/albumart");
        currentSongUri = ContentUris.withAppendedId(artworkUri,
                currentAlbumIdLong);

        //Run ImageLoader AsyncTask here, and somehow retrieve the ImageView & set it.

    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        LayoutInflater inflater = LayoutInflater.from(context);
        View view = inflater.inflate(R.layout.albumitem, null);
        view.setTag(R.id.albumArt, view.findViewById(R.id.albumArt));
        view.setTag(R.id.artistTitle, view.findViewById(R.id.artistTitle));
        view.setTag(R.id.albumTitle, view.findViewById(R.id.albumTitle));
        view.setTag(R.id.totalSongs, view.findViewById(R.id.totalSongs));

        return view;
    }

}

如果有人能向我展示如何处理这个问题,我将非常感激。

谢谢。


1
你开发了这个名为Shuttle Music Player的应用程序吗? - h4ck3d
嗨,是的,我做过了。现在我对AsyncTasks非常熟悉! - Tim Malseed
你应该在适配器之外使用AsyncTask,然后在其完成后重新填充结果。 - S-K'
2个回答

10

您需要将视图传递给AsyncTask,以便在其完成时更新它:

//Run ImageLoader AsyncTask here, and let it set the ImageView when it is done.
new ImageLoader().execute(view, uri);

修改 AsyncTask 以便它可以处理混合参数类型:

public class ImageLoader extends AsyncTask<Object, String, Bitmap> {

    private View view;
    private Bitmap bitmap = null;

    @Override
    protected Bitmap doInBackground(Object... parameters) {

        // Get the passed arguments here
        view = (View) parameters[0];
        String uri = (String)parameters[1];

        // Create bitmap from passed in Uri here
        // ...
        return bitmap;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (bitmap != null && view != null) {
            ImageView albumArt = (ImageView) view.getTag(R.id.albumArt);
            albumArt.setImageBitmap(bitmap);
        }
    }
}

我还没有测试过这段代码,但它应该能让你有个大概的想法。


谢谢你的帮助。我会尝试一下。另外一个问题是,我也遇到了创建位图的困难。我不确定如何从URI创建位图,因为似乎没有上下文无法使用decodeInputStream。 - Tim Malseed
你还可以将上下文作为参数传递:new ImageLoader().execute(view, uri, context); 还有 Context context = (Context)parameters[2]; - Caner
1
那么您的建议是每次CursorAdapter执行bindView()时都应该启动一个新线程吗?这将为游标中的每一行运行一个线程,这将耗尽设备电池,并在达到最大池大小时可能破坏系统服务。 - S-K'

0
为什么你在 AsyncTask 中使用 setImage?你可以在一个线程中完成它。我认为在这种情况下,AsyncTask 不是很好的选择。最好你在不同的线程中完成它。

2
AsyncTask 在不同的线程上运行:http://developer.android.com/reference/android/os/AsyncTask.html - Caner
我不一定需要按照我展示的方式来做。位图可以直接返回给适配器,然后设置ImageView。我愿意听取建议。尽管我读过很多资料,但我确实认为应该以某种方式使用AsyncTask。 - Tim Malseed
我知道AsyncTask在不同的线程中运行。我更喜欢不在AsyncTask中执行此任务。无论如何,如果它能够满足要求,你可以在其中执行它。 - Debarati
我不确定为什么这个答案被投票否决了,@Debarati 指出在这种情况下使用 AsyncTask 是过度杀伤。你不需要线程池或取消,那为什么不只使用 Thread? - S-K'
1
据我所了解,AsyncTask 可以让你轻松地与 UI 进行通信,而 Thread 则不行。 - Azurespot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接