Ответ 1
Проблема была в макете ListView.
Параметр layout_width
был установлен на wrap_content
, когда я изменил его на fill_parent
, проблема исчезла...
Каждый элемент в моем ListView
содержит ImageView
и TextView
, это будет заполнено удаленной информацией.
У меня есть URL-адрес для ImageView
, поэтому я запускаю AsyncTask
, который загружает изображение и вызывает setImageBitmap
с загруженным растровым изображением.
Это очень хорошо, но когда создается ListView, часто вызывается getView()
.
Он вызывает около 7 раз getView
для первых 10 строк (только 7 видимых). (So: 0, 1 и т.д., 10, 0, 1 и т.д.).
Затем я могу просто плавно прокручивать до 10-го элемента. Но после этого для каждой новой строки список снова вызывает примерно 7 раз getView
для первых 10 элементов. (Это приведет к задержке..)
Но когда я удаляю setImageBitmap
из AsyncTask
, все это не произойдет!
В чем может быть проблема? Может ли быть, что какой-то макет будет большим, что вызовет еще одну строку getViews?
Вот код:
<ListView
android:id="@+id/mylist"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentBottom="true"
android:layout_below="@+id/mydivider"
android:divider="@+color/mycolor"
android:dividerHeight="2dp"
android:fadingEdge="none"
android:fastScrollEnabled="true"
android:listSelector="@android:color/transparent"
android:scrollbars="horizontal"
android:scrollingCache="false"
android:smoothScrollbar="false" />
AsyncTask:
public static class MyTask extends AsyncTask<String, Integer, Bitmap> {
private LruCache<String, Bitmap> mMap;
private String mUri;
private int mPosition;
private ViewHolder mHolder;
public MyTask (ViewHolder holder, int position, LruCache<String, Bitmap> map) {
mMap = map;
mHolder = holder;
mPosition = position;
}
@Override
protected Bitmap doInBackground(String... url) {
mUri = url[0];
return getBitmapFromURL(mUri);
}
@Override
protected void onPostExecute(Bitmap b) {
if (b != null) {
mMap.put(mUri, b);
if (mPosition == mHolder.position) {
holder.image.setImageBitmap(b);
}
}
};
}
GetView()
row = convertView;
ViewHolder holder;
if (row == null) {
row = vi.inflate(R.layout.mylayout, null);
holder = new ViewHolder();
holder.image = (ImageView) row.findViewById(R.id.myimage);
holder.title = (TextView) row.findViewById(R.id.mytext);
row.setTag(holder);
} else {
holder = (ViewHolder) row.getTag();
}
holder.title.setText("text");
holder.image.setImageResource(R.drawable.mydefaulticon);
holder.position = position;
startMyTask(content, holder, position);
Дополнительная информация:
При создании нового представления указатель stacktrace, показанный getView, вызывается из ListView.makeAndAddView()
Но в бесполезной полосе getViews она исходит от ListView.measureHeigthOfChildren()
Итак, кажется, что макет изменяется, когда я устанавливаю битмап...
Проблема была в макете ListView.
Параметр layout_width
был установлен на wrap_content
, когда я изменил его на fill_parent
, проблема исчезла...
Я рекомендую вам посмотреть это видео http://www.youtube.com/watch?v=wDBM6wVEO70
в основном андроид будет вызывать getView несколько раз для вычисления высот и т.д., поэтому вам нужно реализовать какой-то кеш в вашем асинхронном режиме, проверьте http://code.google.com/p/android-query/, который имеет это, если вы не используете аутентификацию
Проблема заключается в том, что ваш ListView не знает, что из каждого элемента. При инициализации ListView будет измерять всех своих детей и использовать эту информацию. Что вы делаете с вашей AsyncTask, меняете размеры элементов в списке и не позволяете ListView полностью вычеркнуть. Это приведет к тому, что ListView будет вести себя странно.
Решение. Вы должны обновить ListView, уведомив адаптер, используя notifyDataSetChanged() в PostExecute вашей AsyncTask. Это позволит ListView рисовать себя с новой информацией.
Надеюсь, это сработает!
Несколько полезных ссылок:
http://thinkandroid.wordpress.com/2012/06/13/lazy-loading-images-from-urls-to-listviews/
Извините за поздний ответ. Я столкнулся с той же проблемой в моем адаптере для виджета галереи. Я использовал Universal Image Loader. Решение Martijn работало для меня. Все, что мне нужно было сделать, это добавить notifyDataSetChanged(), когда изображение было полностью загружено и было установлено изображениеView.
Я попытался использовать fill_parent как height в ListView, но я не смог достичь его совершенства, но как бы я его не пытался, не создавая convertViews, если он уже существует. он отлично работает в моем appoach.
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView != null)
return convertView;
else {
//your code...
}
}