ListView BaseAdapter 挂起
ListView BaseAdapter is hanging
我的可滚动列表视图出现一些奇怪的行为。它由 BaseAdapter 控制。
当我向上或向下滚动时,它有时会挂起然后朝相反的方向弹回。我不知道为什么,也不知道如何解决它。
我的基本适配器在下面,它加载了大约 90 个带有图像和一堆文本的片段。
public class PosterListAdapter extends BaseAdapter {
private final ArrayList<Poster> listItems;
private final LayoutInflater inflater;
public PosterListAdapter(ArrayList<Poster> listItems, LayoutInflater inflater) {
this.listItems = listItems;
this.categoryList = categoryItems;
this.inflater = inflater;
}
@Override
public int getCount() {
//Log.d("getCount", String.valueOf(this.listItems.size()));
return this.listItems.size();
}
@Override
public Poster getItem(int i) {
return this.listItems.get(i);
}
@Override
public long getItemId(int i) {
return 0;
}
@Override
public View getView(int i, View convertView, ViewGroup viewGroup) {
MyViewHolder mViewHolder;
SparseArray<String> categoryMap = db.getCategoryMap();
if (convertView == null) {
convertView = inflater.inflate(R.layout.catalog_list_fragment, viewGroup, false);
mViewHolder = new MyViewHolder(convertView);
convertView.setTag(mViewHolder);
} else {
mViewHolder = (MyViewHolder) convertView.getTag();
}
//LayoutParams params = (LayoutParams) imageView.getLayoutParams();
Poster item = this.listItems.get(i);
String filename = item.getPosterFilename();
mViewHolder.posterAuthor.setText("Author: "+item.getPresenterFname()+' '+item.getPresenterLname());
mViewHolder.posterAltAuthors.setText("Supporting Authors: "+item.getPosterAuthors());
mViewHolder.posterTitle.setText(item.getPosterTitle());
mViewHolder.posterSynopsis.setText(item.getPosterSynopsis());
mViewHolder.posterNumber.setText("Poster: "+String.valueOf(item.getPosterNumber()));
mViewHolder.posterPresentation.setText("Live Presentation: "+item.getSessionDate()+" at "+item.getSessionTime()+"\nAt Station: "+item.getPosterStation());
String category = categoryMap.get(item.getCatID());
mViewHolder.posterCategory.setText("Category: "+category);
File imgFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath(), "/"+filename+"/"+filename+".png");
//Here I thought maybe the resizing of the image or even the image itself
// was causing the hang up so I tried the list without it and it is still
// hanging.
//mViewHolder.imageView.setImageURI(Uri.fromFile(new File(imgFile.toString())));
if (imgFile.exists()){
/*Bitmap bmp = BitmapFactory.decodeFile(imgFile.toString());
int newWidth = 500;
Bitmap sizedBMP = getResizedBitmap(bmp, newWidth);
mViewHolder.imageView.setImageBitmap(sizedBMP);*/
}
else{
//set no image available
}
return convertView;
}
private class MyViewHolder {
TextView posterTitle, posterAuthor, posterSynopsis, posterCategory, posterNumber, posterAltAuthors,posterPresentation;
ImageView imageView;
public MyViewHolder(View item) {
posterTitle = (TextView) item.findViewById(R.id.poster_title);
posterAuthor = (TextView) item.findViewById(R.id.poster_author);
posterAltAuthors = (TextView) item.findViewById(R.id.poster_altAuthors);
posterSynopsis = (TextView) item.findViewById(R.id.poster_synopsis);
posterCategory = (TextView) item.findViewById(R.id.poster_category);
posterNumber = (TextView) item.findViewById(R.id.poster_number);
posterPresentation = (TextView) item.findViewById(R.id.poster_presentation);
imageView = (ImageView) item.findViewById(R.id.poster_thumb);
}
}
}
我知道那是一大段代码,而且可能很难看。如果您能指出正确的方向来解决问题,那也会很有帮助。我正在使用日食。
我敢打赌,如果您注释掉那行 File imgFile = new File(...
(以及依赖于 imgFile
的代码),您的列表滚动将会得到改进。
这仍然是一个 I/O 操作,并且由于 getView()
在 UI 线程中运行,因此可能会出现问题。
你应该做的是:一旦你在 getView()
中有了 ImageView
,你就开始 AsyncTask
打开文件,解码它,并将位图分配给 ImageView
.
然后你必须处理这样的事情:ImageView
被回收了,但是任务还没有完成。
阅读这篇文章:Multithreading for Performance | Android Developers Blog。它处理来自远程服务器的图像,但所有原理仍然相同。
另外:这一行 SparseArray<String> categoryMap = db.getCategoryMap();
是否进行数据库查找?这也可能导致打嗝。
TL;DR getView()
需要快;将缓慢的操作放在非 UI 线程中。
我同意之前的回答,看起来创建新 File
是唯一挂起您的应用程序的繁重操作。
尝试使用一些图像加载库,例如Glide。它将简化您的代码,您也不必处理后台作业。图书馆会为你做这一切。
1.Import 图书馆,例如如果您使用 gradle 将其插入您的 build.gradle
文件:
repositories {
mavenCentral() // jcenter() works as well because it pulls from Maven Central
}
dependencies {
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.android.support:support-v4:19.1.0'
}
2.In 您的适配器将与加载图片相关的代码替换为:
Glide
.with(context)
.load(<full path to your local file>)
.into(mViewHolder.imageView);
3.And 就这些了。 Glide 将在内部处理回收和其他事情,它还针对快速加载和平滑滚动进行了优化。
图像相关进程确实在应用程序的 UIThread 上消耗了太多内存,您应该使用异步(后台)方法设置图像。或者,如果这对您来说很麻烦,那么有大量的库可以做到这一点。我特别推荐使用 Picasso,因为它非常动态、易于使用并且定期更新。加载图像需要一行代码:
Picasso.with(getApplicationContext()).load(<your image path or url or resource id>).into(YourImageView);
Picasso 也有多种图像选项供您选择。
数据库相关操作应该在单独的线程中完成。因为如果记录数较多,获取数据可能需要时间。
getView() 方法将被调用不止一次。我们可以说它将在循环中调用直到数组列表的大小 (getCount()).
第二件事:文件操作也应该单独完成thread.becauseIO任务也有些耗时。
解法:
SparseArray<String> categoryMap = db.getCategoryMap();
将这一行放在 getView() 方法之外,因为我在这一行中看不到任何列表相关参数。这应该在 constructor 或 separate thread.
中完成
运行 您的数据库操作和文件 IO 在单独的线程中使用 AsyncTask
我的可滚动列表视图出现一些奇怪的行为。它由 BaseAdapter 控制。
当我向上或向下滚动时,它有时会挂起然后朝相反的方向弹回。我不知道为什么,也不知道如何解决它。
我的基本适配器在下面,它加载了大约 90 个带有图像和一堆文本的片段。
public class PosterListAdapter extends BaseAdapter {
private final ArrayList<Poster> listItems;
private final LayoutInflater inflater;
public PosterListAdapter(ArrayList<Poster> listItems, LayoutInflater inflater) {
this.listItems = listItems;
this.categoryList = categoryItems;
this.inflater = inflater;
}
@Override
public int getCount() {
//Log.d("getCount", String.valueOf(this.listItems.size()));
return this.listItems.size();
}
@Override
public Poster getItem(int i) {
return this.listItems.get(i);
}
@Override
public long getItemId(int i) {
return 0;
}
@Override
public View getView(int i, View convertView, ViewGroup viewGroup) {
MyViewHolder mViewHolder;
SparseArray<String> categoryMap = db.getCategoryMap();
if (convertView == null) {
convertView = inflater.inflate(R.layout.catalog_list_fragment, viewGroup, false);
mViewHolder = new MyViewHolder(convertView);
convertView.setTag(mViewHolder);
} else {
mViewHolder = (MyViewHolder) convertView.getTag();
}
//LayoutParams params = (LayoutParams) imageView.getLayoutParams();
Poster item = this.listItems.get(i);
String filename = item.getPosterFilename();
mViewHolder.posterAuthor.setText("Author: "+item.getPresenterFname()+' '+item.getPresenterLname());
mViewHolder.posterAltAuthors.setText("Supporting Authors: "+item.getPosterAuthors());
mViewHolder.posterTitle.setText(item.getPosterTitle());
mViewHolder.posterSynopsis.setText(item.getPosterSynopsis());
mViewHolder.posterNumber.setText("Poster: "+String.valueOf(item.getPosterNumber()));
mViewHolder.posterPresentation.setText("Live Presentation: "+item.getSessionDate()+" at "+item.getSessionTime()+"\nAt Station: "+item.getPosterStation());
String category = categoryMap.get(item.getCatID());
mViewHolder.posterCategory.setText("Category: "+category);
File imgFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath(), "/"+filename+"/"+filename+".png");
//Here I thought maybe the resizing of the image or even the image itself
// was causing the hang up so I tried the list without it and it is still
// hanging.
//mViewHolder.imageView.setImageURI(Uri.fromFile(new File(imgFile.toString())));
if (imgFile.exists()){
/*Bitmap bmp = BitmapFactory.decodeFile(imgFile.toString());
int newWidth = 500;
Bitmap sizedBMP = getResizedBitmap(bmp, newWidth);
mViewHolder.imageView.setImageBitmap(sizedBMP);*/
}
else{
//set no image available
}
return convertView;
}
private class MyViewHolder {
TextView posterTitle, posterAuthor, posterSynopsis, posterCategory, posterNumber, posterAltAuthors,posterPresentation;
ImageView imageView;
public MyViewHolder(View item) {
posterTitle = (TextView) item.findViewById(R.id.poster_title);
posterAuthor = (TextView) item.findViewById(R.id.poster_author);
posterAltAuthors = (TextView) item.findViewById(R.id.poster_altAuthors);
posterSynopsis = (TextView) item.findViewById(R.id.poster_synopsis);
posterCategory = (TextView) item.findViewById(R.id.poster_category);
posterNumber = (TextView) item.findViewById(R.id.poster_number);
posterPresentation = (TextView) item.findViewById(R.id.poster_presentation);
imageView = (ImageView) item.findViewById(R.id.poster_thumb);
}
}
}
我知道那是一大段代码,而且可能很难看。如果您能指出正确的方向来解决问题,那也会很有帮助。我正在使用日食。
我敢打赌,如果您注释掉那行 File imgFile = new File(...
(以及依赖于 imgFile
的代码),您的列表滚动将会得到改进。
这仍然是一个 I/O 操作,并且由于 getView()
在 UI 线程中运行,因此可能会出现问题。
你应该做的是:一旦你在 getView()
中有了 ImageView
,你就开始 AsyncTask
打开文件,解码它,并将位图分配给 ImageView
.
然后你必须处理这样的事情:ImageView
被回收了,但是任务还没有完成。
阅读这篇文章:Multithreading for Performance | Android Developers Blog。它处理来自远程服务器的图像,但所有原理仍然相同。
另外:这一行 SparseArray<String> categoryMap = db.getCategoryMap();
是否进行数据库查找?这也可能导致打嗝。
TL;DR getView()
需要快;将缓慢的操作放在非 UI 线程中。
我同意之前的回答,看起来创建新 File
是唯一挂起您的应用程序的繁重操作。
尝试使用一些图像加载库,例如Glide。它将简化您的代码,您也不必处理后台作业。图书馆会为你做这一切。
1.Import 图书馆,例如如果您使用 gradle 将其插入您的 build.gradle
文件:
repositories {
mavenCentral() // jcenter() works as well because it pulls from Maven Central
}
dependencies {
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.android.support:support-v4:19.1.0'
}
2.In 您的适配器将与加载图片相关的代码替换为:
Glide
.with(context)
.load(<full path to your local file>)
.into(mViewHolder.imageView);
3.And 就这些了。 Glide 将在内部处理回收和其他事情,它还针对快速加载和平滑滚动进行了优化。
图像相关进程确实在应用程序的 UIThread 上消耗了太多内存,您应该使用异步(后台)方法设置图像。或者,如果这对您来说很麻烦,那么有大量的库可以做到这一点。我特别推荐使用 Picasso,因为它非常动态、易于使用并且定期更新。加载图像需要一行代码:
Picasso.with(getApplicationContext()).load(<your image path or url or resource id>).into(YourImageView);
Picasso 也有多种图像选项供您选择。
数据库相关操作应该在单独的线程中完成。因为如果记录数较多,获取数据可能需要时间。
getView() 方法将被调用不止一次。我们可以说它将在循环中调用直到数组列表的大小 (getCount()).
第二件事:文件操作也应该单独完成thread.becauseIO任务也有些耗时。
解法:
SparseArray<String> categoryMap = db.getCategoryMap();
将这一行放在 getView() 方法之外,因为我在这一行中看不到任何列表相关参数。这应该在 constructor 或 separate thread.
运行 您的数据库操作和文件 IO 在单独的线程中使用 AsyncTask