Форум программистов, компьютерный форум, киберфорум
Наши страницы

Программирование Android

Войти
Регистрация
Восстановить пароль
 
Grafity
59 / 4 / 1
Регистрация: 31.07.2013
Сообщений: 42
#1

Непонятный баг в ListView - Программирование Android

21.08.2013, 14:18. Просмотров 743. Ответов 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
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Непонятный баг в ListView (Программирование Android):

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

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

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

Данные из SQlite в ListView, обработка события ListView - Программирование Android
Ребят, есть БД, ListView. Надо при нажатии на строчку ListView вытащить данные int из БД. mList.setOnItemClickListener(new...

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

Копирование данных из 1 listView во 2 listView - Программирование Android
Нужно что бы при нажатии на один элемент в первом ListView он копировался во второй ListView причём многократно.Но я использую...

8
chizz
981 / 498 / 54
Регистрация: 19.03.2013
Сообщений: 3,094
Записей в блоге: 18
Завершенные тесты: 1
21.08.2013, 14:33 #2
можешь в ЛС весь проект скинуть?
0
Wenceslaus
Android Developer
130 / 130 / 4
Регистрация: 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
981 / 498 / 54
Регистрация: 19.03.2013
Сообщений: 3,094
Записей в блоге: 18
Завершенные тесты: 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 / 4
Регистрация: 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
Привет! Вот еще темы с ответами:

непонятный код - Программирование Android
ДОбрый день, подскажите пожалуйста, что тут к чему case MENU_GET_NEXT_PAGE: // increment the startFrom value and...

Непонятный феномен с дебагом - Программирование Android
1) эксцепшин поймать невозможно 2) первый брейкпоинт не срабатывает 3) неправильно обрабатывает условие И главное - раньше всё...

Использование ListView в ListView - Программирование Android
Всем привет. Можете показать как использовать ListView в ListView, с заполнением как внешнего ListView, так и внутреннего причем с...

Переход из Listview в Listview - Программирование Android
Как сделать, что бы когда нажимаешь в главном листвью на кнопку, открывалось новое нужное(т.е. то по которому тапнули) активити с другим...


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

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

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