i know the title is a bit messy, but here is the problem....
my goal is to retrieve title, and draw thumbnails of my youtube channel videos by using thumbnail URL, to the listView...
so far, i have the textView to display video title properly, but the thumbnail just couldnt be draw anyway..... by the way, i have the json / sqlite stuff classes done properly and they can retrieve data properly, so i dont have to worry about that... the only thing that bothers me is thumbnail wont display, the imageView displays as empty space in the app....
here is my code, please give me a hand. thx
this is the on create method of the activity...
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] uiBindFrom = { TutListDatabase.COL_TITLE, TutListDatabase.COL_THUMBNAIL };
int[] uiBindTo = { R.id.title, R.id.thumbnail };
getLoaderManager().initLoader(TUTORIAL_LIST_LOADER, null, this);
adapter = new SimpleCursorAdapter(
getActivity().getApplicationContext(), R.layout.list_item,
null, uiBindFrom, uiBindTo,
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
adapter.setViewBinder(new MyViewBinder());
setListAdapter(adapter);
}
and this one is the private class for putting stuff onto listView...
private class MyViewBinder implements SimpleCursorAdapter.ViewBinder{
#Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
int viewId = view.getId();
switch(viewId){
case R.id.title:
TextView titleTV = (TextView)view;
titleTV.setText(cursor.getString(columnIndex));
break;
// it is not displaying any thumbnail in app....
case R.id.thumbnail:
ImageView thumb = (ImageView) view;
thumb.setImageURI(Uri.parse(cursor.getString(columnIndex)));
break;
}
return false;
}
}
and here is the xml layout file...
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal" >
<ImageView
android:id="#+id/thumbnail"
android:layout_width="101dp"
android:layout_height="101dp"
android:src="#drawable/icon" />
<TextView
android:id="#+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="6dp"
android:textSize="24dp" />
</LinearLayout>
I can show you a way that I have used and it worked quite well, first off we need a way to cache the images and the best way I have seen to date, is to use the LruCache as described in the excellent Google IO presentation doing more with less: http://www.youtube.com/watch?v=gbQb1PVjfqM
Here is my implementation of the method described in that presentation.
public class BitmapCache extends LruCache<String, Bitmap> {
public BitmapCache(int sizeInBytes) {
super(sizeInBytes);
}
public BitmapCache(Context context) {
super(getOptimalCacheSizeInBytes(context));
}
public static int getOptimalCacheSizeInBytes(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
int memoryClassBytes = am.getMemoryClass() * 1024 * 1024;
return memoryClassBytes / 8;
}
#Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
}
Next we need to load the images asynchronously with an AsyncTask, the following implementation takes care of loading an image into the given ImageView and dealing with the cache:
public class LoadImageAsyncTask extends AsyncTask<Void, Void, Pair<Bitmap, Exception>> {
private ImageView mImageView;
private String mUrl;
private BitmapCache mCache;
public LoadImageAsyncTask(BitmapCache cache, ImageView imageView, String url) {
mCache = cache;
mImageView = imageView;
mUrl = url;
mImageView.setTag(mUrl);
}
#Override
protected void onPreExecute() {
Bitmap bm = mCache.get(mUrl);
if(bm != null) {
cancel(false);
mImageView.setImageBitmap(bm);
}
}
#Override
protected Pair<Bitmap, Exception> doInBackground(Void... arg0) {
if(isCancelled()) {
return null;
}
URL url;
InputStream inStream = null;
try {
url = new URL(mUrl);
URLConnection conn = url.openConnection();
inStream = conn.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(inStream);
return new Pair<Bitmap, Exception>(bitmap, null);
} catch (Exception e) {
return new Pair<Bitmap, Exception>(null, e);
}
finally {
closeSilenty(inStream);
}
}
#Override
protected void onPostExecute(Pair<Bitmap, Exception> result) {
if(result == null) {
return;
}
if(result.first != null && mUrl.equals(mImageView.getTag())) {
mCache.put(mUrl, result.first);
mImageView.setImageBitmap(result.first);
}
}
public void closeSilenty(Closeable closeable) {
if(closeable != null) {
try {
closeable.close();
} catch (Exception e) {
// TODO: Log this
}
}
}
}
Next you need to create an instance of your BitmapCache in the Activity or Fragment that is hosting the ListView, in onCreate(...) or onActivityCreated(...):
mBitmapCache = new BitmapCache(this); // or getActivity() if your using a Fragment
Now we need to update the SimpleCursorAdapter that shows the image, I have ommited most of the code as its specific to my project, but the idea is you override setViewImage where the value should be a value that is bound to the cursor, I zap the imageview to null to make sure it does not have an odd image from the cache associated to an item.
#Override
public void setViewImage(ImageView iv, String value) {
final String url = value;
iv.setImageBitmap(null);
new LoadImageAsyncTask(mBitmapCache, iv, url).execute();
}
Update
To make it clear, your adapter should look something like this
adapter = new SimpleCursorAdapter(
context, R.layout.list_item,
null, uiBindFrom, uiBindTo,
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER) {
#Override
public void setViewImage(ImageView iv, String value) {
final String url = value;
iv.setImageBitmap(null);
new LoadImageAsyncTask(mBitmapCache, iv, url).execute();
}
};
Hope that helps!
Related
I have been working on this issue for days now. I am using the kankan Android wheel example/library, but am wanting to dynamically add images to the wheel when a button is pressed. The image added depends on the button's text. It seems like a fairly easy task, but perhaps I am missing something. I tried calling the adapter's notifyDataChangedEvent() after passing and adding the selected image to the adapter's list of cached images. Debugging has showed that the images were being added to the list of images, but they are not showing up on the wheel. If someone could please help me out with this problem I would appreciate it!
Code:
public void addItem(String text) {
for(Item c: Item.values()){
if(c.getName().equals(text)) {
slotMachineAdapter.addImage(c.getImage());
break;
}
}
slotMachineAdapter.notifyDataChangedEvent();
}
Adapter
private class SlotMachineAdapter extends AbstractWheelAdapter {
// Image size
final int IMAGE_WIDTH = 700;
final int IMAGE_HEIGHT = 150;
// Slot machine symbols
private final int items[] = new int[] {
R.mipmap.ic_flipper
};
// Cached images
private List<SoftReference<Bitmap>> images;
// Layout inflater
private Context context;
/**
* Constructor
*/
public SlotMachineAdapter(Context context) {
this.context = context;
images = new ArrayList<SoftReference<Bitmap>>();
for (int id : items) {
images.add(new SoftReference<Bitmap>(loadImage(id)));
}
}
/**
* Loads image from resources
*/
private Bitmap loadImage(int id) {
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), id);
Bitmap scaled = Bitmap.createScaledBitmap(bitmap, IMAGE_WIDTH, IMAGE_HEIGHT, true);
bitmap.recycle();
return scaled;
}
#Override
public int getItemsCount() {
return items.length;
}
// Layout params for image view
final ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(IMAGE_WIDTH, IMAGE_HEIGHT);
#Override
public View getItem(int index, View cachedView, ViewGroup parent) {
ImageView img;
if (cachedView != null) {
img = (ImageView) cachedView;
} else {
img = new ImageView(context);
}
img.setLayoutParams(params);
SoftReference<Bitmap> bitmapRef = images.get(index);
Bitmap bitmap = bitmapRef.get();
if (bitmap == null) {
bitmap = loadImage(items[index]);
images.set(index, new SoftReference<Bitmap>(bitmap));
}
img.setImageBitmap(bitmap);
return img;
}
//Adds image to list of images
public void addImage(int img){
images.add(new SoftReference<Bitmap>(loadImage(img)));
}
}
Because the count you return referenced to items variable, But addImage function did not change items size. Try to change your code like below and test it again:
#Override
public int getItemsCount() {
return images.size();
}
I'm using Recycler View on my project. I'm generating pinterestlike multicolumn grid. Everything works fine. My application loads data from externalstorage (images too). When loading was in UI thread, scrolling performance was really poor. So I've decided to create AsyncTask to load images from path and this is warking grate while scrolling down. When I'm scrolling up again I have problem because recreating cells are messing with grid layout. It is rearranging and it can be poor for users. Caching images in memory (lot's of them) is not good Idea i think, są maybe it is an option to store information about ImageView sizes for every cell and keep it for reuse?
My RecyclerView Layout adapter looks like this:
public class MainGridAdapter extends RecyclerView.Adapter<ArticleViewHolder> {
private List<Article> articleList;
private Context context;
public MainGridAdapter(Context context, List<Article> articleList) {
this.articleList = articleList;
this.context = context;
}
#Override
public ArticleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.article_cell, null);
ArticleViewHolder avc = new ArticleViewHolder(layoutView);
return avc;
}
#Override
public void onBindViewHolder(ArticleViewHolder holder, final int position) {
Typeface StagMedium = Typeface.createFromAsset(context.getAssets(), "fonts/Stag-Medium.otf");
holder.articleTitle.setText(articleList.get(position).getTitle());
holder.articleTitle.setTypeface(StagMedium);
//Wczytuję obrazek
Log.v("DDD", articleList.get(position).getTitle());
Log.v("DDD", String.valueOf(articleList.get(position).getId()));
//TableRow.LayoutParams params = new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, articleList.get(position).getCover_height());
//holder.container.setLayoutParams(params);
BitmapWorkerTask task = new BitmapWorkerTask(holder.articleImage);
task.execute(articleList.get(position).getCover_local_path());
/*
File file = new File(articleList.get(position).getCover_local_path());
if(file.exists()) {
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), bmOptions);
holder.articleImage.setImageBitmap(bitmap);
}
*/
//Jeżeli materiał to video to pokazuję ikonkę
Log.v("DDD", "Type: " + articleList.get(position).getType());
if(articleList.get(position).getType().equals("article")) {
Log.v("DDD", "Article");
holder.articleVideoIcon.setImageResource(android.R.color.transparent);
} else {
Log.v("DDD", "Video");
holder.articleVideoIcon.setImageResource(R.drawable.play);
}
holder.container.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.v("DDD", "Klikłem sobie na pozycję: " + String.valueOf(position));
Intent intent= new Intent(context,SingleArticle.class);
Log.v("DDD", String.valueOf(articleList.get(position).getId()));
intent.putExtra("articleId", articleList.get(position).getId());
context.startActivity(intent);
}
});
}
#Override
public int getItemCount() {
return articleList.size();
}
class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
private final WeakReference<ImageView> imageViewReference;
private String path = "";
public BitmapWorkerTask(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
// Decode image in background.
#Override
protected Bitmap doInBackground(String... params) {
path = params[0];
File file = new File(path);
if (file.exists()) {
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(),bmOptions);
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//Bitmap resized_bitmap = Bitmap.createScaledBitmap(bitmap, Math.round(width / 2), Math.round(height / 2), false);
//return resized_bitmap;
return bitmap;
} else {
return null;
}
}
// Once complete, see if ImageView is still around and set bitmap.
#Override
protected void onPostExecute(Bitmap bitmap) {
if (imageViewReference != null && bitmap != null) {
final ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setImageBitmap(bitmap);
}
}
}
}
}
Ok I've solved my problem. The thing is that API, and datastructure are also my code, so I've started to store in SQLite thimbs sizes. In this way I can set cell size in "OnBindViewHolder" like this:
TableRow.LayoutParams params = new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, articleList.get(position).getCover_height());
holder.container.setLayoutParams(params);
You can also download Bitmap, and store height in List or array and do the same. Ofcourse height of the container should be converted to dp but it is working like a charm. The problem with floating items is solved. Thx for help :)
I want to use preloader image in gridview for images when its loading form the remote server.
while its loading from the server at that time i want to show preloader image like this(same like progress bar or progress bar).
I want to show small progress bar there in gridview image item or preloader image I dnt know what i can use which would be easily for me to achieve this.
Can anybody please help me how can do this thing in android.
I want to make this as like IOS. this image is form the IOS.
Here is my android layout xml file :
activity_image_grid.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<include
android:id="#+id/title_bar"
android:layout_alignParentTop="true"
layout="#layout/activity_top_header_bar" />
<GridView
android:id="#+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#id/title_bar"
android:gravity="center"
android:horizontalSpacing="4dip"
android:numColumns="4"
android:padding="5dip"
android:stretchMode="columnWidth"
android:verticalSpacing="4dip" />
</RelativeLayout>
This xml file used for item for each grid in Gridview.
item_grid_image.xml
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/image"
android:layout_width="fill_parent"
android:layout_height="75dp"
android:adjustViewBounds="true"
android:contentDescription="#string/descr_image"
android:scaleType="centerCrop" />
Source code :
public class ImageGridActivity extends BaseActivity {
private static final String TAG = "[ImageGridActivity]";
private DisplayImageOptions options;
private PullToRefreshGridView mPullRefreshGridView;
private GridView mGridView = null;
ArrayList<GallaryImage> mGridViewImagesList;
private ImageAdapter mImageAdapter = null;
private String mImageUrl = null;
private String mGallaryTitle = null;
// private ImageLoader imageLoader = ImageLoader.getInstance();
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_grid);
options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.photo_default)
.showImageForEmptyUri(R.drawable.photo_default)
.showImageOnFail(R.drawable.ic_error).cacheInMemory()
.cacheOnDisc().bitmapConfig(Bitmap.Config.RGB_565).build();
final Bundle bundle = getIntent().getExtras();
if (bundle != null) {
mImageUrl = bundle.getString(Constants.GALLARY_FETCH_URL);
mGallaryTitle = bundle.getString(Constants.GALLARY_TYPE);
if (mGallaryTitle != null) {
Locale loc = Locale.getDefault();
TextView tvTitleText = (TextView) findViewById(R.id.tv_title_bar_text);
tvTitleText.setText(mGallaryTitle.toUpperCase(loc));
}
mPullRefreshGridView = (PullToRefreshGridView) findViewById(R.id.pull_refresh_grid);
mPullRefreshGridView.setMode(Mode.PULL_FROM_START);
mGridView = mPullRefreshGridView.getRefreshableView();
mGridViewImagesList = Utility.getImagesList(mImageUrl,
ImageGridActivity.this);
if (mGridViewImagesList != null && !mGridViewImagesList.isEmpty()) {
mImageAdapter = new ImageAdapter(mGridViewImagesList);
((GridView) mGridView).setAdapter(mImageAdapter);
} else {
// did refresh after the previous images are loaded in the
// gridview.
if (Utility.checkConnection(ImageGridActivity.this)) {
Log.i(TAG,
"Wifi/Internet Connection found , have to parse the xml");
final FetchImagesAsyncTaskFeed asyncTask = new FetchImagesAsyncTaskFeed();
asyncTask.execute(mImageUrl);
}
}
mGridView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(final AdapterView<?> parent,
final View view, final int position, final long id) {
if (mGridViewImagesList != null
&& !mGridViewImagesList.isEmpty()) {
startImagePagerActivity(mGridViewImagesList, position);
} else {
Log.d(TAG, "There is no image about this grid image");
}
}
});
// Set a listener to be invoked when the list should be refreshed.
mPullRefreshGridView
.setOnRefreshListener(new OnRefreshListener2<GridView>() {
#Override
public void onPullDownToRefresh(
PullToRefreshBase<GridView> refreshView) {
if (mImageUrl != null) {
final FetchImagesAsyncTaskFeed asyncTask = new FetchImagesAsyncTaskFeed();
asyncTask.execute(mImageUrl);
}
}
#Override
public void onPullUpToRefresh(
PullToRefreshBase<GridView> refreshView) {
}
});
}
}
/**
* #param position
*/
private void startImagePagerActivity(
final ArrayList<GallaryImage> mImageAttributesList,
final int position) {
String[] urls = new String[mImageAttributesList.size()];
final Intent intent = new Intent(this, ImagePagerActivity.class);
intent.putExtra(Constants.GALLARY_IMAGE_POSITION_BUNDLE_KEY, position);
for (int i = 0; i < mImageAttributesList.size(); i++) {
urls[i] = mImageAttributesList.get(i).mImageUrl;
}
intent.putExtra(Constants.GALLARY_IMAGES_IMAGE_BUNDLE_KEY, urls);
startActivity(intent);
}
public class ImageAdapter extends BaseAdapter {
ArrayList<GallaryImage> imageList = null;
public ImageAdapter(final ArrayList<GallaryImage> imageAttributesList) {
this.imageList = imageAttributesList;
}
#Override
public int getCount() {
return imageList.size();
}
#Override
public Object getItem(final int position) {
return imageList.get(position);
}
#Override
public long getItemId(final int position) {
return position;
}
#Override
public View getView(final int position, final View convertView,
final ViewGroup parent) {
final ImageView imageView;
if (convertView == null) {
imageView = (ImageView) getLayoutInflater().inflate(
R.layout.item_grid_image, parent, false);
} else {
imageView = (ImageView) convertView;
}
imageLoader.displayImage(imageList.get(position).mImageUrl,
imageView, options);
return imageView;
}
/**
* #param updateData
*/
public void updatedData(ArrayList<GallaryImage> imgList) {
this.imageList = imgList;
notifyDataSetChanged();
}
}
private class FetchImagesAsyncTaskFeed extends
AsyncTask<String, Void, String> {
#Override
protected void onPreExecute() {
}
#Override
protected String doInBackground(final String... urls) {
try {
Thread.sleep(3000);
final String imageUrl = urls[0];
final GridViewImagesXMLHandler mGallaryXMLHandler = new GridViewImagesXMLHandler();
mGridViewImagesList = mGallaryXMLHandler.getImages(imageUrl);
if (mGridViewImagesList != null
&& !mGridViewImagesList.isEmpty()) {
Utility.setImagesInfromation(imageUrl, mGridViewImagesList,
ImageGridActivity.this);
}
} catch (final Exception e) {
Log.e(TAG, "Exception in fetch images from the url", e);
}
return null;
}
#Override
protected void onPostExecute(final String result) {
if (mGridViewImagesList != null && !mGridViewImagesList.isEmpty()) {
if (mImageAdapter != null) {
mImageAdapter.updatedData(mGridViewImagesList);
mPullRefreshGridView.onRefreshComplete();
} else {
mImageAdapter = new ImageAdapter(mGridViewImagesList);
((GridView) mGridView).setAdapter(mImageAdapter);
}
}
mPullRefreshGridView.onRefreshComplete();
}
}
}
Universal ImageLoader
https://github.com/nostra13/Android-Universal-Image-Loader
rowimage.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ImageView
android:id="#+id/ivv"
android:layout_gravity="center"
android:layout_width="300dp"
android:layout_height="300dp"
/>
<ProgressBar
android:id="#+id/pb"
android:layout_centerInParent="true"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
In your adapter constructor
ImageLoader imageLoader;
DisplayImageOptions options;
File cacheDir = StorageUtils.getOwnCacheDirectory(a, "MyRaghu");
// Get singletone instance of ImageLoader
imageLoader = ImageLoader.getInstance();
// Create configuration for ImageLoader (all options are optional)
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(a)
// You can pass your own memory cache implementation
.discCacheExtraOptions(1024, 1024, CompressFormat.PNG, 100)
.discCache(new UnlimitedDiscCache(cacheDir)) // You can pass your own disc cache implementation
.discCacheFileNameGenerator(new HashCodeFileNameGenerator())
.enableLogging()
.build();
// Initialize ImageLoader with created configuration. Do it once.
imageLoader.init(config);
//imageLoader.init(ImageLoaderConfiguration.createDefault(a));
// imageLoader=new ImageLoader(activity.getApplicationContext());
options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.ic_launcher)
.cacheInMemory()
.cacheOnDisc()
.displayer(new RoundedBitmapDisplayer(20))
.build();
In your getview of your custom adapter
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
if(convertView==null)
vi = inflater.inflate(R.layout.rowimage, null);
ImageView image=(ImageView)vi.findViewById(R.id.ivv);
ProgressBar pb= (ProgressBar)vi.findViewById(R.id.pb);
display(null, data.get(position).toString(), pb);
//imageLoader.displayImage(data.get(position).toString(), image,options);
return vi;
}
public void display(ImageView img, String url, final ProgressBar spinner)
{
imageLoader.displayImage(url, img, options, new ImageLoadingListener() {
#Override
public void onLoadingStarted(String imageUri, View view) {
spinner.setVisibility(View.VISIBLE);
}
#Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
spinner.setVisibility(View.GONE);
}
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
spinner.setVisibility(View.GONE);
}
#Override
public void onLoadingCancelled(String imageUri, View view) {
}
});
}
Resulting snap shot i have used listview but it should work for gridview also.
First a stub image is displayed along with progress bar. In this case a i have used a launcher icon so it looks stretched
Once image is downloaded progress bar is dismissed and stub image is replaced by the downloaded one. Even caches images.
Try to use Android-Universal-Image-Loader api from github.com
Android-Universal-Image-Loader
I think it help you.
Thanks.
In my application I need to download a lot of pictures from urls and display them in a gridView. (It can be between 1-200 pictures). I don't want to download all pictures at once. I read about lazy downloading and my question is: Can i get only one part of the Json, download the pictures in a different thread, and only if the user scroll down the gridView, I will continue to the other parts of the Json, and so on?
Edit: Hi again. I want to implement multi select in this gridView and i'm having difficulty to implement the code in the getView() method of the adapter. This is the example i'm using:example. How can I combine this code in my getView() method:
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);
}
}
my getView() code:
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;
}
This is how I fetch multiple photos in my activity. You can use parts of it for fit your logic. I use this to fetch Facebook Images from an Album. So my needs are (I am assuming) different from your needs. But again, the logic may be of use to you.
Note: This will be lengthy. ;-)
These are the global declarations for use through the ACtivity:
// 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;
This is the code block that fetches the initial set of Images:
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;
}
}
This is to detect when the user has scrolled to the end and fetch new set of images:
// 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();
}
}
}
});
And finally, this is how I fetch the next set of images:
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;
}
}
And this is the helper class to SET and GET the data collected from the queries above:
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;
}
}
If you also want the adapter code, let me know. I use Fedor's Lazy Loading method in the adapter.
Phew. Hope any of this helps. If you have further question, feel free to ask. :-)
EDIT: Adapter code added:
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;
}
}
EDIT: Added steps to show Progress while loading:
Add a ProgressBar to you XML where you have the GridView right below it. Play around with the weight if it causes any problems.
<LinearLayout
android:id="#+id/linlaProgressBar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal" >
<ProgressBar
style="#style/Spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="2dp" />
</LinearLayout>
In your Java, declare the Linearlayout linlaProgressBar as Global and cast it in the onCreate() and set it's visibility as linlaProgressBar.setVisibility(View.GONE);
And in the onPreExecute() use it like this:
#Override
protected void onPreExecute() {
// SHOW THE BOTTOM PROGRESS BAR (SPINNER) WHILE LOADING MORE PHOTOS
linlaProgressBar.setVisibility(View.VISIBLE);
}
And finally add, this in the onPostExecute()
// HIDE THE BOTTOM PROGRESS BAR (SPINNER) AFTER LOADING MORE ALBUMS
linlaProgressBar.setVisibility(View.GONE);
You can, take a look to Using adapter Views and GridView from Android Documentation.
The most important thing is that the adapter call the method getView passing only the position of the entries showing on screen, and asking for different positions when user scrolls.
The easy way to do is download the required image on the getView method of your adapter with and AsyncTask.
There is an example
Talking from experience, it's tricky to achieve smooth scrolling (and overall responsiveness) while consuming memory reasonably.
It would be a good idea to look for existing solutions first, e.g., start here:
Lazy load of images in ListView
We ended up with a custom proprietary solution. It is a background thread that queues download requests and downloads and caches on the external storage only the images that are still visible. When a new image arrives, the view gets notified and decides when to notify the adapter to update.
It also saves the bandwidth, which was important in some cases.
I found IceMAN's answer very useful, but I also recommend avoid using two AsyncTasks and you can make this easily.
You need to create a universal method to fetch needed data, where you can make an if/else condition (as an example):
movies = fetchMovie.execute(sort).get();
if (movies == null) {
movieList = new ArrayList<>();
} else if (addMovies) {
movieList.addAll(movies);
} else {
movieList = movies;
}
addMovies is a boolean in your onScroll method.
In AsyncTask provide current page in query URL and voila - you made your code smaller :)
I want to download some images through AsyncTask to SDcard and on completion to show them on an listview. When the download starts an progress bar is shown, when it stops will show the image downloaded on sdcard.
I saw that are a lot of posts with lazy-load, but what i want is to show a progress bar before showing the image, and i want to be stored on sdcard.
The bellow code works almost ok, but the problem is that it doesnt show the right picture on the right item all the time when it is downloading the image. I am using the bellow in the adapter:
public View getView(final int position, View convertView, ViewGroup parent) {
final PooHolder holder;
if(convertView == null){
convertView = inflater.inflate(R.layout.item_poo, null);
holder = new PooHolder();
holder.tvTime = (TextView)convertView.findViewById(R.id.tvTime);
holder.tvMessage = (TextView)convertView.findViewById(R.id.tvMessage);
holder.btnDislike = (ImageButton)convertView.findViewById(R.id.btnDislike);
holder.btnLike = (ImageButton)convertView.findViewById(R.id.btnLike);
holder.btnReport = (ImageButton)convertView.findViewById(R.id.btnReport);
holder.bar = (ProgressBar)convertView.findViewById(R.id.progressBar1);
holder.bar1 = (ProgressBar)convertView.findViewById(R.id.progressBar2);
holder.bar2 = (ProgressBar)convertView.findViewById(R.id.progressBar3);
holder.tvLikes = (TextView)convertView.findViewById(R.id.tvLikes);
holder.tvComments = (TextView)convertView.findViewById(R.id.tvComments);
holder.tvDislikes = (TextView)convertView.findViewById(R.id.tvDislike);
holder.imgPoo = (ImageView)convertView.findViewById(R.id.imgPoo);
convertView.setTag(holder);
}else{
holder = (PooHolder)convertView.getTag();
}
final Poo poo = list.get(position);
String vote = poo.getVote();
if(poo.getDrawablePath()!=null){
if(!poos.get(position).isDownloadComplete()){
Log.i(DEBUG, poo.getMessage());
holder.bar2.setVisibility(View.VISIBLE);
holder.imgPoo.setImageBitmap(BitmapFactory.decodeFile(poo.getDrawablePath()));
holder.imgPoo.setVisibility(View.INVISIBLE);
if(!poos.get(position).getDownloadState(Poo.DOWNLOAD_START)){
DownloadImageTask task = new DownloadImageTask(poo, holder.bar2, holder.imgPoo);
task.setOnDownloadListener(new OnDownloadListener(){
public void onDownloadStarted() {
poos.get(position).startDownload();
}
public void onDownloadFinished(final Bitmap bmp) {
poos.get(position).stopDownload();
}
});
task.execute(poo.getImagePath());
}
}else{
holder.bar2.setVisibility(View.INVISIBLE);
holder.imgPoo.setVisibility(View.VISIBLE);
holder.imgPoo.setImageBitmap(BitmapFactory.decodeFile(poo.getDrawablePath()));
}
}else{
holder.bar2.setVisibility(View.INVISIBLE);
holder.imgPoo.setVisibility(View.VISIBLE);
holder.imgPoo.setImageResource(R.drawable.icon);
}
....
The DownloadImageTask.java:
public class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
private final String DEBUG = "DownloadImageTask";
private String url;
private String imagePath;
private OnDownloadListener listener;
private Bitmap bmp;
private ProgressBar bar;
private ImageView img;
private Poo poo;
public DownloadImageTask(Poo poo, ProgressBar bar, ImageView img) {
this.img = img;
this.bar = bar;
this.poo = poo;
}
public void onPreExecute(){
super.onPreExecute();
listener.onDownloadStarted();
bar.setVisibility(View.VISIBLE);
img.setImageBitmap(BitmapFactory.decodeFile(poo.getDrawablePath()));
img.setVisibility(View.INVISIBLE);
}
public void setOnDownloadListener(OnDownloadListener listener){
this.listener = listener;
}
#Override
// Actual download method, run in the task thread
protected Bitmap doInBackground(String... params) {
this.imagePath = poo.getImagePath();
// params comes from the execute() call: params[0] is the url.
if(imagePath != null && !imagePath.isEmpty()){
String file = imagePath.substring(imagePath.lastIndexOf("/") + 1, imagePath.length());
return BoopoohooUtils.downloadImage(params[0], file);
}else{
return null;
}
}
#Override
// Once the image is downloaded, associates it to the imageView
protected void onPostExecute(Bitmap bitmap) {
Log.i(DEBUG, "bitmap " + bitmap);
bar.setVisibility(View.INVISIBLE);
img.startAnimation(BoopoohooUtils.fadeIn());
img.setVisibility(View.VISIBLE);
img.setImageBitmap(bitmap);
listener.onDownloadFinished(bitmap);
}
}
So to start you will need to use the progress method on the async task class. Link . Then for your method on the top create a variable for passing through different sequence let's say 5,4,3,2,1. When it starts it goes by 5 and decrements to 1 then application will go to post and do whatever gui interactions you are doing.