实现从网上下载大量图片在GridView上显示过程中遇到的一些问题

来源:互联网 发布:淘宝店名设计在线 编辑:程序博客网 时间:2024/04/30 05:08

  图片从Flickr上下载(需要fq才能访问)。
  
  我们先理下思路再开始写代码,首先从Flickr上拿到JSON字符串,然后解析,我们可以用一个类来代表每张图片所包含的信息,这些信息的下载都很快,我们可以用AsyncTask来解决,等要通过url去下载图片的时候,AsyncTask就不合适了,具体原因看我这篇文章:关于Android AsyncTask的一些总结。所以我们现在来单独考虑下图片的下载,首先我们没必要也不应该一次下载所有图片,因为我们只需要及时加载用户当前滑到界面的图片即可,而且一次下载所有图片从时间和内存角度考虑都是不可取的,因此只需要下载当前用户所看到界面的图片即可。具体实现时,我们可以弄个后台线程去下载图片。下面我们来具体看下实现过程。
  
  关于Flickr API的使用这里就不说了,可以去Flickr网站看相关文档。
  
  其中JSON字符串下载及解析的相关主要代码如下:

AsyncTask<Void,Void,List<PhotoItem>> mAsyncTask = new AsyncTask<Void,Void,List<PhotoItem>>() {    @Override    protected List<PhotoItem> doInBackground(Void... params) {        try {            mData = parseJson(getJsonString());            return mData;        } catch (IOException e) {            e.printStackTrace();        } catch (JSONException e) {            e.printStackTrace();        }        return null;    }    @Override    protected void onPostExecute(List<PhotoItem> data) {        if (data != null) {            mGridViewAdapter.setData(data);            mGridView.setAdapter(mGridViewAdapter);        }    }};//解析JSON字符串List<PhotoItem> parseJson(String jsonString) throws JSONException {    List<PhotoItem> data = new ArrayList<>();    JSONObject jsonObject = new JSONObject(jsonString);    JSONObject jsonObject1 = jsonObject.getJSONObject("photos");    JSONArray jsonArray = jsonObject1.getJSONArray("photo");    for (int i = 0; i < jsonArray.length(); i++) {        PhotoItem photoItem = new PhotoItem();        photoItem.setImageUrl((String) jsonArray.getJSONObject(i).get("url_s"));        data.add(photoItem);    }    return data;}//获取JSON字符串public String getJsonString() throws IOException {    URL url = new URL("https://api.flickr.com/services/rest/?method=flickr.photos.getRecent" +            "&api_key=XXX&format=json&nojsoncallback=1" +            "&extras=url_s");    HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();    try {        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();        InputStream inputStream = httpURLConnection.getInputStream();        if (httpURLConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {            return null;        }        int bytesRead = 0;        byte[] buffer = new byte[1024];        while ((bytesRead = inputStream.read(buffer)) > 0) {            byteArrayOutputStream.write(buffer, 0, bytesRead);        }        byteArrayOutputStream.close();        return new String(byteArrayOutputStream.toByteArray());    } finally {        httpURLConnection.disconnect();    }}  

  注意URL中的api_key=XXX换成你从Flickr网站上获取的API KRY。
  上面的getJsonString方法主要是将获得的InputStream变成String,parseJson方法就是解析获得的String,并返回解析的结果。
  
  下面我们来看下怎么实现只下载当前用户所看到界面的图片,其实主要代码就下面两段:

//给GridView设置滑动监听mGridView.setOnScrollListener(new AbsListView.OnScrollListener() {    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {        if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {            mGridViewIsIdle = true;            mGridViewAdapter.notifyDataSetChanged();        } else {            mGridViewIsIdle = false;        }    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {    }});  
//滑动过程中不产生新的异步下载任务(在getView中做的判断)if (((MainActivity) mContext).isGridViewIsIdle()) {    final ViewHolder finalViewHolder = viewHolder;    mHandlerThreadHandler.post(new Runnable() {        @Override        public void run() {            mMyHandlerThread.downloadImage(                    mData.get(position).getImageUrl(), finalViewHolder.mImageView);        }    });}  

  上面代码一个就是给GridView设置滑动的监听,一个就是在下载图片前做判断,只有GridView处于静止状态才下载图片。至于为什么要句mGridViewAdapter.notifyDataSetChanged(),是因为我们是在GridView处于idle状态时才设置mGridViewIsIdle = true,而这时并不会调用getView,所以需要mGridViewAdapter.notifyDataSetChanged()去调用下getView。
  
  下面我们再来看下图片错位的问题怎么解决。先看下为什么会发生图片错位,其实问题就出在convertView的复用,在从服务器下载图片的时候,可能某个图片下载的比较慢,这样当用户已经滑过它所处的位置时,它才下载好,因为View的复用,它便被放到了那个原先属于它,现在不属于它的位置,于是出现了图片错位。下面看下怎么解决这个问题。
  首先在getView中:

viewHolder.mImageView.setTag(mData.get(position).getImageUrl());//防止图片错位  

  然后在更新UI时:

//更新UI操作,放回主线程mMainThreadHandler.post(new Runnable() {    @Override    public void run() {        //防止图片错位        if (imageView.getTag() == string) {            imageView.setImageBitmap(bitmap);        }    }});  

  这样就可以有效防止图片错位。
     
  其他的细节问题可以具体看源代码:
  https://github.com/qian135/BlogArticleDemo/tree/master/ImageDownloadOne
  再次友情提示下:注意URL中的api_key=XXX换成你从Flickr网站上获取的API KEY
  
  参考资料:
  《Android编程权威指南》
  《Big.Nerd.Ranch.Guides.Android.Programming.The.Big.Nerd.Ranch.Guide.2nd.Edition》(Android编程权威指南第二版)
  
  

0 0