我正在寻找答案半天,但我找不到任何东西,虽然我认为这是一个常见的问题.我的问题:
我有一个ListView,它有不同大小(高度)的项目.每个项目都包含一个 ImageView.此 ImageView的图像由ImageLoader类在后台加载:
我有一个ListView,它有不同大小(高度)的项目.每个项目都包含一个 ImageView.此 ImageView的图像由ImageLoader类在后台加载:
imageLoader.DisplayImage(url,holder.image);
如果我向下滚动ListView一切正常.图像被加载并显示(当然在屏幕/列表的底部).
但是如果我向上滚动并且图像不再存储在缓存中,那么ImageLoader必须重新加载图像,ListView跳转/项目被移动.我认为这是因为在列表顶部创建了一个新的View,其ImageView的高度为0dp.如果图像已加载并设置为ImageView,则ImageView的高度会自动从0dp更改为图像的大小.我认为这会推动ListView.
如果设置了图像,我尝试保存ImageViews的高度,然后将在顶部创建的ImageView的高度设置为保存的高度.但没有成功..
我不知道你是否能理解我的问题:D我希望如此:)
感谢,并有一个愉快的一天!
编辑:添加了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,file.getAbsolutePath()); Bitmap bitmap=memoryCache.get(file.getAbsolutePath()); if(bitmap!=null){ imageView.setImageBitmap(bitmap); saveImageSize(imageView,file.getAbsolutePath()); } else { queuePhoto(file,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,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(); }
编辑:添加适配器:
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,COLD); }else { Toast.makeText(activity,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,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; } }
这里是Fragment,它包含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); } }
现在这里有很多信息和代码.我希望这不会吓到任何人提供帮助:D
谢谢!!
解决方法
请,请,请不要“重新发明轮子”并使用任何lib来进行图像加载,缓存等,如Picasso,Fresco,Glide等….