延迟将图像下载到 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;
    


这是SETGET 从上述查询中收集的数据的辅助类:

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的主要内容,如果未能解决你的问题,请参考以下文章

具有延迟加载和下载图像的 ListView [重复]

使用延迟在 UITableViewCell 上延迟图像下载

将图像从 Parse 加载到 UICollectionView 单元格没有延迟

UITableView 中多张图片的延迟加载

SpriteKit - 可以使用 SDWebImage 将 Facebook 朋友的图像延迟加载到 spriteNodeWithTexture 中吗?

在不延迟页面加载的情况下将图像不可见地预加载到 DOM [重复]