图片从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编程权威指南第二版)