【发布时间】:2015-11-09 15:23:57
【问题描述】:
我正在开发一个音乐应用程序和一个 android 应用程序的新手。我正在使用ListView 来显示设备中的音乐文件内容。这是我在ListView 中所做的:
- 显示音乐文件标题、艺术家和持续时间
- 音乐文件的专辑封面(如果有)
所以
我正在做的第一点很容易做到并且非常有效,滚动行为非常流畅,问题从第二点开始,即加载我正在使用AsyncTask 做的音乐文件的专辑封面。我得到AsyncTask 工作,但它不是最聪明的AsyncTask。例如,我希望 ListView 和 AsyncTask 提供这两种行为:
- 仅当用户停止滚动时才开始执行
AsyncTask- 我想在用户尝试浏览视图(快速滚动)时节省重要的 CPU 周期。现在,使用我的方法,当所有AsyncTask发生投掷时立即开始加载。我已经实现了函数cancelPotentialTask(),如果我不需要它,它会立即取消任务,但是如果我可以控制启动任务,那将比取消它更有效。就像当用户停止滚动ListView时,我得到一个启动AsyncTask的触发器。 - 如果
AsyncTask一旦返回结果是没有专辑封面,那么我再调用它就不需要再次进入任务了-这可以通过下图很容易理解(抱歉编辑不好)。
在第一部分,我让视图与 AsyncTask 一起加载。在下一个中,我向上滑动并获得另一个视图,然后让一排去回收站。第三次向下滑动时,我得到了回收的视图,但 Asynctask 加载了,但我不需要 AsyncTask,因为很明显结果将是空的,因为它是第一次。我知道代码不知道这一点,直到我们不告诉它。但我怎么知道。我在getView() 中有一个线索,我可以在其中查看convertView == null。
感谢您到目前为止的阅读,感谢您的耐心等待。这是给你的奶酪-
public Bitmap getAlbumart(Long album_id) {
Bitmap bm = null;
try {
final Uri sArtworkUri = Uri
.parse("content://media/external/audio/albumart");
Uri uri = ContentUris.withAppendedId(sArtworkUri, album_id);
ParcelFileDescriptor pfd = activity.getContentResolver()
.openFileDescriptor(uri, "r");
if (pfd != null) {
FileDescriptor fd = pfd.getFileDescriptor();
bm = BitmapFactory.decodeFileDescriptor(fd);
}
} catch (Exception ignored) {
}
return bm;
}
// This is what I call to get image in getView()
public void loadBitmap(String resId, ImageView imageView) {
if (cancelPotentialWork(resId, imageView)) {
String imageKey = String.valueOf(resId);
LoadRows task = new LoadRows(imageView);
SoftReference<Bitmap> bitmap1;
try {
bitmap1 = new SoftReference<>(Bitmap.createScaledBitmap(getBitmapFromMemCache(imageKey), 80, 80, true));
imageView.setImageBitmap(bitmap1.get());
} catch (Exception e) {
SoftReference<AsyncDrawable> asyncDrawable = new SoftReference<>(new AsyncDrawable(resId, null,
task));
imageView.setImageDrawable(asyncDrawable.get());
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, resId);
}
}
}
public class LoadRows extends AsyncTask<String, Void, Bitmap> {
String data = null;
private final WeakReference<ImageView> imageViewReference;
public LoadRows(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage
// collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
@Override
protected Bitmap doInBackground(String... params) {
data = params[0];
Bitmap imageResult;
imageResult = getAlbumart(Long.parseLong(data));
if (imageResult != null) {
imageResult = Bitmap.createScaledBitmap(imageResult, 300, 300, true);
addBitmapToMemoryCache(data, imageResult);
imageResult = Bitmap.createScaledBitmap(imageResult, 80, 80, true);
}
return imageResult;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (isCancelled()) {
bitmap = null;
}
if (bitmap != null) {
ImageView imageView = imageViewReference.get();
LoadRows bitmapWorkerTask = getLoadRows(imageView);
if (this == bitmapWorkerTask) {
imageView.setImageBitmap(bitmap);
}
}
}
}
static class AsyncDrawable extends BitmapDrawable {
private final WeakReference<LoadRows> LoadRowsReference;
@SuppressWarnings("deprecation")
public AsyncDrawable(String res, Bitmap bitmap, LoadRows LoadRows) {
super(bitmap);
LoadRowsReference = new WeakReference<LoadRows>(LoadRows);
}
public LoadRows getLoadRows() {
return LoadRowsReference.get();
}
}
public static boolean cancelPotentialWork(String data, ImageView imageView) {
final LoadRows LoadRows = getLoadRows(imageView);
if (LoadRows != null) {
final String bitmapData = LoadRows.data;
// If bitmapData is not yet set or it differs from the new data
if (bitmapData == "0" || bitmapData != data) {
// Cancel previous task
LoadRows.cancel(true);
} else {
// The same work is already in progress
return false;
}
}
// No task associated with the ImageView, or an existing task was
// cancelled
return true;
}
private static LoadRows getLoadRows(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getLoadRows();
}
}
return null;
}
请大家慢慢来,我不急于很快接受任何答案。希望有人可以玩代码,或者除了解释我应该采取什么方法。
如果你们还需要了解其他信息,请告诉我。谢谢!
编辑
getView() 方法 -
@SuppressWarnings("deprecation")
@SuppressLint("SimpleDateFormat")
public View getView(final int position, View convertView, ViewGroup parent) {
View vi = convertView;
final ViewHolder holder;
if (convertView == null) {
/****** Inflate tabitem.xml file for each row ( Defined below ) *******/
vi = inflater.inflate(R.layout.row, null);
/****** View Holder Object to contain tabitem.xml file elements ******/
holder = new ViewHolder();
holder.text = (TextView) vi.findViewById(R.id.text);
holder.text1 = (TextView) vi.findViewById(R.id.text1);
holder.duration = (TextView) vi.findViewById(R.id.textTime);
holder.image = (ImageView) vi.findViewById(R.id.image);
holder.btn = (Button) vi.findViewById(R.id.button1);
/************ Set holder with LayoutInflater ************/
vi.setTag(holder);
} else {
holder = (ViewHolder) vi.getTag();
}
if (data.size() > 0) {
/***** Get each Model object from Arraylist ********/
tempValues = null;
tempValues = (ListModel) data.get(position);
final String duration = tempValues.getDuration();
final String artist = tempValues.getArtist();
final String songName = tempValues.getName();
final String title = tempValues.getTitle();
final String albumid = tempValues.getAlbumID();
String finalTitle;
if (title != null) {
finalTitle = title;
} else {
finalTitle = songName;
}
loadBitmap(albumid, holder.image); // HERE I GET IMAGE ***********
holder.text.setText(finalTitle);
holder.text1.setText(artist);
holder.duration.setText(duration);
vi.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
MusicUtils.play(position, listModelToArrayList(), activity, songsManager);
}
});
final PopupMenu pop = new PopupMenu(activity, holder.btn);
int[] j = new int[6];
j[0] = MusicUtils.PLAY;
j[1] = MusicUtils.PLAY_NEXT;
j[2] = MusicUtils.ADD_TO_QUEUE;
j[3] = MusicUtils.ADD_TO_PLAYLIST;
j[4] = MusicUtils.SHUFFLE_PLAY;
j[5] = MusicUtils.DELETE;
MusicUtils.generateMenu(pop, j);
pop.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case MusicUtils.PLAY:
MusicUtils.play(position, listModelToArrayList(), activity, songsManager);
return true;
case MusicUtils.DELETE:
return true;
case MusicUtils.PLAY_NEXT:
MusicUtils.playNext(listModelToArrayList().get(position));
return true;
case MusicUtils.ADD_TO_QUEUE:
MusicUtils.addToQueue(listModelToArrayList().get(position), songsManager);
return true;
case MusicUtils.ADD_TO_PLAYLIST:
MusicUtils.addToPlaylist(position, listModelToArrayList().get(position), activity, songsManager);
return true;
case MusicUtils.SHUFFLE_PLAY:
MusicUtils.shufflePlay(position, listModelToArrayList(), activity, songsManager);
return true;
default:
return false;
}
}
});
holder.btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pop.show();
}
});
tempValues = null;
vi.setVisibility(View.VISIBLE);
} else {
vi.setVisibility(View.INVISIBLE);
}
return vi;
}
更新
使用UIL,结果几乎与我提供的代码相同,即滞后。但是Picasso 真的值得一试,我想说滞后几乎消失了。这几乎就是我想要实现的。
【问题讨论】:
-
为什么不使用库来加载图像?像 UIL(通用图像加载器)或 Picasso 或 ....?
-
我不确定,但那些可能只用于下载图像。如果它可以工作,那么我该如何处理音乐文件的专辑封面?
-
我试过UIL,没有什么区别。滞后仍然是相同的。请帮忙。
-
您是否从developer.android.com 获得此代码?我正在使用相同的代码进行修改,它可以工作。你能发布你实际上是如何调用 loadBitmap() 的吗?
-
是的,我从 Google Developers 那里得到了这个,并且为支持
URI的音乐文件做了一些修改。我在if-else下方对convertView进行了检查。
标签: android android-listview android-asynctask android-scroll