#java #android #listview #android-listview #android-asynctask
#java #Android #listview #android-listview #android-asynctask
Вопрос:
Я разрабатываю приложение для чата, в котором я реализовал отправку изображений. Итак, без изображений все работает нормально. Но теперь я сталкиваюсь с проблемами при отображении изображений, которые отправлены / получены. Для этого я сначала загрузил изображение и сохранил его во внешнем хранилище. Моя идея заключалась в том, чтобы просто извлечь растровое изображение, установить его в методе imageview
on getView
. Ничего плохого в извлечении изображения и отображении, но listview все портит.
Это мой код-
ListView getView()
ViewHolder holder = null;
if(convertView == null ){
convertView = inflater.inflate(R.layout.chat_list, null);
holder = new ViewHolder();
holder.mMsg = (TextView) convertView.findViewById(R.id.text);
holder.mDate = (TextView) convertView.findViewById(R.id.text1);
holder.mLinLay=(LinearLayout) convertView.findViewById(R.id.linSide1);
holder.mRelLay=(RelativeLayout) convertView.findViewById(R.id.relSide);
holder.img=(ImageView)convertView.findViewById(R.id.image);
holder.progCircle=(ProgressBar)convertView.findViewById(R.id.pbHeaderProgress);
convertView.setTag(holder);
} else{
holder = (ViewHolder) convertView.getTag();
}
holder.position=position;
holder.img.setTag(position "img");
HashMap<String, String> hm = list.get(position);
String date=hm.get("date");
long l = Long.parseLong(date);
long unixTime = System.currentTimeMillis();
CharSequence datedate=DateUtils.getRelativeTimeSpanString(l, unixTime, 1);
String status = hm.get("status");
String who = hm.get("who");
if(hm.get("message").contains("[[pic_message]]"))
{
String picName=hm.get("message").replace("[[pic_message]]", "");
final String picPath1=Environment.getExternalStorageDirectory() "/App/sent_pics/" picName ".jpg";
File file = new File(picPath);
if(file.exists())
{
new AsyncTask<ViewHolder, Void, Bitmap>() {
private ViewHolder v;
Bitmap bm;
@Override
protected Bitmap doInBackground(ViewHolder... params) {
v = params[0];
bm = decodeSampledBitmapFromResource(picPath1,100,100);
return bm;
}
@Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
if (v.img.getTag().equals(position "img")) {
v.img.setImageBitmap(result);
}
}
}.execute(holder);
}else{
holder.img.setImageBitmap(null);
}
}else{
holder.img.setImageBitmap(null);
Spannable msg =getSmiledText(getApplicationContext(),hm.get("message"));
holder.mMsg.setText(msg);
}
holder.mDate.setText(datedate);
return convertView;
В чем проблема-
Отображается listview, но изображения отображаются в случайных элементах listview. Я много искал такого рода проблемы, пробовал их, но все равно безуспешно. Код asyntask взят из Google- Используйте фоновый поток.. Как вы видите выше, я не устанавливал никаких holder.mMsg.setText(msg);
, но все же всякий раз, когда изображения отображаются, они сопровождаются сообщением, что нежелательно. Также прокрутка немного отстает при отображении изображений. Я видел много приложений для обмена сообщениями, все они работают так гладко, и элементы никогда не перепутываются. Итак, что они используют.Кроме того, при поиске в Google этой проблемы в одном ответе предлагалось удалить converView if else
, и тогда все будет хорошо. Но это приведет к проблемам с производительностью, которых я не хочу.
Итак, мой вопрос в том, что мне делать? Как приложения для чата, такие как hangouts, bbm, WhatsApp, достигают этого так гладко?
Редактировать
Картинки отображаются без проблем, но главная проблема заключается в том, что они воспроизводятся в случайном порядке.
Вот так — я прокручиваю до появления сообщения об изображении, они отображаются. Затем я снова прокручиваю, и теперь изображение отображается в другом элементе listview. Я предполагаю, что тот же вид (который изначально содержал изображение) теперь используется другими, но изображение остается там. Как я могу это исправить? И сделать listview плавным (даже с изображениями), как в приложениях обмена мгновенными сообщениями?
Ответ №1:
Это потому, что Android ListView повторно использует представления, поэтому, когда вы Asynctask
выполняете donInBackground, пользователь уже делает что-то еще, и ваш код ничего об этом не знает. (Таким образом, он поместит изображение)
Я бы использовал Picasso
, чтобы избежать проблемы и позволить кому-то другому справиться с вашей проблемой.
Я решил эту проблему в своем приложении (контактный телефон) с помощью:
- Кэш, чтобы избежать отправки нескольких запросов на одно и то же изображение, даже если у меня уже есть в памяти его изображение
- Я отправляю
WeakReference<>
в Asynctask, поэтому, когда это будет сделано, проверьте, является ли ImageView все еще действительным, и поместите в него растровое изображение. Обновите кэш. - (Специфично для меня) Поскольку иногда у меня могло случиться так, что у контакта нет фотографии, я просто создал еще один список со всеми пользователями без фотографии (поэтому я не пытаюсь его загрузить)
-
Каждый раз, когда вызывается .getview, проверяйте, находится ли изображение внутри кэша, и если оно находится внутри кэша, поместите его непосредственно или запустите asynctask.
Bitmap bitmap = cache.get(number); if (bitmap == null) { Log.d(TAG, "I will download image for " name "."); // i set a dummy image to avoid to show the wrong picture holder.contactPhoto.setImageResource(R.drawable.ic_action_person); loadUserProfilePhoto(number, holder.contactPhoto); } else { // show it directly Log.d(TAG, "I already have " name " in cache."); holder.contactPhoto.setImageBitmap(bitmap); }
Если вы видите, может случиться так, что вместо фотографии пользователя будет отображаться другое изображение, пока оно все еще загружается, поэтому я установил фиктивное изображение вместо старой фотографии.
Это не самая лучшая реализация, но она предназначена для того, чтобы показать вам, что реализовать ее самостоятельно и сделать это правильно может быть очень сложно.
Прочитайте документацию Picasso и посмотрите, как это можно реализовать в вашем коде (это просто, это всего одна строка.)
Комментарии:
1. Спасибо за библиотеку и ваш ответ. Похоже, это решит проблемы, просто надеюсь, что так оно и будет. Я это реализую и дам вам знать.
2. эй, я попробовал picasso, загрузка работает нормально, но все же изображения сначала отображаются в нужном месте, но когда я прокручиваю после этого, изображения отображаются в неправильных элементах listview. Пожалуйста, помогите мне с этой проблемой.
Ответ №2:
Проблема в listview, который постоянно обновляет его.
Итак, весь ваш код в порядке, но просто установите изображение по умолчанию в holder.img вместо того, чтобы делать его нулевым holder.img.setImageBitmap (null);
И, возможно, также в асинхронном режиме, вам нужно установить default_image во время загрузки вашего изображения.
Ответ №3:
Если вы хотите отображать изображения на лету, я думаю, вам следует использовать SurfaceView. Это намного быстрее и имеет более высокую частоту обновления. Я создал приложение для потокового видео, в котором я пытался отобразить каждый видеокадр в ImageView, но это не удалось. Но когда я опробовал SurfaceView, все получилось отлично.