Join FlipAndroid.COM Telegram Group: https://t.me/joinchat/F_aqThGkhwcLzmI49vKAiw


ListView – ImageLoader mueve Lista / Elementos en Scroll up

Estoy buscando una respuesta medio día, pero no puedo encontrar nada, aunque pensé que es un problema común. Mi problema: Tengo un ListView que tiene elementos de diferentes tamaños (alturas). Cada elemento contiene un ImageView . La imagen de este ImageView se carga en segundo plano mediante una clase ImageLoader:

 imageLoader.DisplayImage(url, holder.image); 

Si desplazo el ListView todo funciona bien. Las imágenes se cargan y se muestran (por supuesto en la parte inferior de la pantalla / lista).

Pero si me desplazo hacia arriba y la imagen no se almacena en caché más, por lo que el ImageLoader tiene que volver a cargar la imagen, el ListView salta / los elementos se mueven. Creo que es porque una nueva View se crea en la parte superior de la lista, con un ImageView con 0dp de altura. Si la imagen se carga y se fija en ImageView la altura de ImageView cambia automáticamente de 0dp al tamaño de la imagen. Esto podría empujar el ListView hacia abajo, creo.

He intentado guardar la altura de los ImageViews si una imagen está fijada y después fijar la altura del ImageView que se crea en la tapa a la altura ahorrada. Pero sin éxito

No sé si usted puede entender mi problema: DI esperanza tan 🙂

¡Gracias y que tenga un buen día!

EDIT: Añadido Clase ImageLoader

 public class ImageLoader { MemoryCache memoryCache=new MemoryCache(); FileCache fileCache; int size; Context context; private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>()); private HashMap<String, Integer> imagesSizes; ExecutorService executorService; Handler handler=new Handler();//handler to display images in UI thread public ImageLoader(Context context, int size){ fileCache=new FileCache(context); this.context = context; executorService=Executors.newFixedThreadPool(5); this.size = size; imagesSizes = new HashMap<String, Integer>(); } final int stub_id=R.color.transparent; public void DisplayImage(String url, ImageView imageView) { imageViews.put(imageView, url); Bitmap bitmap=memoryCache.get(url); if(bitmap!=null){ imageView.setImageBitmap(bitmap); saveImageSize(imageView, url); } else { queuePhoto(url, imageView); imageView.setImageResource(stub_id); setImageSize(imageView, url); } } public void DisplayImage(File file, ImageView imageView) { imageViews.put(imageView, file.getAbsolutePath()); Bitmap bitmap=memoryCache.get(file.getAbsolutePath()); if(bitmap!=null){ imageView.setImageBitmap(bitmap); saveImageSize(imageView, file.getAbsolutePath()); } else { queuePhoto(file, imageView); imageView.setImageResource(stub_id); setImageSize(imageView, file.getAbsolutePath()); } } private void saveImageSize(ImageView imageView, String url){ int height = imageView.getHeight(); imagesSizes.put(url, height); System.out.println("IMAGE SIZE: Save: " + url + " " + height ); } private void setImageSize(ImageView imageView, String url){ if(imageView != null && imagesSizes!=null && imagesSizes.containsKey(url)){ imageView.getLayoutParams().height = imagesSizes.get(url); imageView.requestLayout(); System.out.println("IMAGE SIZE: Set: " + url + " " + imagesSizes.get(url) ); } } private void queuePhoto(String url, ImageView imageView) { PhotoToLoad p=new PhotoToLoad(url, imageView); executorService.submit(new PhotosLoader(p)); } private void queuePhoto(File file, ImageView imageView) { PhotoToLoad p=new PhotoToLoad(file, imageView); executorService.submit(new PhotosLoader(p)); } public Bitmap getImage(String url){ return getBitmap(url); } private Bitmap getBitmap(String url) { File f=fileCache.getFile(url); //from SD cache Bitmap b = decodeFile(f); if(b!=null) return b; //from web try { Bitmap bitmap=null; URL imageUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection(); conn.setConnectTimeout(30000); conn.setReadTimeout(30000); conn.setInstanceFollowRedirects(true); InputStream is=conn.getInputStream(); OutputStream os = new FileOutputStream(f); Utils.CopyStream(is, os); os.close(); conn.disconnect(); bitmap = decodeFile(f); return bitmap; } catch (Throwable ex){ ex.printStackTrace(); if(ex instanceof OutOfMemoryError) memoryCache.clear(); return null; } } //decodes image and scales it to reduce memory consumption private Bitmap decodeFile(File f){ try { //decode image size BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; FileInputStream stream1=new FileInputStream(f); BitmapFactory.decodeStream(stream1,null,o); stream1.close(); //Find the correct scale value. It should be the power of 2. final int REQUIRED_SIZE=size; int width_tmp=o.outWidth, height_tmp=o.outHeight; int scale=1; while(true){ if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE) break; width_tmp/=2; height_tmp/=2; scale*=2; } //decode with inSampleSize BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inSampleSize=scale; FileInputStream stream2=new FileInputStream(f); Bitmap bitmap=BitmapFactory.decodeStream(stream2, null, o2); stream2.close(); return bitmap; } catch (FileNotFoundException e) { } catch (IOException e) { e.printStackTrace(); } return null; } //Task for the queue private class PhotoToLoad { public File file; public String url; public ImageView imageView; public PhotoToLoad(String u, ImageView i){ url=u; imageView=i; file = null; } public PhotoToLoad(File file, ImageView i){ url=file.getAbsolutePath(); imageView=i; this.file = file; } } class PhotosLoader implements Runnable { PhotoToLoad photoToLoad; PhotosLoader(PhotoToLoad photoToLoad){ this.photoToLoad=photoToLoad; } @Override public void run() { try{ if(imageViewReused(photoToLoad)) return; Bitmap bmp; if(photoToLoad.file== null){ bmp=getBitmap(photoToLoad.url); } else { bmp=decodeFile(photoToLoad.file); } memoryCache.put(photoToLoad.url, bmp); if(imageViewReused(photoToLoad)) return; BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad); handler.post(bd); }catch(Throwable th){ th.printStackTrace(); } } } boolean imageViewReused(PhotoToLoad photoToLoad){ String tag=imageViews.get(photoToLoad.imageView); if(tag==null || !tag.equals(photoToLoad.url)) return true; return false; } //Used to display bitmap in the UI thread class BitmapDisplayer implements Runnable { Bitmap bitmap; PhotoToLoad photoToLoad; public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;} public void run() { if(imageViewReused(photoToLoad)) return; if(bitmap!=null) photoToLoad.imageView.setImageBitmap(bitmap); else photoToLoad.imageView.setImageResource(stub_id); if(photoToLoad.file== null){ setImageSize(photoToLoad.imageView, photoToLoad.url); } else { setImageSize(photoToLoad.imageView, photoToLoad.file.getAbsolutePath()); } } } public void clearCache() { memoryCache.clear(); fileCache.clear(); } 

EDIT: Adaptador añadido:

  public class LazyNewPostsAdapter extends BaseAdapter implements Constants{ private Activity activity; private ArrayList<Image> data; private static LayoutInflater inflater=null; private ImageLoader imageLoader; private AsyncHelper helper; public static final int VIEW_TYPE_LOADING = 0; public static final int VIEW_TYPE_ACTIVITY = 1; private int imgposition; Handler fmHandler = null; Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { switch(msg.what){ case HANDLER_NEW_POST_VOTE_IMAGE: int position = msg.arg1; JSONObject json; try { json = new JSONObject((String) msg.obj); int success = json.getInt("success"); if(success == 1){ int i_id = json.getInt("i_id"); int votesUp = json.getInt("votes_up"); int votesDown = json.getInt("votes_down"); data.get(position).setVotesUp(votesUp); data.get(position).setVotesDown(votesDown); notifyDataSetChanged(); } } catch (JSONException e) { e.printStackTrace(); } break; case HANDLER_USER_REPORTED_IMAGE: JSONObject json2 = Utils.createJSON((String)msg.obj); System.out.println("REPORT IMAGE " + json2); if(json2 != null){ try { int success = json2.getInt("success"); if(success==1){ Toast.makeText(activity, "Image reported!", Toast.LENGTH_LONG).show(); } } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); } } break; case HANDLER_IMAGE_SIZE_AVAILABLE: String url = (String) msg.obj; int height = msg.arg1; int width = msg.arg2; int imgPosition = findImageOfCertainURL(url); System.out.println("GETVIEW HANDLER 1: IMAGE POSITION" + imgPosition); if(imgPosition != -1){ data.get(imgPosition).setHeight(height); data.get(imgPosition).setWidth(width); } Message copyMsg = new Message(); copyMsg.what = HANDLER_IMAGE_SIZE_AVAILABLE; copyMsg.arg1 = height; copyMsg.arg2 = width; copyMsg.obj = url; fmHandler.sendMessage(copyMsg); notifyDataSetChanged(); break; } }; }; private int findImageOfCertainURL(String url){ for(int i = 0; i <data.size();i++){ if((URL_FOLDER_IMAGES + data.get(i).getUrl()).equalsIgnoreCase(url)){ return i; } } return -1; } public LazyNewPostsAdapter(Activity a, ArrayList<Image> d, Handler fragmentHandler) { activity = a; data=d; inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); helper = new AsyncHelper(activity, handler); imageLoader=new ImageLoader(activity.getApplicationContext(), 600,handler) ; fmHandler = fragmentHandler; } public void updateData(ArrayList<Image> d){ data = d; notifyDataSetChanged(); } public int getCount() { return data.size()+1; } @Override public int getViewTypeCount() { return 2; } @Override public int getItemViewType(int position) { // TODO Auto-generated method stub return (position >= data.size()) ? VIEW_TYPE_LOADING : VIEW_TYPE_ACTIVITY; } @Override public boolean isEnabled(int position) { return getItemViewType(position) == VIEW_TYPE_ACTIVITY; } public Object getItem(int position) { return (getItemViewType(position) == VIEW_TYPE_ACTIVITY) ? data.get(position) : null; } public long getItemId(int position) { return (getItemViewType(position) == VIEW_TYPE_ACTIVITY) ? position : -1; } public View getFooterView(int position, View convertView, ViewGroup parent) { // the ListView has reached the last row TextView tvLastRow = new TextView(activity); if(AsyncHelper.isDownloadingImages){ tvLastRow.setHint("Requesting new Images.."); } else { tvLastRow.setHint("Reached the last row."); } tvLastRow.setGravity(Gravity.CENTER); return tvLastRow; } private OnClickListener myHotButtonClickListener = new OnClickListener() { @Override public void onClick(View v) { int position = (Integer) v.getTag(); if(ActivityMain.user.isLoggedIn()){ data.get(position).setThisUserVote(1); helper.vote_image(position, data.get(position).getId(), HOT); } else { Toast.makeText(activity, "Login to vote" , Toast.LENGTH_SHORT).show(); } } }; private OnClickListener myColdButtonClickListener = new OnClickListener() { @Override public void onClick(View v) { int position = (Integer) v.getTag(); if(ActivityMain.user.isLoggedIn()){ data.get(position).setThisUserVote(2); helper.vote_image(position, data.get(position).getId(), COLD); }else { Toast.makeText(activity, "Login to vote" , Toast.LENGTH_SHORT).show(); } } }; private OnClickListener myOptionsButtonClickListener = new OnClickListener() { @Override public void onClick(View v) { final int position = (Integer) v.getTag(); Runnable optionsRunnable = new Runnable() { @Override public void run() { openOptionsDialog(position); } }; handler.postDelayed(optionsRunnable, 500); } }; private void openOptionsDialog(final int imgposition){ final CharSequence[] items = {"Share", "Save", "Report"}; AlertDialog.Builder builder = new AlertDialog.Builder(activity); builder.setTitle("Options"); builder.setItems(items, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { if(item==0){ Utils.shareImage(activity, imageLoader.getImage(URL_FOLDER_IMAGES + data.get(imgposition).getUrl()) , data.get(imgposition).getTitle()); } else if(item==1) { Utils.saveImage(imageLoader.getImage(URL_FOLDER_IMAGES + data.get(imgposition).getUrl()),activity); } else if(item==2) { openReportDialog(imgposition); } } }); AlertDialog alert = builder.create(); alert.show(); } private void openReportDialog(final int imgposition){ AlertDialog.Builder builder = new AlertDialog.Builder(activity, R.style.AppCompatAlertDialogStyle); builder.setTitle("Report This Image?"); builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { helper.reportImage(data.get(imgposition).getId()); } }); builder.setNegativeButton("No", null); AlertDialog alert = builder.create(); alert.show(); } public View getView(final int position, View convertView, ViewGroup parent) { if (getItemViewType(position) == VIEW_TYPE_LOADING) { // display the last row return getFooterView(position, convertView, parent); } View vi=convertView; final ViewHolder holder; final Image img = data.get(position); boolean isViNull = false; if(convertView==null){ vi = inflater.inflate(R.layout.item_new_posts, null); holder = new ViewHolder(); isViNull = true; holder.title=(BrushTextView)vi.findViewById(R.id.tv_newposts_title); holder.image=(RatioImageView)vi.findViewById(R.id.iv_newposts_image); holder.uploader = (BrushTextView) vi.findViewById(R.id.tv_newposts_uploader); holder.upvotes = (BrushTextView) vi.findViewById(R.id.tv_newposts_upvotes); holder.downvotes= (BrushTextView) vi.findViewById(R.id.tv_newposts_downvotes); holder.options=(LinearLayout)vi.findViewById(R.id.ll_newposts_button_options); holder.iv_hot=(ImageView)vi.findViewById(R.id.iv_newposts_button_upvote); holder.iv_cold=(ImageView)vi.findViewById(R.id.iv_newposts_button_downvote); holder.ll_hot=(LinearLayout)vi.findViewById(R.id.ll_newposts_button_upvote); holder.ll_cold=(LinearLayout)vi.findViewById(R.id.ll_newposts_button_downvote); vi.setTag(holder); } else { holder = (ViewHolder) vi.getTag(); } if(img.getHeight() != 0 && img.getWidth() != 0){ holder.image.getLayoutParams().height = img.getHeight(); holder.image.getLayoutParams().width = img.getWidth(); holder.image.requestLayout(); } holder.ll_hot.setTag(position); holder.ll_hot.setOnClickListener(myHotButtonClickListener); holder.ll_cold.setTag(position); holder.ll_cold.setOnClickListener(myColdButtonClickListener); holder.options.setTag(position); holder.options.setOnClickListener(myOptionsButtonClickListener); changeVoteButtonImages(img, holder.iv_hot, holder.iv_cold); if(img.getTitle()!=null){ holder.title.setVisibility(View.VISIBLE); holder.title.setText(img.getTitle()); } else { holder.title.setVisibility(View.GONE); } holder.uploader.setText(img.getUploader()); holder.upvotes.setText(img.getVotesUp()+""); holder.downvotes.setText(img.getVotesDown()+""); String url = URL_FOLDER_IMAGES + img.getUrl(); System.out.println("GETVIEW NEW POST ADAPTER: height = " + img.getHeight() + " width = " + img.getWidth()); if(isViNull) System.out.println("GETVIEW CONVERTVIEW: VI: " +vi.getHeight()); imageLoader.DisplayImage(url, holder.image); return vi; } private void changeVoteButtonImages(Image image, ImageView upvote,ImageView downvote){ switch(image.getThisUserVote()){ case 0: upvote.setImageDrawable(activity.getResources().getDrawable(R.drawable.ic_post_hot_0)); downvote.setImageDrawable(activity.getResources().getDrawable(R.drawable.ic_post_cold_0)); break; case 1: downvote.setImageDrawable(activity.getResources().getDrawable(R.drawable.ic_post_cold_0)); upvote.setImageDrawable(activity.getResources().getDrawable(R.drawable.ic_post_hot_1)); break; case 2: upvote.setImageDrawable(activity.getResources().getDrawable(R.drawable.ic_post_hot_0)); downvote.setImageDrawable(activity.getResources().getDrawable(R.drawable.ic_post_cold_1)); break; } } static class ViewHolder { public BrushTextView title, uploader, upvotes, downvotes; public ImageView iv_hot,iv_cold; public LinearLayout options, ll_hot,ll_cold; public RatioImageView image; } } 

Y aquí está el Fragment , que contiene el ListView

 public class Fragment_New_Posts extends Fragment implements Constants, OnRefreshListener{ private static final String ARG_SECTION_NUMBER = "section_number"; private ListView list; private LazyNewPostsAdapter adapter; private Images images; private ArrayList<Image> imagesList; private SharedPreferences prefs; private Editor editor; private int visible_i_id; private SwipeRefreshLayout swipeLayout; private int option_image_filter; private AsyncHelper helper; private boolean onRefreshFired = false; private Parcelable state; public static Fragment_New_Posts newInstance(int sectionNumber) { Fragment_New_Posts fragment = new Fragment_New_Posts(); Bundle args = new Bundle(); args.putInt(ARG_SECTION_NUMBER, sectionNumber); fragment.setArguments(args); return fragment; } public static Fragment_New_Posts newInstance(int sectionNumber, Images imgs) { Fragment_New_Posts fragment = new Fragment_New_Posts(imgs); Bundle args = new Bundle(); args.putInt(ARG_SECTION_NUMBER, sectionNumber); fragment.setArguments(args); return fragment; } public void updateImages(Images images, boolean createNew, boolean loadOldImages){ int i_id_position = 0; this.images = images; if(!images.hasErrorOccured){ if(this.imagesList == null || createNew || onRefreshFired){ this.imagesList = images.getListOfImages(); } else { this.imagesList.addAll(images.getListOfImages()); } } if(loadOldImages){ i_id_position = this.images.getIDPosition(visible_i_id); } if(onRefreshFired){ swipeLayout.setRefreshing(false); onRefreshFired = false; } synchronized (adapter) { adapter.updateData(this.imagesList); if(list!=null && createNew){ if(loadOldImages){ list.setSelection(i_id_position); } else { list.setSelection(0); } } } } private Handler handler = new Handler(){ public void handleMessage(android.os.Message msg) { switch(msg.what){ case HANDLER_MAIN_IMAGE_UPDATE: String imagesString = (String) msg.obj; Images imgs = new Images(imagesString); updateImages(imgs, false, false); if(imgs.hasErrorOccured){ Toast.makeText(getActivity(), "Can\'t get new Images", Toast.LENGTH_SHORT).show(); } break; case HANDLER_IMAGE_SIZE_AVAILABLE: String url = (String) msg.obj; int height = msg.arg1; int width = msg.arg2; int imgPosition = findImageOfCertainURL(url); System.out.println("GETVIEW HANDLER 2: IMAGE POSITION" + imgPosition); if(imgPosition != -1){ imagesList.get(imgPosition).setHeight(height); imagesList.get(imgPosition).setWidth(width); } break; } }; }; private int findImageOfCertainURL(String url){ for(int i = 0; i <imagesList.size();i++){ if((URL_FOLDER_IMAGES + imagesList.get(i).getUrl()).equalsIgnoreCase(url)){ return i; } } return -1; } public Fragment_New_Posts() { } public Fragment_New_Posts(Images imgs) { this.images = imgs; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { System.out.println("NEW POST FRAGMENT ONCREATE"); View rootView = inflater.inflate(R.layout.fragment_new_posts, container, false); prefs = getActivity().getSharedPreferences(PREF_WOODU, getActivity().MODE_PRIVATE); editor = prefs.edit(); if(prefs!=null){ visible_i_id = prefs.getInt(PREF_NEW_POSTS_VISIBLE_I_ID, 0); option_image_filter = prefs.getInt(PHP_TAG_IMAGE_FILTER, 0); } swipeLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipe_container); swipeLayout.setColorSchemeColors(R.color.red_hot); swipeLayout.setOnRefreshListener(this); helper = new AsyncHelper(getActivity(), handler); imagesList = new ArrayList<Image>(); if(images != null){ imagesList = images.getListOfImages(); System.out.println("SAVE IMAGES: load: imgsList " + imagesList); } list = (ListView) rootView.findViewById(R.id.lv_new_posts); adapter = new LazyNewPostsAdapter(getActivity(), imagesList, handler); list.setAdapter(adapter); list.setOnScrollListener(new EndlessScrollListener() { @Override public void onLoadMore(int page, int totalItemsCount) { if(imagesList!=null && imagesList.size()!=0){ helper.getImageUpdate(imagesList.get(imagesList.size()-1).getId(), option_image_filter); } } }); if(images != null){ updateImages(images, true, true); } return rootView; } @Override public void onPause() { int lastVisposition = list.getLastVisiblePosition(); if(lastVisposition>=0 && lastVisposition < this.imagesList.size()){ visible_i_id = this.imagesList.get(list.getFirstVisiblePosition()).getId(); editor.putInt(PREF_NEW_POSTS_VISIBLE_I_ID, visible_i_id); editor.commit(); } super.onPause(); } @Override public void onResume() { super.onResume(); } @Override public void onDestroy() { if(images != null){ editor.putString(PREF_NEW_POSTS_CURRENT_IMAGES, Utils.createJSONStringFromImageArrayList(imagesList, editor)); editor.commit(); } super.onDestroy(); } @Override public void onRefresh() { onRefreshFired = true; if(prefs!=null){ option_image_filter = prefs.getInt(PHP_TAG_IMAGE_FILTER, 0); } helper.getImageUpdate(0, option_image_filter); } } 

Ahora aquí están muchas muchas información y código. Espero que no asuste a nadie para ayudar: D

¡¡Gracias!!

  • Infinite Auto Scroll ListView con velocidad de desplazamiento controlada
  • Cómo desplazarse mediante programación WebView de Android
  • Se barajaron las imágenes al desplazar un ListView con un ViewHolder
  • Android: Detectando cuando ScrollView Hits Bottom
  • Casilla de verificación desmarcada cuando hago scrolllistview en Android
  • Android ListView - detiene el desplazamiento en la posición de fila 'completa'
  • Android WebView desplácese hacia abajo
  • Touchend no dispara después de touchmove
  • 3 Solutions collect form web for “ListView – ImageLoader mueve Lista / Elementos en Scroll up”

    Por favor, por favor, por favor, no "reinventar la rueda" y utilizar cualquier lib hacia fuera allí para la carga de imágenes, almacenamiento en caché etc, como Picasso, Fresco, Glide etc ….

    Probablemente resuelto el problema .. Finalmente .. Mi idea de establecer la altura de la ImageView en getView no era tan malo .. problema era:

    Mi ImageView fue envuelto dentro de un CardView en XML .. Así que necesitaba cambiar la altura de la CardView lugar de la ImageView 🙂

    WeakHashMap … estás usando referencias débiles. Lo que hace bien eso. Borra los objetos cuando no se usa más. Por lo tanto, solo cambia a HashMap y deberías estar bien.

    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.