Форум программистов, компьютерный форум, киберфорум
Наши страницы
Программирование Android
Войти
Регистрация
Восстановить пароль
 
Рейтинг 5.00/4: Рейтинг темы: голосов - 4, средняя оценка - 5.00
Grafity
59 / 4 / 1
Регистрация: 31.07.2013
Сообщений: 42
#1

Непонятный баг в ListView

21.08.2013, 14:18. Просмотров 776. Ответов 8
Метки нет (Все метки)

В общем, суть такова:

Имеется список контактов в ListView. Каждый пункт содержит аватарку, имя и телефон.

Адаптер ListView:

Кликните здесь для просмотра всего текста

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
class ContactAdapter extends BaseAdapter{
        
        private class ViewHolder {
            public TextView name;
            public TextView telephone;
            public ImageView image;
        }
 
        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return userContacts.size();
        }
 
        @Override
        public Object getItem(int position) {
            // TODO Auto-generated method stub
            return userContacts.get(position);
        }
 
        @Override
        public long getItemId(int position) {
            // TODO Auto-generated method stub
            return position;
        }
 
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            
            View view = convertView;
            final Contact p = getContact(position);
            
            listener = new OnTouchListener() {
                 
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    // TODO Auto-generated method stub
                    switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                            from = event.getX();
                            break;
                        case MotionEvent.ACTION_MOVE:
                            
                            Log.i("log_tag", "Движение");
                            Log.i("log_tag", "Флаг действия: " + action_flag);
                            
                            if (action_flag == false){
                                
                                ListViewAnalizeSwipe(from, event.getX(), v);
                                
                            }
                            
                            break;
                        case MotionEvent.ACTION_UP:
                            
                            Log.i("log_tag","Отпускание");
                            action_flag = false;
                            
                    }
                    return true;
                }
            };
            
            final ViewHolder holder;
            
            
            if (convertView == null) {
                
                view = getLayoutInflater().inflate(R.layout.item, parent, false);
                
                holder = new ViewHolder();
                holder.name = (TextView) view.findViewById(R.id.name_tv);
                holder.telephone = (TextView) view.findViewById(R.id.tel_tv);
                holder.image = (ImageView) view.findViewById(R.id.contact_icon);
                view.setTag(holder);
                
            }
            else{
                holder = (ViewHolder) view.getTag();
            }
            
            if (view != convertView) {
                view.setOnTouchListener(listener);
            }
            
            holder.name.setText(p.name);
            holder.telephone.setText(p.tel);
            
            Log.i("AcyncTask", "Устанавливаем аватар контакту: " + p.id + " " + p.name);
            
            BitmapAsyncLoad bmLoad = new BitmapAsyncLoad(holder.image);
            bmLoad.execute((int) p.photo_id);
            
            return view;
        }
        
        Contact getContact(int position) {
            return ((Contact) getItem(position));
          }
        
        class BitmapAsyncLoad extends AsyncTask<Integer, Void, Bitmap>{
            
            private final WeakReference<ImageView> imageViewReference;
            
            public BitmapAsyncLoad(ImageView imageView) {
                imageViewReference = new WeakReference<ImageView>(imageView);
            }
 
            @Override
            protected Bitmap doInBackground(Integer... imageDataRow) {
                // TODO Auto-generated method stub
                Cursor c = getContentResolver().query(ContactsContract.Data.CONTENT_URI, new String[] {
                        ContactsContract.CommonDataKinds.Photo.PHOTO
                    }, ContactsContract.Data._ID + "=?", new String[] {
                        Integer.toString(imageDataRow[0])
                    }, null);
                    byte[] imageBytes = null;
                    if (c != null) {
                        if (c.moveToFirst()) {
                            imageBytes = c.getBlob(0);
                        }
                        c.close();
                    }
                    if (imageBytes != null) {
                        return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length); 
                    } else {
                        return null;
                    }
                    
                
                //return queryContactImage(imageDataRow[0]);
            }
            
            @Override
            protected void onPostExecute(Bitmap bitmap) {
                super.onPostExecute(bitmap);
                
                if (isCancelled()) {
                    Log.i("onPostExecute", "Отмена");
                    bitmap = null;
                }
                if (imageViewReference != null) {
                    ImageView imageView = imageViewReference.get();
                    if (bitmap != null){ 
                        imageView.setImageBitmap(bitmap);
                        Log.i("onPostExecute", "Установка аватара");
                    }
                    else{
                        imageView.setImageDrawable(getResources().getDrawable(R.drawable.standart_contact));
                        Log.i("onPostExecute", "Аватар отсутствует");
                    }
                }
            }
            
        }
 
    }


Проблема заключается в следующем:

При прокрутке списка дохожу до первого контакта, который имеет аватар. Аватар отображается, все нормально. Но потом начинается такая фигня: адаптер начинает заново перебирать элементы списка, которые видно на самой верхней позиции списка. Причем, в первых двух прогонах на один контакт больше.
Кроме того, после того, как аватарка контакта скрывается из вида, точно такая-же аватарка появляется у контакта, который следующим появиться на экране. Причем, судя по логам, никто туда эту картинку не ставит, она появляется сама.
Пока забил этот баг принудительной вставкой картинки-заглушки из onPostExecute. Но если быстро листать список, то видно эти дергания с заменами картинок.

Вот лог:

Кликните здесь для просмотра всего текста


08-21 11:55:45.408: I/AcyncTask(16646): Устанавливаем аватар контакту: 87 Дамир
08-21 11:55:45.503: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:51.758: I/AcyncTask(16646): Устанавливаем аватар контакту: 275 Дамир
08-21 11:55:51.783: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:55.768: I/AcyncTask(16646): Устанавливаем аватар контакту: 198 Дашка
08-21 11:55:55.793: I/onPostExecute(16646): Установка аватара
08-21 11:55:55.803: I/AcyncTask(16646): Устанавливаем аватар контакту: 260 NCC
08-21 11:55:55.808: I/AcyncTask(16646): Устанавливаем аватар контакту: 738 NCC
08-21 11:55:55.808: I/AcyncTask(16646): Устанавливаем аватар контакту: 691 Андрей ВАЗ
08-21 11:55:55.808: I/AcyncTask(16646): Устанавливаем аватар контакту: 258 Андрей Мастер
08-21 11:55:55.813: I/AcyncTask(16646): Устанавливаем аватар контакту: 730 Андрей НашТольятти
08-21 11:55:55.813: I/AcyncTask(16646): Устанавливаем аватар контакту: 681 Анискин Анатолий
08-21 11:55:55.813: I/AcyncTask(16646): Устанавливаем аватар контакту: 73 Антон Литта
08-21 11:55:55.823: I/AcyncTask(16646): Устанавливаем аватар контакту: 260 NCC
08-21 11:55:55.823: I/AcyncTask(16646): Устанавливаем аватар контакту: 738 NCC
08-21 11:55:55.828: I/AcyncTask(16646): Устанавливаем аватар контакту: 691 Андрей ВАЗ
08-21 11:55:55.828: I/AcyncTask(16646): Устанавливаем аватар контакту: 258 Андрей Мастер
08-21 11:55:55.828: I/AcyncTask(16646): Устанавливаем аватар контакту: 730 Андрей НашТольятти
08-21 11:55:55.833: I/AcyncTask(16646): Устанавливаем аватар контакту: 681 Анискин Анатолий
08-21 11:55:55.833: I/AcyncTask(16646): Устанавливаем аватар контакту: 73 Антон Литта
08-21 11:55:55.843: I/AcyncTask(16646): Устанавливаем аватар контакту: 260 NCC
08-21 11:55:55.848: I/AcyncTask(16646): Устанавливаем аватар контакту: 738 NCC
08-21 11:55:55.848: I/AcyncTask(16646): Устанавливаем аватар контакту: 691 Андрей ВАЗ
08-21 11:55:55.853: I/AcyncTask(16646): Устанавливаем аватар контакту: 258 Андрей Мастер
08-21 11:55:55.853: I/AcyncTask(16646): Устанавливаем аватар контакту: 730 Андрей НашТольятти
08-21 11:55:55.853: I/AcyncTask(16646): Устанавливаем аватар контакту: 681 Анискин Анатолий
08-21 11:55:55.863: I/AcyncTask(16646): Устанавливаем аватар контакту: 260 NCC
08-21 11:55:55.863: I/AcyncTask(16646): Устанавливаем аватар контакту: 738 NCC
08-21 11:55:55.863: I/AcyncTask(16646): Устанавливаем аватар контакту: 691 Андрей ВАЗ
08-21 11:55:55.868: I/AcyncTask(16646): Устанавливаем аватар контакту: 258 Андрей Мастер
08-21 11:55:55.868: I/AcyncTask(16646): Устанавливаем аватар контакту: 730 Андрей НашТольятти
08-21 11:55:55.873: I/AcyncTask(16646): Устанавливаем аватар контакту: 681 Анискин Анатолий
08-21 11:55:55.908: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:55.913: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:55.913: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:55.913: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:55.913: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:55.913: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:55.918: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:55.918: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:55.978: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:55.988: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:55.998: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:56.003: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:56.008: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:56.013: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:56.023: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:56.028: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:56.033: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:56.223: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:56.233: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:56.238: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:56.243: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:56.248: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:56.258: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:56.258: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:56.263: I/onPostExecute(16646): Аватар отсутствует
08-21 11:55:56.273: I/onPostExecute(16646): Аватар отсутствует


Добавлено через 21 минуту
Вообще-то сейчас исследовал еще раз - баг может вылезать и после контактов, у которых нет аватара. GetView() так-же четыре раза перебирает все верхние элементы...

Добавлено через 2 часа 0 минут
Баг связан с AsyncTask. Кто подскажет, где поправить?
Первый раз с AsyncTask работаю...
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
21.08.2013, 14:18
Ответы с готовыми решениями:

Баг с items при прокручивании ListView
Суть такая, что выводится список ListView. Там items могут быть двух разных...

ListView. После добавления кнопки перестал быть доступен сам ListView
Кастомизировал ListView с помощью нескольких TextView. Но после того, как...

Listview вывод данных и переход по нажатию на другой Listview SQLite
Доброго извините за название темы, долго не мог сформировать название, В общем...

Данные из SQlite в ListView, обработка события ListView
Ребят, есть БД, ListView. Надо при нажатии на строчку ListView вытащить данные...

Передача данных из ListView в другой Activity в ListView
Добрый день. Пытаюсь разобраться с передачей данных с ListView, но выходит что...

8
chizz
983 / 501 / 101
Регистрация: 19.03.2013
Сообщений: 3,106
Записей в блоге: 19
Завершенные тесты: 1
21.08.2013, 14:33 #2
можешь в ЛС весь проект скинуть?
0
Wenceslaus
Android Developer
130 / 130 / 6
Регистрация: 05.07.2013
Сообщений: 205
21.08.2013, 14:40 #3
Grafity, всё дело в кеше. Система кеширует элемент контакта и при прокручивании списка верхний, исчезающий элемент, подставляется в низ (вместо нового, появляющегося). Фактически существует всего 5-7 элементов, которые просто меняются местами. Посему, все настройки (текст и изображения) остаются и отображаются в определённый момент. Что бы этого не происходило, поставьте
Java
1
imageView.setImageDrawable(getResources().getDrawable(R.drawable.standart_contact));
как аватар по умолчанию (рядом с именем и номером телефона).
0
Grafity
59 / 4 / 1
Регистрация: 31.07.2013
Сообщений: 42
21.08.2013, 15:05  [ТС] #4
Цитата Сообщение от Wenceslaus Посмотреть сообщение
Grafity, всё дело в кеше. Система кеширует элемент контакта и при прокручивании списка верхний, исчезающий элемент, подставляется в низ (вместо нового, появляющегося). Фактически существует всего 5-7 элементов, которые просто меняются местами. Посему, все настройки (текст и изображения) остаются и отображаются в определённый момент. Что бы этого не происходило, поставьте
Java
1
imageView.setImageDrawable(getResources().getDrawable(R.drawable.standart_contact));
как аватар по умолчанию (рядом с именем и номером телефона).
По умолчанию у меня установлено это изображение в imageView, оно перебивается все равно.

Тут проблема в неправильной работе AsynkTask. Как-то я его не так вызываю и что-то не так делаю.
0
chizz
983 / 501 / 101
Регистрация: 19.03.2013
Сообщений: 3,106
Записей в блоге: 19
Завершенные тесты: 1
21.08.2013, 15:10 #5
Попробуй вызывать так:

new BitmapAsyncLoad(holder.image).execute((int) p.photo_id);

Вместо двух строчек
BitmapAsyncLoad bmLoad = new BitmapAsyncLoad(holder.image);
bmLoad.execute((int) p.photo_id);

Хотя у меня asynctask как static определен.. да и вообще вряд ли в это дело
0
Wenceslaus
Android Developer
130 / 130 / 6
Регистрация: 05.07.2013
Сообщений: 205
21.08.2013, 15:12 #6
Grafity, где по умолчанию установлено? В xml? Я говорю о программной установке, как в случае с телефоном и именем. Вот так:
Java
1
2
3
holder.name.setText(p.name);
holder.telephone.setText(p.tel);
holder.image.setImageDrawable(getResources().getDrawable(R.drawable.standart_contact));
1
Grafity
59 / 4 / 1
Регистрация: 31.07.2013
Сообщений: 42
21.08.2013, 15:42  [ТС] #7
Цитата Сообщение от chizz Посмотреть сообщение
Попробуй вызывать так:

new BitmapAsyncLoad(holder.image).execute((int) p.photo_id);

Вместо двух строчек
BitmapAsyncLoad bmLoad = new BitmapAsyncLoad(holder.image);
bmLoad.execute((int) p.photo_id);
Не помогло.

Гугл дает здесь вот такой готовый метод:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Using an AsyncTask to load the slow images in a background thread
new AsyncTask<ViewHolder, Void, Bitmap>() {
    private ViewHolder v;
 
    @Override
    protected Bitmap doInBackground(ViewHolder... params) {
        v = params[0];
        return mFakeImageLoader.getImage();
    }
 
    @Override
    protected void onPostExecute(Bitmap result) {
        super.onPostExecute(result);
        if (v.position == position) {
            // If this item hasn't been recycled already, hide the
            // progress and set and show the image
            v.progress.setVisibility(View.GONE);
            v.icon.setVisibility(View.VISIBLE);
            v.icon.setImageBitmap(result);
        }
    }
}.execute(holder);
Я пока не могу сообразить как это прикрутить к моему проекту. Пробовал - не получилось.
Содержимое моего doInBackground я запихивал туда, это понятно. Но вот не понятно с position.

Добавлено через 5 минут
Цитата Сообщение от Wenceslaus Посмотреть сообщение
Grafity, где по умолчанию установлено? В xml? Я говорю о программной установке, как в случае с телефоном и именем. Вот так:
Java
1
2
3
holder.name.setText(p.name);
holder.telephone.setText(p.tel);
holder.image.setImageDrawable(getResources().getDrawable(R.drawable.standart_contact));
Сделал, но не помогло. Тут явно проблема в асинхронке.
Нашел на ХешКоде очень похожий пример. Там написали несколько решений, но я толком ничего не понял

Добавлено через 19 минут
В идеале, прикрутить бы сюда ImageLoader и бед не знать, но я не знаю, как подсунуть ему битмапы.
0
V0v1k
1160 / 984 / 1
Регистрация: 28.06.2012
Сообщений: 3,462
21.08.2013, 21:07 #8
Wenceslaus все правильно сказал, в getView, перед запуском таска, нужно проставлять дефолтную картинку.
еще проблемы могут быть если долго загружается картинка, она может стать неактуальной для ImageView, поэтому нужно в ImageView запихивать p.photo_id методом setTag, а перед вставкой картинки проверять не изменилась ли photo_id.
1
Grafity
59 / 4 / 1
Регистрация: 31.07.2013
Сообщений: 42
22.08.2013, 11:17  [ТС] #9
Цитата Сообщение от V0v1k Посмотреть сообщение
Wenceslaus все правильно сказал, в getView, перед запуском таска, нужно проставлять дефолтную картинку.
еще проблемы могут быть если долго загружается картинка, она может стать неактуальной для ImageView, поэтому нужно в ImageView запихивать p.photo_id методом setTag, а перед вставкой картинки проверять не изменилась ли photo_id.
Словил ClassCastExeption...

Java
1
2
3
4
5
6
7
8
9
10
11
public View getView(final int position, View convertView, ViewGroup parent) {
 . . .
holder.name.setText(p.name);
holder.telephone.setText(p.tel);
holder.image.setTag(p.photo_id);
holder.image.setImageDrawable(getResources().getDrawable(R.drawable.standart_contact));
 
new BitmapAsyncLoad(holder.image).execute((int) p.photo_id);
            
return view;
        }
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class BitmapAsyncLoad extends AsyncTask<Integer, Void, Bitmap>{
            
            private final WeakReference<ImageView> imageViewReference;
            private long photo_id;
            
            public BitmapAsyncLoad(ImageView imageView) {
                imageViewReference = new WeakReference<ImageView>(imageView);
            }
 
            @Override
            protected Bitmap doInBackground(Integer... imageDataRow) {
                // TODO Auto-generated method stub
                photo_id = imageDataRow[0];
                Log.d("doInBackground", "Грузим фото. ID = " + photo_id);
                return loadContactPhoto(photo_id);
            }
            
            @Override
            protected void onPostExecute(Bitmap bitmap) {
                super.onPostExecute(bitmap);
                
                if (isCancelled()) {
                    Log.i("onPostExecute", "Отмена");
                    bitmap = null;
                }
                
                if (imageViewReference != null && ((String)imageViewReference.get().getTag()).equals(photo_id) && bitmap != null) {
                    imageViewReference.get().setImageBitmap(bitmap);
                    Log.i("onPostExecute", "Установка аватара");
       
                }
 
            }
            
        }
Добавлено через 6 минут
Все! Отбой.

Все шикарно работает!

Немного поменял onPosnExecute()

Java
1
2
3
4
5
6
7
ImageView imageView = imageViewReference.get();
                
                if (imageViewReference != null && imageView.getTag().equals(photo_id) && bitmap != null) {
                    imageViewReference.get().setImageBitmap(bitmap);
                    Log.i("onPostExecute", "Установка аватара");
                    
                }
Добавлено через 13 минут
Всем спасибо большое за помощь!
0
22.08.2013, 11:17
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
22.08.2013, 11:17

ListView, ToggleButton, SQLite, CursorAdapter. Как найти idItem в ListView, где была нажата ToggleButton?
Добрый день. Затрудняюсь понять один момент: У меня есть ListView,...

Копирование данных из 1 listView во 2 listView
Нужно что бы при нажатии на один элемент в первом ListView он копировался во...

непонятный код
ДОбрый день, подскажите пожалуйста, что тут к чему case MENU_GET_NEXT_PAGE:...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru