I use SimpleCursorAdapter and GridActivity (extended Activity written by me based on ListActivity) to load music albums from MediaStore, and use AsyncTask load each album art .
I tried this in bindView or getView, like this:
new AsyncAlbumArtLoader(viewholder.album_art, mShowFadeAnimation).execute(aid, width, height);
class AsyncAlbumArtLoader:
private class AsyncAlbumArtLoader extends AsyncTask<Object, Void, Bitmap> {
boolean enable_animation = false;
private ImageView imageview;
public AsyncAlbumArtLoader(ImageView imageview, Boolean animation) {
enable_animation = animation;
this.imageview = imageview;
}
#Override
protected void onPreExecute() {
if (enable_animation) {
imageview.startAnimation(AnimationUtils.loadAnimation(getApplicationContext(),
android.R.anim.fade_out));
imageview.setVisibility(View.INVISIBLE);
}
}
#Override
protected Bitmap doInBackground(Object... params) {
return MusicUtils.getCachedArtwork(getApplicationContext(), (Long) params[0],
(Integer) params[1], (Integer) params[2]);
}
#Override
protected void onPostExecute(Bitmap result) {
if (result != null) {
imageview.setImageBitmap(result);
} else {
imageview.setImageResource(R.drawable.albumart_mp_unknown_list);
}
if (enable_animation) {
imageview.setVisibility(View.VISIBLE);
imageview.startAnimation(AnimationUtils.loadAnimation(getApplicationContext(),
android.R.anim.fade_in));
}
}
}
But images shifting between gridview items randomly.
You can see screen record video here.
edited prevent this error by setTag() and getTag() is also no effect.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
mAlbumCursor.moveToPosition(position);
ViewHolder viewholder = (ViewHolder) view.getTag();
String album_name = mAlbumCursor.getString(mAlbumIndex);
if (album_name == null || MediaStore.UNKNOWN_STRING.equals(album_name)) {
viewholder.album_name.setText(R.string.unknown_album_name);
} else {
viewholder.album_name.setText(album_name);
}
String artist_name = mAlbumCursor.getString(mArtistIndex);
if (album_name == null || MediaStore.UNKNOWN_STRING.equals(album_name)) {
viewholder.artist_name.setText(R.string.unknown_artist_name);
} else {
viewholder.artist_name.setText(artist_name);
}
// We don't actually need the path to the thumbnail file,
// we just use it to see if there is album art or not
long aid = mAlbumCursor.getLong(mAlbumIdIndex);
int width = getResources().getDimensionPixelSize(R.dimen.gridview_bitmap_width);
int height = getResources().getDimensionPixelSize(R.dimen.gridview_bitmap_height);
viewholder.album_art.setTag(aid);
new AsyncAlbumArtLoader(viewholder.album_art, mShowFadeAnimation, aid, width, height).execute();
long currentalbumid = MusicUtils.getCurrentAlbumId();
if (currentalbumid == aid) {
viewholder.album_name.setCompoundDrawablesWithIntrinsicBounds(0, 0,
R.drawable.ic_indicator_nowplaying_small, 0);
} else {
viewholder.album_name.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
return view;
}
// FIXME image loaded some times incorrect
private class AsyncAlbumArtLoader extends AsyncTask<Object, Void, Bitmap> {
boolean enable_animation = false;
private ImageView imageview;
private long album_id;
private int width,height;
public AsyncAlbumArtLoader(ImageView imageview, Boolean animation, long album_id, int width, int height) {
enable_animation = animation;
this.imageview = imageview;
this.album_id = album_id;
this.width = width;
this.height = height;
}
#Override
protected void onPreExecute() {
if (imageview.getTag() == null || (Long)imageview.getTag() != album_id) {
return;
}
if (enable_animation) {
imageview.startAnimation(AnimationUtils.loadAnimation(getApplicationContext(),
android.R.anim.fade_out));
imageview.setVisibility(View.INVISIBLE);
}
}
#Override
protected Bitmap doInBackground(Object... params) {
if (imageview.getTag() == null || (Long)imageview.getTag() != album_id) {
return null;
}
return MusicUtils.getCachedArtwork(getApplicationContext(), album_id,
width, height);
}
#Override
protected void onPostExecute(Bitmap result) {
if (imageview.getTag() == null || (Long)imageview.getTag() != album_id) {
return;
}
if (result != null) {
imageview.setImageBitmap(result);
} else {
imageview.setImageResource(R.drawable.albumart_mp_unknown_list);
}
if (enable_animation) {
imageview.setVisibility(View.VISIBLE);
imageview.startAnimation(AnimationUtils.loadAnimation(getApplicationContext(),
android.R.anim.fade_in));
}
}
}
The Problem is, that the AsyncTask don't know für which ImageView they where started, respectivley they overlap.
To prevent this you need to do the following:
In your getView Method (before calling the AsyncTask-Constructor you need so set a Tag to your ImageView: myImageView.setTag(object). The best choice is, if you use the object from which getView gets its information. In you case i think it is the ArrayList with the Album-Information. Let' say myImageView.setTag(myAlbumArray.get(position)) THE TAG MUST BE UNIQUE
Now add a new String 'tag' to your AsyncTask class and add this.tag = imageview.getTag().toString().
Now finally add the test in your onPostExecute:
if (imageview.getTag().toString().equals(tag)) {
// you got the right imageView, *your PostExecute Code* }
else {// wrong one, do nothing
}
Related
I've an app on Google Play that contains wallpapers (it's a wallpaper chooser) that still uses Gallery (http://developer.android.com/reference/android/widget/Gallery.html, yes, it's an old app). I'm trying to implement apk expansion files to break the 50MB limit.
Right now in-app downloader and .obb download from GP works very well. I can find it in /Android/obb. I still can't ready wallpapers from .obb file. I tried APK Expansion Zip Library but I don't know how to use it with a Gallery...
This is my wallpaper.java file:
public class wallpaper extends Activity implements AdapterView.OnItemSelectedListener,
OnClickListener {
private Gallery mGallery;
private ImageView mImageView;
private TextView mInfoView;
private boolean mIsWallpaperSet;
private Bitmap mBitmap;
private ArrayList<Integer> mThumbs;
private ArrayList<Integer> mImages;
private WallpaperLoader mLoader;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// Start wallpaper app
requestWindowFeature(Window.FEATURE_NO_TITLE);
findWallpapers();
setContentView(R.layout.wallpaper_chooser);
mGallery = (Gallery) findViewById(R.id.gallery);
mGallery.setAdapter(new ImageAdapter(this));
mGallery.setOnItemSelectedListener(this);
mGallery.setCallbackDuringFling(false);
findViewById(R.id.set).setOnClickListener(this);
mImageView = (ImageView) findViewById(R.id.wallpaper);
mInfoView = (TextView) findViewById(R.id.info);
}
private void findWallpapers() {
mThumbs = new ArrayList<Integer>(24);
mImages = new ArrayList<Integer>(24);
final Resources resources = getResources();
final String packageName = getApplication().getPackageName();
addWallpapers(resources, packageName, R.array.wallpapers);
addWallpapers(resources, packageName, R.array.extra_wallpapers);
}
private void addWallpapers(Resources resources, String packageName, int list) {
final String[] extras = resources.getStringArray(list);
for (String extra : extras) {
int res = resources.getIdentifier(extra, "drawable", packageName);
if (res != 0) {
final int thumbRes = resources.getIdentifier(extra + "_small",
"drawable", packageName);
if (thumbRes != 0) {
mThumbs.add(thumbRes);
mImages.add(res);
}
}
}
}
#Override
protected void onResume() {
super.onResume();
mIsWallpaperSet = false;
}
#Override
protected void onDestroy() {
super.onDestroy();
if (mLoader != null && mLoader.getStatus() != WallpaperLoader.Status.FINISHED) {
mLoader.cancel(true);
mLoader = null;
}
}
public void onItemSelected(AdapterView parent, View v, int position, long id) {
if (mLoader != null && mLoader.getStatus() != WallpaperLoader.Status.FINISHED) {
mLoader.cancel();
}
mLoader = (WallpaperLoader) new WallpaperLoader().execute(position);
}
/*
* When using touch if you tap an image it triggers both the onItemClick and
* the onTouchEvent causing the wallpaper to be set twice. Ensure we only
* set the wallpaper once.
*/
private void selectWallpaper(int position) {
if (mIsWallpaperSet) {
return;
}
mIsWallpaperSet = true;
try {
InputStream stream = getResources().openRawResource(mImages.get(position));
setWallpaper(stream);
setResult(RESULT_OK);
finish();
} catch (IOException e) {
Log.e("Paperless System", "Failed to set wallpaper: " + e);
}
}
public void onNothingSelected(AdapterView parent) {
}
private class ImageAdapter extends BaseAdapter {
private LayoutInflater mLayoutInflater;
ImageAdapter(wallpaper context) {
mLayoutInflater = context.getLayoutInflater();
}
public int getCount() {
return mThumbs.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView image;
if (convertView == null) {
image = (ImageView) mLayoutInflater.inflate(R.layout.wallpaper_item, parent, false);
} else {
image = (ImageView) convertView;
}
int thumbRes = mThumbs.get(position);
image.setImageResource(thumbRes);
Drawable thumbDrawable = image.getDrawable();
if (thumbDrawable != null) {
thumbDrawable.setDither(true);
} else {
Log.e("Paperless System", String.format(
"Error decoding thumbnail resId=%d for wallpaper #%d",
thumbRes, position));
}
return image;
}
}
public void onClick(View v) {
selectWallpaper(mGallery.getSelectedItemPosition());
}
class WallpaperLoader extends AsyncTask<Integer, Void, Bitmap> {
BitmapFactory.Options mOptions;
WallpaperLoader() {
mOptions = new BitmapFactory.Options();
mOptions.inDither = false;
mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
}
protected Bitmap doInBackground(Integer... params) {
if (isCancelled()) return null;
try {
return BitmapFactory.decodeResource(getResources(),
mImages.get(params[0]), mOptions);
} catch (OutOfMemoryError e) {
return null;
}
}
#Override
protected void onPostExecute(Bitmap b) {
if (b == null) return;
if (!isCancelled() && !mOptions.mCancel) {
// Help the GC
if (mBitmap != null) {
mBitmap.recycle();
}
mInfoView.setText(getResources().getStringArray(R.array.info)[mGallery.getSelectedItemPosition()]);
final ImageView view = mImageView;
view.setImageBitmap(b);
mBitmap = b;
final Drawable drawable = view.getDrawable();
drawable.setFilterBitmap(true);
drawable.setDither(true);
view.postInvalidate();
mLoader = null;
} else {
b.recycle();
}
}
void cancel() {
mOptions.requestCancelDecode();
super.cancel(true);
}
}
Wallpapers are defined in res/values/wallpapers.xml:
<resources>
<string-array name="wallpapers" translatable="false">
<item>wallpaper1</item>
<item>wallpaper2</item>
<item>wallpaper3</item>
[...]
</string-array>
<string-array name="info" translatable="false">
<item>wallpaper1 description</item>
<item>wallpaper2 description</item>
<item>wallpaper3 description</item>
[...]
</string-array>
How can I change patch of my wallpapers from /res/drawable to /obb/com.example.app/main.8.com.example.app.obb ?
Thanks in advance for your help!
First read about APK expansion files here: http://developer.android.com/google/play/expansion-files.html
Then, read more about using Gallery widget here: http://developer.android.com/reference/android/widget/Gallery.html
Finally, consider using a ViewPager, referenced from here: http://developer.android.com/training/implementing-navigation/lateral.html
I am not sure if you should ask questions like that, please correct me if I am wrong..
I have implemented android app which should download images from server and display them in ListView, but very interesting thing occures while images are downloading
As you can see in video pictures which haven't been downloaded yet are represented by those which have been already downloaded. How that can happen? I've thinking about it almost two days.
http://www.youtube.com/watch?v=lxY-HAuJO0o&feature=youtu.be
here is my code of ListView adapter.
public class MoviesAdapter extends ArrayAdapter<ParkCinema> {
private ArrayList<ParkCinema> movieDataItems;
private Activity context;
public MoviesAdapter(Activity context, int textViewResourceId, ArrayList<ParkCinema> movieDataItems) {
super(context, textViewResourceId, movieDataItems);
this.context = context;
this.movieDataItems = movieDataItems;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.movie_data_row, null);
}
ParkCinema movie = movieDataItems.get(position);
if (movie!=null){
ImageView imageView = (ImageView) convertView.findViewById(R.id.movie_thumb_icon);
String url = movie.poster();
if (url!=null) {
Bitmap bitmap = fetchBitmapFromCache(url);
if (bitmap==null) {
new BitmapDownloaderTask(imageView).execute(url);
}
else {
imageView.setImageBitmap(bitmap);
}
}
}
return convertView;
}
private LinkedHashMap<String, Bitmap> bitmapCache = new LinkedHashMap<String, Bitmap>();
private void addBitmapToCache(String url, Bitmap bitmap) {
if (bitmap != null) {
synchronized (bitmapCache) {
bitmapCache.put(url, bitmap);
}
}
}
private Bitmap fetchBitmapFromCache(String url) {
synchronized (bitmapCache) {
final Bitmap bitmap = bitmapCache.get(url);
if (bitmap != null) {
return bitmap;
}
}
return null;
}
private class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
private String url;
private final WeakReference<ImageView> imageViewReference;
public BitmapDownloaderTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
}
#Override
protected Bitmap doInBackground (String... source) {
url = source[0];
Bitmap image;
try{
image = BitmapFactory.decodeStream(new URL(url).openConnection().getInputStream());
return image;
}
catch(Exception e){Log.e("Error", e.getMessage()); e.printStackTrace();}
return null;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
addBitmapToCache(url, bitmap);
imageViewReference.get().setImageBitmap(bitmap);
}
}
}
Edit 3:
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.movie_data_row, null);
}
ParkCinema movie = movieDataItems.get(position);
ImageView imageView = (ImageView) convertView.findViewById(R.id.movie_thumb_icon);
if (movie!=null){
String url = movie.poster();
if (url != null) {
Bitmap bitmap = fetchBitmapFromCache(url);
if (bitmap == null) {
imageView.setImageResource(R.drawable.no_image);
new BitmapDownloaderTask(imageView).execute(url);
}
else {
imageView.setImageBitmap(bitmap);
}
}
else {
imageView.setImageResource(R.drawable.no_image);
}
}
else {
imageView.setImageResource(R.drawable.no_image);
}
return convertView;
}
Aha! I think I may know the issue. Right now, your getView method sets your ImageView like this:
Gets movie object at position
Pulls out the movie's thumbnail url
Using that url, it tries to find the image in the cache
If it finds the image, it sets it
If it can't find the image, it starts an async network request to go get it, and sets it after it gets downloaded.
Your issus arises since ListView reuses its rows' Views. When the first View scrolls off the screen, rather than inflate a new one, ListView passes the now offscreen row's View in as convertView for you to reuse (this is for efficiency).
When your getView gets a convertView that is getting reused, its ImageView has already been set from the row that had it before, so you see the old image from the offscreen row's View. With your current getView process, you check for the new row's image, and it doesn't find it in the cache, it starts a request to download it. While it is downloading, you see the old image until you get the new image.
To fix this, you need to make sure you set every field in the row's View immediately, to make sure you don't have any Views showing stale data. I would suggest you set the ImageView to the default drawable resource (you have set in your R.layout.movie_data_row) while you wait for the network download to get the image.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.movie_data_row, null);
}
ParkCinema movie = movieDataItems.get(position);
ImageView imageView = (ImageView) convertView.findViewById(R.id.movie_thumb_icon);
if (movie != null) {
String url = movie.poster();
if (url != null) {
Bitmap bitmap = fetchBitmapFromCache(url);
if (bitmap == null) {
// Set the movie thumbnail to the default icon while we load
// the real image
imageView.setImageResource(R.drawable.movie_thumb_icon);
new BitmapDownloaderTask(imageView).execute(url);
}
else {
// Set the image to the bitmap we get from the cache
imageView.setImageBitmap(bitmap);
}
}
else {
// Set the movie thumbnail to the default icon, since it doesn't
// have a thumbnail URL
imageView.setImageResource(R.drawable.movie_thumb_icon);
}
}
else {
// Set the movie thumbnail to the default icon, since there's no
// movie data for this row
imageView.setImageResource(R.drawable.movie_thumb_icon);
}
-Edit-
Updated to be even more robust, using your drawable. You also have an issue with your BitmapDownloaderTask, it does not handle errors/null. Try adding this as well.
#Override
protected void onPostExecute(Bitmap bitmap) {
addBitmapToCache(url, bitmap);
if (bitmap == null) {
// Set the movie thumbnail to the default icon, since an error occurred while downloading
imageViewReference.get().setImageResource(R.drawable.movie_thumb_icon);
}
else {
imageViewReference.get().setImageBitmap(bitmap);
}
}
i had this issue and implemented lruCache ...i believe you need api 12 and above or use the compatiblity v4 library. lurCache is fast memory but it also has a budget, so if your worried about that you can use a diskcache ...its all described here http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
I'll now provide my implementation which is a singleton i call from anywhere like this:
//where first is a string and other is a imageview to load
DownloadImageTask.getInstance().loadBitmap(avatarURL, iv_avatar);
here's the ideal code to cache and then call the above in getView of an adapter when retrieving the web image:
public class DownloadImageTask {
private LruCache<String, Bitmap> mMemoryCache;
/* create a singleton class to call this from multiple classes */
private static DownloadImageTask instance = null;
public static DownloadImageTask getInstance() {
if (instance == null) {
instance = new DownloadImageTask();
}
return instance;
}
//lock the constructor from public instances
private DownloadImageTask() {
// Get max available VM memory, exceeding this amount will throw an
// OutOfMemory exception. Stored in kilobytes as LruCache takes an
// int in its constructor.
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
#Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount() / 1024;
}
};
}
public void loadBitmap(String avatarURL, ImageView imageView) {
final String imageKey = String.valueOf(avatarURL);
final Bitmap bitmap = getBitmapFromMemCache(imageKey);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
imageView.setImageResource(R.drawable.ic_launcher);
new DownloadImageTaskViaWeb(imageView).execute(avatarURL);
}
}
private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
private Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
/* a background process that opens a http stream and decodes a web image. */
class DownloadImageTaskViaWeb extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public DownloadImageTaskViaWeb(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
mIcon = BitmapFactory.decodeStream(in);
} catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
addBitmapToMemoryCache(String.valueOf(urldisplay), mIcon);
return mIcon;
}
/* after decoding we update the view on the mainUI */
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
}
Views are reused for performance with Adapters. You should use another approch .
You have to have a class holder which reuse your views. In your case you class should be something like this:
public class MoviesAdapter extends ArrayAdapter<ParkCinema> {
private ArrayList<ParkCinema> movieDataItems;
private Activity context;
public MoviesAdapter(Activity context, int textViewResourceId, ArrayList<ParkCinema> movieDataItems) {
super(context, textViewResourceId, movieDataItems);
this.context = context;
this.movieDataItems = movieDataItems;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.movie_data_row, null);
holder = new ViewHolder();
holder.imageView = (BarImageView) convertView.findViewById(R.id.movie_thumb_icon);
} else {
holder = (ViewHolder) convertView.getTag();
}
ParkCinema movie = movieDataItems.get(position);
if (movie!=null){
String url = movie.poster();
if (url!=null) {
Bitmap bitmap = fetchBitmapFromCache(url);
if (bitmap==null) {
new BitmapDownloaderTask(imageView).execute(url);
}
else {
imageView.setImageBitmap(bitmap);
}
}
}
return convertView;
}
private LinkedHashMap<String, Bitmap> bitmapCache = new LinkedHashMap<String, Bitmap>();
private void addBitmapToCache(String url, Bitmap bitmap) {
if (bitmap != null) {
synchronized (bitmapCache) {
bitmapCache.put(url, bitmap);
}
}
}
private Bitmap fetchBitmapFromCache(String url) {
synchronized (bitmapCache) {
final Bitmap bitmap = bitmapCache.get(url);
if (bitmap != null) {
return bitmap;
}
}
return null;
public static class ViewHolder {
ImageView imageView;
}
}
I have spent hours trying to figure this one out as well...Thanks to Steven Byle's solution...
Here is my solution to something similar when a user selects an item from a list:
adapter.setSelectedIndex(position);
then in the custom adapter:
public void setSelectedIndex(int ind)
{
selectedIndex = ind;
notifyDataSetChanged();
}
and then finally in the getView method of the adapter:
if(selectedIndex!= -1 && position == selectedIndex)
{
holder.tab.setBackgroundColor(Color.BLACK);
}
else{
holder.tab.setBackgroundColor(Color.DKGRAY);
}
So in conclusion make sure you assign default values
In my case i used Picasso library instead of AsyncTask for downloading image.
enter link description here
Also write if else condition, that is set null to image if url is not available
instead of using the convertview object create a new view each time.
View localView = ((LayoutInflater)parentscreen.getSystemService("layout_inflater")).inflate(R.layout.activity_list_row, null);
By inflating as above.
I am trying to add Loading dialog in following code to fetch image from server and display it in Gallery view. it shows blank screen untill image comes. please help me how do i show Loading dialog while getting image from server.
Here is the code, pls help.
public class ImagedisplaytestActivity extends Activity {
private ImageView leftArrowImageView;
private ImageView rightArrowImageView;
private Gallery gallery;
public int selectedImagePosition;
private GalleryImageAdapter galImageAdapter;
private String bitmapImg = "";
Bitmap bitmap = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setupUI();
}
private void setupUI() {
Intent i = getIntent();
Bundle extras=i.getExtras();
bitmapImg = extras.getString("BitmapImage");
selectedImagePosition = extras.getInt("Pos");
leftArrowImageView = (ImageView) findViewById(R.id.left_arrow_imageview);
rightArrowImageView = (ImageView) findViewById(R.id.right_arrow_imageview);
gallery = (Gallery) findViewById(R.id.gallery);
leftArrowImageView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (selectedImagePosition > 0) {
--selectedImagePosition;
}
gallery.setSelection(selectedImagePosition, false);
}
});
rightArrowImageView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (selectedImagePosition < DetailView.bitmapURL.size() - 1) {
++selectedImagePosition;
}
gallery.setSelection(selectedImagePosition, false);
}
});
gallery.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
selectedImagePosition = pos;
if (selectedImagePosition > 0 && selectedImagePosition < DetailView.bitmapURL.size() - 1) {
leftArrowImageView.setImageDrawable(getResources().getDrawable(R.drawable.arrow_left_disabled));
rightArrowImageView.setImageDrawable(getResources().getDrawable(R.drawable.arrow_right_disabled));
} else if (selectedImagePosition == 0) {
leftArrowImageView.setImageDrawable(getResources().getDrawable(R.drawable.arrow_left_enabled));
} else if (selectedImagePosition == DetailView.bitmapURL.size() - 1) {
rightArrowImageView.setImageDrawable(getResources().getDrawable(R.drawable.arrow_right_enabled));
}
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});
galImageAdapter = new GalleryImageAdapter(this, DetailView.bitmapURL);
gallery.setAdapter(galImageAdapter);
if (DetailView.bitmapURL.size() > 0) {
gallery.setSelection(selectedImagePosition, false);
}
if (DetailView.bitmapURL.size() == 1) {
rightArrowImageView.setImageDrawable(getResources().getDrawable(R.drawable.arrow_right_disabled));
}
}
public class GalleryImageAdapter extends BaseAdapter {
private Activity context;
private ImageView imageView;
private List<String> plotsImages;
private ViewHolder holder;
public GalleryImageAdapter(Activity context, List<String> plotsImages) {
this.context = context;
this.plotsImages = plotsImages;
}
#Override
public int getCount() {
return plotsImages.size();
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
holder = new ViewHolder();
imageView = new ImageView(this.context);
imageView.setPadding(3, 3, 3, 3);
convertView = imageView;
holder.imageView = imageView;
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.imageView.setLayoutParams(new Gallery.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
holder.imageView.setScaleType(ImageView.ScaleType.FIT_XY);
try {
bitmap = DownloadImage(plotsImages.get(position));
holder.imageView.setImageBitmap(bitmap);
bitmap = null;
} catch (Exception e) {
e.printStackTrace();
}
return imageView;
}
private class ViewHolder {
ImageView imageView;
}
private Bitmap DownloadImage(String URL){
Bitmap bitmap = null;
try {
InputStream in = OpenHttpConnection(URL);
bitmap = BitmapFactory.decodeStream(in);
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
return bitmap;
}
private InputStream OpenHttpConnection(String urlString) throws IOException{
InputStream in = null;
int response = -1;
URL url = new URL(urlString);
URLConnection conn = url.openConnection();
if (!(conn instanceof HttpURLConnection)) {
throw new IOException("Not an HTTP connection");
}
try{
HttpURLConnection httpConn = (HttpURLConnection) conn;
httpConn.setAllowUserInteraction(false);
httpConn.setInstanceFollowRedirects(true);
httpConn.setRequestMethod("GET");
httpConn.connect();
response = httpConn.getResponseCode();
if (response == HttpURLConnection.HTTP_OK) {
in = httpConn.getInputStream();
}
}
catch (Exception ex){
throw new IOException("Error connecting");
}
return in;
}
}
#Override
public void onBackPressed() {
DetailView.bundleID = DetailView.idList.get(selectedImagePosition);
super.onBackPressed();
}
}
You should use AsyncTask!
See this also!
& in Vogella Example will clear your doubt .
see this for the lazy image Loading DEMO! on github.
use assynctask,
in that assynctask method you can write your getting images from server in doinbackground(),
in that time you can display alert dialogue using preexectue(), after loading from server just dismiss the alert dialogue in postexectue().
This code work for me
private class LoadGlobalDataSearch extends AsyncTask<String, Void, Void>
{
#Override
protected void onPostExecute(Void result)
{
super.onPostExecute(result);
progressdialog.dismiss();
}
#Override
protected Void doInBackground(String... params)
{
//Load your images this task
setupUI();
return null;
}
#Override
protected void onPreExecute()
{
super.onPreExecute();
progressdialog = ProgressDialog.show(MainActivity.this, "",getString(R.string.inprogress));
}
}
call this class in your oncreate method
new loadImages().execute(0);
You can use a progress dialoge until the image is downloaded then remove the dialog.
In OpenHttpConnection add this --
dialog = ProgressDialog.show(DutyRotaActivity.this, "",
"Please wait for few seconds...", true);
in DownloadImage dissmiss the dialog.
dialog.dismiss();
Or you can use asynchronous task too. cause network operation should always do on other thread
not in the main thread. In that case in preExecute add the dialog and in postExecute dissmiss the dialog. And in doinbackground call your downloadimage function that will also do the tricks.
Edit
Here is a complete source code and a library to load image lazily in the listView. I think its also can help you to solve your issue.Thanks
https://github.com/nostra13/Android-Universal-Image-Loader
I'm developping a thumbnail mosaic of all pictures on SdCard.
If the user longclick on a picture, i want to start drag and drop process.
I clearly manage to get all images files and showing them with my own BaseAdapter in a GridView, but the process is long because I create previews of each image this way :
uri = Uri.fromFile(file);
try {
bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
if (bitmap != null) {
newBitmap = Bitmap.createScaledBitmap(bitmap, 200, 200, true);
bitmap.recycle();
// here code to add the bitmap to the adapter (see after)
}
} catch (IOException e) {
//Error fetching image, try to recover
}
So in order to avoid long wait, I'm loading the images in a AsyncTask.
The process work well and the images appears one by one, but now an error appear on LongClick, in this code :
mGrid.setOnItemLongClickListener(new OnItemLongClickListener()
{
public boolean onItemLongClick(AdapterView<?> adapter, View view, int position, long id)
{
ClipData data = ClipData.newPlainText("AdapterPosition", String.valueOf(position) );
view.startDrag(data, new DragShadowBuilder(view), null, 0);
return true;
}
});
and the error :
12-27 16:54:14.980: E/View(8089): Unable to initiate drag
12-27 16:54:14.980: E/View(8089): java.lang.NullPointerException
12-27 16:54:14.980: E/View(8089): at android.view.View.startDrag(View.java:11470)
12-27 16:54:14.980: E/View(8089): at renault.limandroid.widget.PartageWidgetActivity$9.onItemLongClick(PartageWidgetActivity.java:547)
Is someone have a idea?
Here is a large part of my code :
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_partage_widget);
// ... //
mImageAdapter = new ImageAdapter(this);
loadImages();
mGrid.setOnItemLongClickListener(new OnItemLongClickListener()
{
public boolean onItemLongClick(AdapterView<?> adapter, View view, int position, long id)
{
ClipData data = ClipData.newPlainText("AdapterPosition", String.valueOf(position) );
view.startDrag(data, new DragShadowBuilder(view), null, 0);
return true;
}
});
mGrid.setAdapter(mImageAdapter);
}
private void loadImages()
{
// si c'est la première fois qu'on passe ici
if (mLoadImageTask.getStatus() == AsyncTask.Status.PENDING)
{
mLoadImageTask.execute();
}
// si le scan est terminé mais qu'on veut le remettre à jour
// on va recréer la tâche
else if (mLoadImageTask.getStatus() == AsyncTask.Status.FINISHED)
{
mLoadImageTask = new LoadImagesFromSDCard();
mLoadImageTask.execute();
}
}
#Override
protected Object doInBackground(Object... params) {
setProgressBarIndeterminateVisibility(true);
Log.d(TAG,"LoadImages - do in background");
Bitmap bitmap = null;
Bitmap newBitmap = null;
Uri uri = null;
List<File> listFiles = new ArrayList<File>();
listFiles = StorageManager.findImageFiles();
for (File file : listFiles)
{
// mImageListFile contains the files that have already been loaded
if (!mImageListFile.contains(file))
{
uri = Uri.fromFile(file);
try {
bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
if (bitmap != null) {
newBitmap = Bitmap.createScaledBitmap(bitmap, 200, 200, true);
bitmap.recycle();
if (newBitmap != null) {
publishProgress(new LoadedImage(newBitmap));
}
}
} catch (IOException e) {
//Error fetching image, try to recover
}
mImageListFile.add(file);
}
}
return null;
}
/**
* Add a new LoadedImage in the images grid.
*
* #param value The image.
*/
#Override
public void onProgressUpdate(LoadedImage... value)
{
mImageAdapter.addPhoto(value[0]);
mImageAdapter.notifyDataSetChanged();
}
/**
* Set the visibility of the progress bar to false.
*
* #see android.os.AsyncTask#onPostExecute(java.lang.Object)
*/
#Override
protected void onPostExecute(Object result)
{
setProgressBarIndeterminateVisibility(false);
mGrid.invalidate();
}
}
class ImageAdapter extends BaseAdapter {
private Context mContext;
private ArrayList<LoadedImage> photos = new ArrayList<LoadedImage>();
public ImageAdapter(Context context) {
mContext = context;
photos.add(new LoadedImage(BitmapFactory.decodeResource(context.getResources(),
R.drawable.click)));
}
public void addPhoto(LoadedImage photo)
{
photos.add(photo);
}
public int getCount() {
return photos.size();
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent)
{
final ImageView imageView;
final int pos = position;
if (convertView == null) {
imageView = new ImageView(mContext);
} else {
imageView = (ImageView) convertView;
}
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageView.setPadding(2, 2, 2, 2);
imageView.setLayoutParams(new GridView.LayoutParams(parent.getWidth()/4, parent.getWidth()/4));
imageView.setImageBitmap(photos.get(position).getBitmap());
return imageView;
}
public Object getItem(int position)
{
return photos.get(position);
}
}
/**
* A LoadedImage contains the Bitmap loaded for the image.
*/
private static class LoadedImage {
Bitmap mBitmap;
LoadedImage(Bitmap bitmap) {
mBitmap = bitmap;
}
public Bitmap getBitmap() {
return mBitmap;
}
}
I have an adapter with this getView:
public View getView(int position, View convertView, ViewGroup parent) {
Log.d("getView gv", position+"");
NewsLine holder = null;
if (convertView == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
convertView = inflater.inflate(R.layout.grid_entry, parent, false);
holder = new NewsLine();
holder.iv= (ImageView) convertView.findViewById(R.id.photo);
convertView.setTag(holder);
} else {
holder = (NewsLine) convertView.getTag();
}
NewsItem item=news.ITEMS.get(position);
//---------
if(item.imgurl!=null && item.imgurl.compareToIgnoreCase("null")!=0)
{
holder.iv.setVisibility(View.VISIBLE);
mMemoryCache.loadBitmap(item.imgurl, holder.iv,position);
}
else
holder.iv.setVisibility(View.INVISIBLE);
//-------------
return convertView;
}
I have two problems:
the getView is called several times for position 0 (the bitmap is downloaded with an AsyncTask if its missed in a LruCache). I have an animation (alpha from 0-1) that restarts several times for that position.
because I'm recycling the view sometimes you can see the old imageView content for a fraction of a second.
//----
And here is the cache class (only heap):
public class SetImgAT extends LruCache<String, Bitmap> {
private static SetImgAT instance;
private Animation FadeInAnimation;
private SetImgAT(int size, Context context) {
super(size);
FadeInAnimation = AnimationUtils.loadAnimation(context, R.anim.fadein);
}
public static synchronized SetImgAT getInstance(int size, Context context) {
if (instance == null) {
instance = new SetImgAT(size, context);
}
return instance;
}
#Override
protected int sizeOf(String key, Bitmap value) {
return (value.getRowBytes() * value.getHeight());
}
public void loadBitmap(String url, ImageView imageView,int pos) {
Bitmap bitmap = instance.get(url.hashCode() + "");
if (bitmap != null) {
Log.d("ImageCache", "hit - "+url.hashCode()+"pos:"+pos);
imageView.setImageBitmap(bitmap);
imageView.invalidate();
} else {
Log.d("ImageCache", "miss");
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
task.execute(url);
}
}
class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
ImageView mImageView;
public BitmapWorkerTask(ImageView imageView) {
mImageView = imageView;
}
#Override
protected Bitmap doInBackground(String... url) {
Bitmap Picture = null;
if (url[0] != null && url[0].compareToIgnoreCase("null") != 0) {
Log.d("GetBMP from", url[0]);
URL img_value = null;
try {
img_value = new URL(url[0]);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
Picture = BitmapFactory.decodeStream(img_value
.openConnection().getInputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (Picture == null) {
Log.d("deb", "no bitmap");
} else {
Log.d("got deb", "got bitmap to "+url[0].hashCode());
instance.put(url[0].hashCode()+"", Picture);
}
}
return Picture;
}
#Override
protected void onPostExecute(Bitmap result) {
// super.onPostExecute(result);
if (result != null) {
Log.d("deb", "set bitmap");
mImageView.setImageBitmap(result);
//mImageView.startAnimation(FadeInAnimation);
}
}
}
//----------------
}
Thank you! :)
I've seen similar behavior when scrolling back and forth or haphazardly calling notifyDataSetChanged().
As an enhancement to what you are doing now I would suggest using Picasso instead since it handles this case very well, in addition to the fade in animation.
A one liner in your getView():
Picasso.with(context).load(urlToLoad).into(imageView);
See:
http://square.github.io/picasso/