我有一个ListView
,其中每一行都有一些文本和两个ImageView
:每一行都相同,另一行取决于当前项目。
这是我的适配器:
mArrayAdapter(Context context, int layoutResourceId, ArrayList<Exhibition> data) {
super(context, layoutResourceId, data);
this.context = context;
this.layoutResourceId = layoutResourceId;
this.list = data;
this.originalList = data;
viewHolder = new ViewHolder();
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount() / 1024;
}
};
}
@Override
@NonNull
public View getView(final int position, View convertView, @NonNull final ViewGroup parent) {
View row;
final Exhibition ex;
if(convertView==null){
row = LayoutInflater.from(getContext()).inflate(R.layout.row,parent,false);
viewHolder.expand = (ImageView)row.findViewById(R.id.expand);
row.setTag(viewHolder);
}
else {
row = convertView;
viewHolder = (ViewHolder)row.getTag();
}
ex = list.get(position);
descr = (TextView)row.findViewById(R.id.descr);
ttl = (TextView)row.findViewById(R.id.title);
city = (TextView)row.findViewById(R.id.city);
dates = (TextView)row.findViewById(R.id.dates);
museum = (TextView)row.findViewById(R.id.location);
header = (ImageView)row.findViewById(R.id.hd);
ttl.setText(ex.name);
descr.setText(ex.longdescr);
museum.setText(ex.museum);
city.setText(ex.city);
final Bitmap bitmap = getBitmapFromMemCache(ex.key);
if (bitmap != null) {
header.setImageBitmap(bitmap);
} else {
header.setImageBitmap(ex.getHeader());
addBitmapToMemoryCache(ex.key,ex.header);
}
SimpleDateFormat myFormat = new SimpleDateFormat("dd/MM/yyyy", Locale.ITALY);
Date start = new Date(ex.getStart()), end = new Date(ex.getEnd());
String startEx = myFormat.format(start);
String endEx = myFormat.format(end);
String finalDate = getContext().getResources().getString(R.string.ex_date, startEx, endEx);
dates.setText(finalDate);
viewHolder.expand.setId(position);
if(position == selectedId){
descr.setVisibility(View.VISIBLE);
ttl.setMaxLines(Integer.MAX_VALUE);
dates.setMaxLines(Integer.MAX_VALUE);
museum.setMaxLines(Integer.MAX_VALUE);
city.setMaxLines(Integer.MAX_VALUE);
}else{
descr.setVisibility(View.GONE);
ttl.setMaxLines(1);
dates.setMaxLines(1);
museum.setMaxLines(1);
city.setMaxLines(1);
}
viewHolder.expand.setOnClickListener(this.onCustomClickListener);
return row;
}
public void setDescr(int p){
selectedId = p;
}
public void setOnCustomClickListener(final View.OnClickListener onClickListener) {
this.onCustomClickListener = onClickListener;
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
@Override
public int getCount()
{
return list.size();
}
@Override
public boolean isEnabled(int position)
{
return true;
}
@Override
public Exhibition getItem (int pos){
return list.get(pos);
}
void resetData() {
list = originalList;
}
private class ViewHolder {
ImageView expand,header;
}
@Override
@NonNull
public Filter getFilter() {
if (valueFilter == null) {
Log.d("SEARCH1","New filter");
valueFilter = new ValueFilter();
}
return valueFilter;
}
private class ValueFilter extends Filter {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
if(constraint == null || constraint.length() == 0){
results.values = originalList;
results.count = originalList.size();
}
else {
List<Exhibition> nExhList = new ArrayList<>();
for(Exhibition e : list){
Log.d("NAMEE",e.name + " " + constraint.toString());
if (e.getName().toUpperCase().contains(constraint.toString().toUpperCase()) || e.getCity().toUpperCase().contains(constraint.toString().toUpperCase())
||e.getMuseum().toUpperCase().contains(constraint.toString().toUpperCase()) || e.getLongDescription().toUpperCase().contains(constraint.toString().toUpperCase())
|| e.getDescription().toUpperCase().contains(constraint.toString().toUpperCase()) || e.getCategory().toUpperCase().contains(constraint.toString().toUpperCase())){
nExhList.add(e);
}
}
results.values= nExhList;
results.count=nExhList.size();
}
return results;
}
@Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
if(results.count==0){
notifyDataSetInvalidated();
}
else{
list = (ArrayList<Exhibition>)results.values;
notifyDataSetChanged();
}
}
}
第一个ImageView
是存储在Bitmap
变量中的Exhibition
。
第二个更改文本的可见性以获得类似可扩展的效果(因为现在我无法将ListView
转换为ExpandableListView
)。
我尝试了其他操作,例如缓存,AsyncTask
,删除了自定义点击侦听器,将所有内容都放在了ViewHolder
中,但是滚动充满了微滞。适配器有什么我不明白的错误吗?
答案 0 :(得分:2)
要使您的列表平滑,可以尝试以下方法,
答案 1 :(得分:1)
您可以采取几项措施来提高性能。
了解配置文件,它可以告诉您哪些功能是 被称为最多和/或需要最长时间才能完成的任务。这样,您可以决定在哪里投入时间来修复或更改代码。
请参见https://developer.android.com/studio/profile/android-profiler和 https://developer.android.com/studio/profile/
您滥用ViewHolder pattern。在您的代码中,适配器的ViewHolder
字段中只有一个viewHolder
实例。然后,您可以像常规的局部变量一样在getView()
函数内部使用此字段。
然后,即使row.findViewById()
不是convertView
,您也会多次呼叫null
。 findViewById()
调用很慢,并且视图持有者的优点是,扩展后每个视图只需要调用一次(在if
的convertView == null分支中)。
相反,每个行视图应该有1个视图持有者。请注意,您并不是在创建要与ViewHolder
一起分配的新setTag()
,而是要重用相同的对象。然后,应该使用descr
的字段代替ttl
,city
,ViewHolder
之类的变量,从而快速引用。
内存分配也很慢。
每次调用getView()
时,您也要创建对象,而您可以创建一个对象,而后只能重复使用。
一个这样的示例是SimpleDateFormat
,它可以在适配器构造函数中创建一次,并仅用于生成文本。
研究如何避免创建太多String
对象。使用字符串缓冲区或类似格式进行格式化。您没有显示Exhibition
类的源代码,因此尚不清楚为什么需要创建一个Date
对象并调用getStart()
和{{1} }。
如果getEnd()
对象的'start'和'end'字段从未用作Exhibition
,请考虑在JSON解析期间将它们变成不可变的long
s,而不是每个使用它们的时间。
未显示Date
类的源代码,因此我们无法确定Exhibition
函数的作用。如果有位图的下载和/或解码,将其移至后台线程(并在位图准备好后进行更新)将提高Exhitition.getHeader()
的滚动性能。
即使不需要,也有正在执行的呼叫。例如,ListView
末尾的“单击时”侦听器的分配。由于所有行都使用相同的侦听器,因此在进行通货膨胀时(getView()
为convertView
时,只能设置一次)。
您提到每个null
对象都有一个Exhibition
字段,该字段是在解析JSON时设置的。这意味着所有位图始终都在内存中。这意味着在这种情况下,不需要LRU缓存,因为始终会强烈引用位图。
这还意味着随着列表中项目数量的增加,所需的内存也随之增加。随着使用更多内存,垃圾收集(GC)需要更频繁地发生,并且GC速度很慢,并且可能导致结结或冻结。分析可以告诉您您遇到的冻结是否是由于GC造成的。
如果一次内存中只有几个位图,列表中当前可见的项目所需要的位图,以及其他一些位图,则位图缓存将很有用。如果在缓存中找不到所需的位图,则应从磁盘加载或从网络下载。
P.S。
请记住,您有一个公共Bitmap
函数,该函数仅将引用分配给该字段。如果您使用新的监听器进行调用,则当前代码将在所有未刷新并使用新引用进行更新的行上使用旧的监听器。