延迟将图像下载到 gridView
Posted
技术标签:
【中文标题】延迟将图像下载到 gridView【英文标题】:Lazy download images into gridView 【发布时间】:2012-10-27 06:10:39 【问题描述】:在我的应用程序中,我需要从 url 下载大量图片并将它们显示在 gridView 中。 (可以在 1-200 张图片之间)。我不想一次下载所有图片。我读到关于延迟下载的文章,我的问题是:我可以只获取 Json 的一部分,在不同的线程中下载图片,并且只有当用户向下滚动 gridView,我才会继续Json的其他部分,等等?
编辑:再次嗨。我想在这个 gridView 中实现多选,我很难在适配器的 getView() 方法中实现代码。这是我正在使用的示例:example。如何在我的 getView() 方法中组合此代码:
public View getView(int position, View convertView, ViewGroup parent)
CheckableLayout l;
ImageView i;
if (convertView == null)
i = new ImageView(Grid3.this);
i.setScaleType(ImageView.ScaleType.FIT_CENTER);
i.setLayoutParams(new ViewGroup.LayoutParams(50, 50));
l = new CheckableLayout(Grid3.this);
l.setLayoutParams(new GridView.LayoutParams(GridView.LayoutParams.WRAP_CONTENT,
GridView.LayoutParams.WRAP_CONTENT));
l.addView(i);
else
l = (CheckableLayout) convertView;
i = (ImageView) l.getChildAt(0);
ResolveInfo info = mApps.get(position);
i.setImageDrawable(info.activityInfo.loadIcon(getPackageManager()));
return l;
public class CheckableLayout extends FrameLayout implements Checkable
private boolean mChecked;
public CheckableLayout(Context context)
super(context);
public void setChecked(boolean checked)
mChecked = checked;
setBackgroundDrawable(checked ?
getResources().getDrawable(R.drawable.blue)
: null);
public boolean isChecked()
return mChecked;
public void toggle()
setChecked(!mChecked);
我的 getView() 代码:
public View getView(int position, View convertView, ViewGroup parent)
// TODO Auto-generated method stub
ViewHolder holder;
View vi = convertView;
if(convertView == null)
vi = inflater.inflate(com.egedsoft.instaprint.R.layout.item_clickable, null);
holder = new ViewHolder();
holder.imgPhoto = (ImageView)vi.findViewById(com.egedsoft.instaprint.R.id.imageClickable);
vi.setTag(holder);
else
holder = (ViewHolder) vi.getTag();
if (!arrayUrls.get(position).getThumbnailUrl().isEmpty())
imageLoader.DisplayImage(arrayUrls.get(position).getThumbnailUrl(), holder.imgPhoto);
return vi;
【问题讨论】:
您如何请求获取 JSON 数据?源是否支持分页? 我从源获得“下一个 url”,以防有更多照片要加载 @Siddharth Lele:如果您能查看我编辑的问题,我将不胜感激。非常感谢您的帮助。 恐怕,我从来不用带选择的网格视图。您应该为此发布另一个问题。 【参考方案1】:这就是我在我的活动中获取多张照片的方式。您可以使用其中的一部分来适应您的逻辑。我用它从相册中获取 Facebook 图片。所以我的需求(我假设)与您的需求不同。但同样,这个逻辑可能对你有用。
注意:这会很长。 ;-)
这些是通过活动使用的全局声明:
// HOLD THE URL TO MAKE THE API CALL TO
private String URL;
// STORE THE PAGING URL
private String pagingURL;
// FLAG FOR CURRENT PAGE
int current_page = 1;
// BOOLEAN TO CHECK IF NEW FEEDS ARE LOADING
Boolean loadingMore = true;
Boolean stopLoadingData = false;
这是获取初始图像集的代码块:
private class getPhotosData extends AsyncTask<Void, Void, Void>
@Override
protected Void doInBackground(Void... arg0)
// CHANGE THE LOADING MORE STATUS TO PREVENT DUPLICATE CALLS FOR
// MORE DATA WHILE LOADING A BATCH
loadingMore = true;
// SET THE INITIAL URL TO GET THE FIRST LOT OF ALBUMS
URL = "https://graph.facebook.com/" + initialAlbumID
+ "/photos&access_token="
+ Utility.mFacebook.getAccessToken() + "?limit=10";
try
HttpClient hc = new DefaultHttpClient();
HttpGet get = new HttpGet(URL);
HttpResponse rp = hc.execute(get);
if (rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
String queryAlbums = EntityUtils.toString(rp.getEntity());
JSONObject JOTemp = new JSONObject(queryAlbums);
JSONArray JAPhotos = JOTemp.getJSONArray("data");
// IN MY CODE, I GET THE NEXT PAGE LINK HERE
getPhotos photos;
for (int i = 0; i < JAPhotos.length(); i++)
JSONObject JOPhotos = JAPhotos.getJSONObject(i);
// Log.e("INDIVIDUAL ALBUMS", JOPhotos.toString());
if (JOPhotos.has("link"))
photos = new getPhotos();
// GET THE ALBUM ID
if (JOPhotos.has("id"))
photos.setPhotoID(JOPhotos.getString("id"));
else
photos.setPhotoID(null);
// GET THE ALBUM NAME
if (JOPhotos.has("name"))
photos.setPhotoName(JOPhotos.getString("name"));
else
photos.setPhotoName(null);
// GET THE ALBUM COVER PHOTO
if (JOPhotos.has("picture"))
photos.setPhotoPicture(JOPhotos
.getString("picture"));
else
photos.setPhotoPicture(null);
// GET THE PHOTO'S SOURCE
if (JOPhotos.has("source"))
photos.setPhotoSource(JOPhotos
.getString("source"));
else
photos.setPhotoSource(null);
arrPhotos.add(photos);
catch (Exception e)
e.printStackTrace();
return null;
@Override
protected void onPostExecute(Void result)
// SET THE ADAPTER TO THE GRIDVIEW
gridOfPhotos.setAdapter(adapter);
// CHANGE THE LOADING MORE STATUS
loadingMore = false;
这是为了检测用户何时滚动到最后并获取新的图像集:
// ONSCROLLLISTENER
gridOfPhotos.setOnScrollListener(new OnScrollListener()
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount)
int lastInScreen = firstVisibleItem + visibleItemCount;
if ((lastInScreen == totalItemCount) && !(loadingMore))
if (stopLoadingData == false)
// FETCH THE NEXT BATCH OF FEEDS
new loadMorePhotos().execute();
);
最后,这就是我获取下一组图像的方式:
private class loadMorePhotos extends AsyncTask<Void, Void, Void>
@Override
protected Void doInBackground(Void... arg0)
// SET LOADING MORE "TRUE"
loadingMore = true;
// INCREMENT CURRENT PAGE
current_page += 1;
// Next page request
URL = pagingURL;
try
HttpClient hc = new DefaultHttpClient();
HttpGet get = new HttpGet(URL);
HttpResponse rp = hc.execute(get);
if (rp.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
String queryAlbums = EntityUtils.toString(rp.getEntity());
// Log.e("PAGED RESULT", queryAlbums);
JSONObject JOTemp = new JSONObject(queryAlbums);
JSONArray JAPhotos = JOTemp.getJSONArray("data");
// IN MY CODE, I GET THE NEXT PAGE LINK HERE
getPhotos photos;
for (int i = 0; i < JAPhotos.length(); i++)
JSONObject JOPhotos = JAPhotos.getJSONObject(i);
// Log.e("INDIVIDUAL ALBUMS", JOPhotos.toString());
if (JOPhotos.has("link"))
photos = new getPhotos();
// GET THE ALBUM ID
if (JOPhotos.has("id"))
photos.setPhotoID(JOPhotos.getString("id"));
else
photos.setPhotoID(null);
// GET THE ALBUM NAME
if (JOPhotos.has("name"))
photos.setPhotoName(JOPhotos.getString("name"));
else
photos.setPhotoName(null);
// GET THE ALBUM COVER PHOTO
if (JOPhotos.has("picture"))
photos.setPhotoPicture(JOPhotos
.getString("picture"));
else
photos.setPhotoPicture(null);
// GET THE ALBUM'S PHOTO COUNT
if (JOPhotos.has("source"))
photos.setPhotoSource(JOPhotos
.getString("source"));
else
photos.setPhotoSource(null);
arrPhotos.add(photos);
catch (Exception e)
e.printStackTrace();
return null;
@Override
protected void onPostExecute(Void result)
// get listview current position - used to maintain scroll position
int currentPosition = gridOfPhotos.getFirstVisiblePosition();
// APPEND NEW DATA TO THE ARRAYLIST AND SET THE ADAPTER TO THE
// LISTVIEW
adapter = new PhotosAdapter(Photos.this, arrPhotos);
gridOfPhotos.setAdapter(adapter);
// Setting new scroll position
gridOfPhotos.setSelection(currentPosition + 1);
// SET LOADINGMORE "FALSE" AFTER ADDING NEW FEEDS TO THE EXISTING
// LIST
loadingMore = false;
这是SET
和GET
从上述查询中收集的数据的辅助类:
public class getPhotos
String PhotoID;
String PhotoName;
String PhotoPicture;
String PhotoSource;
// SET THE PHOTO ID
public void setPhotoID(String PhotoID)
this.PhotoID = PhotoID;
// GET THE PHOTO ID
public String getPhotoID()
return PhotoID;
// SET THE PHOTO NAME
public void setPhotoName(String PhotoName)
this.PhotoName = PhotoName;
// GET THE PHOTO NAME
public String getPhotoName()
return PhotoName;
// SET THE PHOTO PICTURE
public void setPhotoPicture(String PhotoPicture)
this.PhotoPicture = PhotoPicture;
// GET THE PHOTO PICTURE
public String getPhotoPicture()
return PhotoPicture;
// SET THE PHOTO SOURCE
public void setPhotoSource(String PhotoSource)
this.PhotoSource = PhotoSource;
// GET THE PHOTO SOURCE
public String getPhotoSource()
return PhotoSource;
如果您还想要adapter
代码,请告诉我。我在适配器中使用Fedor'sLazy Loading方法。
呸。希望这对您有所帮助。如果您还有其他问题,请随时提问。 :-)
编辑:添加了适配器代码:
public class PhotosAdapter extends BaseAdapter
private Activity activity;
ArrayList<getPhotos> arrayPhotos;
private static LayoutInflater inflater = null;
ImageLoader imageLoader;
public PhotosAdapter(Activity a, ArrayList<getPhotos> arrPhotos)
activity = a;
arrayPhotos = arrPhotos;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
imageLoader = new ImageLoader(activity.getApplicationContext());
public int getCount()
return arrayPhotos.size();
public Object getItem(int position)
return arrayPhotos.get(position);
public long getItemId(int position)
return position;
public View getView(final int position, View convertView, ViewGroup parent)
ViewHolder holder;
View vi = convertView;
if(convertView == null)
vi = inflater.inflate(R.layout.photos_item, null);
holder = new ViewHolder();
holder.imgPhoto = (ImageView)vi.findViewById(R.id.grid_item_image);
vi.setTag(holder);
else
holder = (ViewHolder) vi.getTag();
if (arrayPhotos.get(position).getPhotoPicture() != null)
imageLoader.DisplayImage(arrayPhotos.get(position).getPhotoPicture(), holder.imgPhoto);
return vi;
static class ViewHolder
ImageView imgPhoto;
编辑:添加了在加载时显示进度的步骤:
将 ProgressBar 添加到您的 XML 中,您的下方有 GridView。如果它引起任何问题,请尝试使用重量。
<LinearLayout
android:id="@+id/linlaProgressBar"
android:layout_
android:layout_
android:gravity="center"
android:orientation="horizontal" >
<ProgressBar
style="@style/Spinner"
android:layout_
android:layout_
android:padding="2dp" />
</LinearLayout>
在您的 Java 中,将 Linearlayout linlaProgressBar
声明为 Global 并将其转换为 onCreate()
并将其可见性设置为 linlaProgressBar.setVisibility(View.GONE);
在onPreExecute()
中这样使用:
@Override
protected void onPreExecute()
// SHOW THE BOTTOM PROGRESS BAR (SPINNER) WHILE LOADING MORE PHOTOS
linlaProgressBar.setVisibility(View.VISIBLE);
最后添加,onPostExecute()
// HIDE THE BOTTOM PROGRESS BAR (SPINNER) AFTER LOADING MORE ALBUMS
linlaProgressBar.setVisibility(View.GONE);
【讨论】:
这正是我所需要的!我现在就试试。如果您也可以发布适配器代码,我将不胜感激。谢谢! 非常感谢!它工作得很好。你知道是否有可能在网格底部添加一个小加载标志? (因为加载需要一些时间) 给我一个小时左右到我的办公室。 @user1787773:检查编辑。这将在加载数据时在底部为您提供一个进度条(微调器)。 真正的节省时间..@IceMAN 非常感谢这个答案! :)【参考方案2】:您可以查看 Android 文档中的 Using adapter Views 和 GridView。
最重要的是适配器调用方法getView
只传递屏幕上显示的条目的位置,并在用户滚动时询问不同的位置。
最简单的方法是使用适配器的getView
方法和AsyncTask 下载所需的图像。
有一个example
【讨论】:
【参考方案3】:从经验来看,在合理消耗内存的同时实现平滑滚动(和整体响应能力)是很棘手的。
最好先寻找现有的解决方案,例如,从这里开始: Lazy load of images in ListView
我们最终得到了一个定制的专有解决方案。它是一个后台线程,将下载请求和下载并缓存在外部存储上,仅对仍然可见的图像进行排队。当新图像到达时,视图会收到通知并决定何时通知适配器进行更新。
它还可以节省带宽,这在某些情况下很重要。
【讨论】:
有没有我可以在任何地方查看的代码来实现你们实现的东西?谢谢!【参考方案4】:我发现 IceMAN 的 答案非常有用,但我也建议避免使用两个 AsyncTasks,您可以轻松做到这一点。
您需要创建一个通用方法来获取所需数据,您可以在其中创建 if/else 条件(例如):
movies = fetchMovie.execute(sort).get();
if (movies == null)
movieList = new ArrayList<>();
else if (addMovies)
movieList.addAll(movies);
else
movieList = movies;
addMovies 是 onScroll 方法中的布尔值。 在 AsyncTask 中,在查询 URL 中提供当前页面,瞧——你让你的代码更小了 :)
【讨论】:
以上是关于延迟将图像下载到 gridView的主要内容,如果未能解决你的问题,请参考以下文章
将图像从 Parse 加载到 UICollectionView 单元格没有延迟
SpriteKit - 可以使用 SDWebImage 将 Facebook 朋友的图像延迟加载到 spriteNodeWithTexture 中吗?