package com.octo.android.robospice.sample.adapter; import java.util.ArrayList; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.ArrayAdapter; import android.widget.ImageView; import com.octo.android.robospice.SpiceManager; import com.octo.android.robospice.persistence.exception.SpiceException; import com.octo.android.robospice.request.CachedSpiceRequest; import com.octo.android.robospice.request.listener.RequestListener; import com.octo.android.robospice.sample.model.FlickrPhoto; import com.octo.android.robospice.sample.request.FlickrImageRequestFactory; import com.octo.android.robospice.sample.service.FlickrSpiceService; /** * An adapter that can be used inside a list or grid to display pictures obtained from Flickr web * service. * @author David Stemmer */ public class FlickrPhotoAdapter extends ArrayAdapter<FlickrPhoto> implements AbsListView.OnScrollListener { private final int layoutRes; private final int imageRes; private int lastScrollState = SCROLL_STATE_IDLE; private final FlickrImageRequestFactory imageRequestFactory; private final SpiceManager spiceManager; public FlickrPhotoAdapter(Context context, int layoutRes, int imageRes, ArrayList<FlickrPhoto> photos, FlickrImageRequestFactory requestFactory) { super(context, -1, -1, photos); this.layoutRes = layoutRes; this.imageRes = imageRes; this.imageRequestFactory = requestFactory; this.spiceManager = new SpiceManager(FlickrSpiceService.class); this.spiceManager.start(getContext()); } @Override public void onScrollStateChanged(AbsListView absListView, int scrollState) { if (scrollState != SCROLL_STATE_FLING && lastScrollState == SCROLL_STATE_FLING) { int firstVisiblePosition = absListView.getFirstVisiblePosition(); int lastVisiblePosition = absListView.getLastVisiblePosition(); int numVisiblePositions = lastVisiblePosition - firstVisiblePosition + 1; for (int i = 0; i < numVisiblePositions; i++) { int adapterItemPosition = firstVisiblePosition + i; View visibleItem = absListView.getChildAt(i); ViewMetaData viewMetaData = (ViewMetaData) visibleItem.getTag(); boolean imageNotLoaded = viewMetaData.imageState == ImageState.EMPTY; boolean imageNotLoadingFromNetwork = viewMetaData.imageState == ImageState.LOADING_CACHE_ONLY; if (imageNotLoaded || imageNotLoadingFromNetwork) { executeImageRequest(adapterItemPosition, viewMetaData, false); } } } lastScrollState = scrollState; } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { LayoutInflater inflater = LayoutInflater.from(getContext()); convertView = inflater.inflate(layoutRes, parent, false); AbsListView.LayoutParams params = new AbsListView.LayoutParams( imageRequestFactory.getTargetWidth(), imageRequestFactory.getTargetHeight()); convertView.setLayoutParams(params); ViewMetaData viewMetaData = new ViewMetaData(convertView); convertView.setTag(viewMetaData); } ViewMetaData viewMetaData = (ViewMetaData) convertView.getTag(); // TODO: load a proper placeholder image here viewMetaData.image.setImageDrawable(new ColorDrawable(Color.parseColor("#000000"))); viewMetaData.imageState = ImageState.EMPTY; if (viewMetaData.pendingRequest != null) { viewMetaData.pendingRequest.cancel(); } boolean isFlinging = lastScrollState == SCROLL_STATE_FLING; if (isFlinging) { executeImageRequest(position, viewMetaData, true); } else { executeImageRequest(position, viewMetaData, false); } return convertView; } private void executeImageRequest(int position, ViewMetaData viewMetaData, boolean cacheOnly) { FlickrPhoto photoSource = getItem(position); viewMetaData.pendingRequest = imageRequestFactory.create(photoSource); BitmapRequestListener requestListener = new BitmapRequestListener(viewMetaData); if (cacheOnly) { viewMetaData.imageState = ImageState.LOADING_CACHE_ONLY; spiceManager.getFromCache(viewMetaData.pendingRequest.getResultType(), viewMetaData.pendingRequest.getRequestCacheKey(), viewMetaData.pendingRequest.getCacheDuration(), requestListener); } else { viewMetaData.imageState = ImageState.LOADING_WITH_NETWORK; spiceManager.execute(viewMetaData.pendingRequest, requestListener); } } private class ViewMetaData { public ImageView image; public CachedSpiceRequest<Bitmap> pendingRequest; public ImageState imageState; public ViewMetaData(View itemView) { image = (ImageView) itemView.findViewById(imageRes); } } private enum ImageState { EMPTY, LOADING_WITH_NETWORK, LOADING_CACHE_ONLY, LOADING_COMPLETE } private class BitmapRequestListener implements RequestListener<Bitmap> { private final ViewMetaData target; public BitmapRequestListener(ViewMetaData target) { this.target = target; } @Override public void onRequestFailure(SpiceException spiceException) { // maybe we should log something here...need to differentiate // between regular and canceled requests } @Override public void onRequestSuccess(Bitmap bitmap) { if (bitmap == null) { target.imageState = ImageState.EMPTY; } else { target.imageState = ImageState.LOADING_COMPLETE; target.image.setImageBitmap(bitmap); target.image.setImageBitmap(bitmap); } target.pendingRequest = null; } } }