Viewpager show two images, high and low res, screen black during loading - android

one again facing a "strange" problem.
Im using a Viewpager to display 2 separate images (Viewpager contains just a layout and 2 imageviews).
The concept would be, display a low resoltion image from local file cache (immediately) and load the high-res picutre meanwhile and show it.
the problem is: only using low res pictures, pictures are showing immediately and everything perfect, but as soon as high-res pictures are enabled (to be shown),
if the user swipes really fast, the screen stays black for "a short time" (500ms to 1,5s) and low resolution images are never displayed.
just the high-res pictures..
maybe anyone faced a similar problem, any assistance appriciated :) thank you!
ViewPager code:
/**
* Create and add a new page of the image at the given position.
*
* #param collection
* #param position
*/
#Override
public Object instantiateItem (View collection, final int position) {
Log.v(TAG, "instantiateItem: pos: " +position);
final Context context = collection.getContext();
RelativeLayout layout = new RelativeLayout(context);
ImageViewTouch imageView = new ImageViewTouch(context);
ImageViewTouch imageView2 = new ImageViewTouch(context);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.FILL_PARENT, RelativeLayout.LayoutParams.FILL_PARENT);
layout.addView(imageView, lp);
layout.addView(imageView2, lp);
imageView.setOnSingleTapConfirmedListener((OnImageViewSingleTapConfirmedListener) context);
imageView2.setOnSingleTapConfirmedListener((OnImageViewSingleTapConfirmedListener) context);
imageView2.setVisibility(View.GONE);
if (position == restorePageNumber) {
loadBitmap(context, position, imageView, imageView2, restoreZoomLevel);
Log.w(TAG, "zoom level regocnized for pos: " + position + " resetting...");
restorePageNumber = Constants.INVALID_INT;
restoreZoomLevel = Constants.INVALID_LONG;
} else {
loadBitmap(context, position, imageView, imageView2, Constants.INVALID_LONG);
}
imageView.setFitToScreen(true);
imageView2.setFitToScreen(true);
activePages.put(position, imageView2);
((ViewPager) collection).addView(layout);
return layout;
}
protected void loadBitmap (Context context, int position,
ImageViewTouch imageView, ImageViewTouch imageView2,
float restoreZoomLevel) {
Photo photo = getPhotoAtPosition(position);
Log.v(TAG, "loading photo. pos: " + position + " id: " + photo.getId());
// show small image first
if (!(photo instanceof CameraPhoto)) {
StorageManager.retrieveBitmapBackgroundWithImageview(context, photo,
Photo.SIZE_SMALL, imageView, true, Constants.INVALID_LONG);
}
// afterwards replace with big image
StorageManager.retrieveBitmapBackgroundWithImageview(context, photo,
Photo.SIZE_LARGE, imageView2, true, restoreZoomLevel);
}
in Those methods (retrieveBitmapBackgroundWithImageview) Images are loaded in Background, and afterwards set to the imageview.
It seems that it has some problem with setting the large bitmap.
Even if the Imageview with the large bitmap stays hidden (View.GONE), and only local cache images are shown, the ViewPager stays black for some "time" (as above, 500ms to 1.5s) on loading pages, if swiping fast :)
thx :)

If you mean swiping fast to alot of pages it just has problems with loading all those views. Do you cancel/interrupt the loadBitmap tasks that are out of the range of the ViewPager (not in view or cache)?
And, do you handle concurrency in threads? If you always want to load the pages that are in the ViewPager's view or cache first, you should Override Comparator and let it compare on the page. The comparator can be used in a PriorityBlockingQueue which can be used in a Executor

Related

Android ViewPager using SubsamplingScaleImageView results in blank screen for sometime when sliding images

I have made a ViewPager using davemorrissey's SubsamplingScaleImageView
When sliding the ViewPager, the next slide appears to be blank for some seconds before the image loads.
Has anyone faced same type of issue ? Any pointers for possible fixes ?
viewPager = (ViewPager) findViewById(R.id.pager);
magePagerAdapter adapter = new ImagePagerAdapter();
viewPager.setAdapter(adapter);
ArrayList<String> imageFull = new ArrayList<String>();
for(int i=0;i<10;i++){
String image = "mnt/sdcard/imageDemo"+i+".jpg";
imageFull.add(image);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Context context = ImageGallery.this;
SubsamplingScaleImageView fullImage = new SubsamplingScaleImageView(ImageGallery.this);
fullImage.setImage(ImageSource.uri(imageFull.get(position)));
return fullImage;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((SubsamplingScaleImageView) object);
}
Your Screen is black cause the image is not decoded yet.
You can use:
public final void setImage(ImageSource imageSource, ImageSource previewSource)
and use a thumbnail as preview. The preview is shown until the decoding of the image is finished.
Note that:
the Preview image cannot be used unless dimensions are provided for the main image.
No tested but something like:
Uri uri = imageFull.get(position);
Bitmap preview = MediaStore.Images.Thumbnails.getThumbnail(
getContentResolver(), uri,
MediaStore.Images.Thumbnails.MINI_KIND,
null);
ImageSource src = ImageSource.uri(uri);
src .dimensions(w, h); // if you don't know the size, you can just decode the bounds of your image which is quite fast
fullImage.setImage(src , ImageSource.bitmap(preview));
If generation of the Thumbnail is to slow for you, feel free to do that in an AsyncTask.
I was able to workaround it with this patch. I also wrote a similar answer in the visibility problem issue
initialiseBaseLayer is called the first onDraw of the view, that's why it doesn't load until the page is partially visible.
The issue is that initializing outside onDraw you lose access to the canvas, which is used to calculate the max bitmap dimensions:
private Point getMaxBitmapDimensions(Canvas canvas) {
if (VERSION.SDK_INT >= 14) {
try {
int maxWidth = (Integer)Canvas.class.getMethod("getMaximumBitmapWidth").invoke(canvas);
int maxHeight = (Integer)Canvas.class.getMethod("getMaximumBitmapHeight").invoke(canvas);
return new Point(maxWidth, maxHeight);
} catch (Exception e) {
// Return default
}
}
return new Point(2048, 2048);
}
So, you will have to provide the max dimensions on your own. I'm using this function. I store the value during the onCreate of the activity and later the ViewPager get that value.
Before setting the image call fullImage.setMaxDimensions(x, y) and that's it.
It works fine (in my case) for a ViewPager, but it doesn't on a RecyclerView, so use it with care.

Android scrollby() blocks my UI

I have used custom coveflow, everything works fine when i load small amount of data, but it doesn't work with large amount of data,
check it out my below code
if (mAdapter == null || mAdapter.getCount() == 0)
throw new IllegalStateException(
"You are trying to scroll container with no adapter set. Set adapter first.");
if (mLastCenterItemIndex != -1) {
final int lastCenterItemPosition = (mFirstItemPosition + mLastCenterItemIndex)
% mAdapter.getCount();
final int di = lastCenterItemPosition - position;
final int dst = (int) (di * mCoverWidth * mSpacing);
mScrollToPositionOnNextInvalidate = -1;
scrollBy(-dst, 0);
} else {
mScrollToPositionOnNextInvalidate = position;
}
invalidate();
i have used this code to move my item to center of the tablet, now i am gng to explain my view, in one activity half of my screen occupies coverflow and other half occupies mapview, when i click map marker icon i need to move particular item to center of my screen in coverflow, so basically my coverflow and map sync, the changes must affect both,
it works perfectly when i load small amount of data, but now i tried to load 11000 records when i click marker then my UI gets blocked becuase of scrollby in Coverflow, can you suggest any idea to move my item center?? or is there any method which doesn't affect UI
All suggestion are most welcome
Thanks
Just include your code in
new Thread(new Runnable() {
public void run () {
//.........
}
}).start();
or load items by parts

android: retain image in display once loaded from url?

i am retriving around 20 images url from an xml , and appearing them in gridview.
thing going nice except one.
when i scroll down new images start loading , but when i again scrolls up. the previously loaded images again stars loading .
how can i resolve this bug. the previously loaded image should be retained in the layout , why it is not retaining itself .
I am using the adapter extends the BaseAdapter to view the images
Gridview like any other views that inherit AdapterView only uses the amount of views that you see on the screen. So when you scroll your views going to be reused. The Adapter's responsibility is to reassign the content to the views.
So, you need to cache your bitmaps. There is a very nice tutorial about this on the Android developer side.
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
(I uses grid view in the example)
Every time you scroll, getView() method of the adapter is called.
So every time one view come in to the screen is built in the getView() method.
You build the new view in that method, I suppose you download the image when that method is called, then one solution is caching the images. If you download few images and they are small size, a simply Map with key url and value bitmap solves the problem, if not, use database.
Every time getView() is called, check first if image is in cache, if image is in cache use it to build the view, if not download it.
use LoaderImageView from this blog http://blog.blundell-apps.com/imageview-with-loading-spinner/ & add following function
public void setImageDrawable(final MyGridViewItem obj) {
mDrawable = null;
if(obj.mDrawable != null){
mDrawable = obj.mDrawable
imageLoadedHandler.sendEmptyMessage(COMPLETE);
return;
}
mSpinner.setVisibility(View.VISIBLE);
mImage.setVisibility(View.GONE);
new Thread(){
public void run() {
try {
mDrawable = getDrawableFromUrl(imageUrl);
obj.mDrawable = mDrawable;
imageLoadedHandler.sendEmptyMessage(COMPLETE);
} catch (MalformedURLException e) {
imageLoadedHandler.sendEmptyMessage(FAILED);
} catch (IOException e) {
imageLoadedHandler.sendEmptyMessage(FAILED);
}
};
}.start();
}

How can I improve the performance (first load time, scroll smoothness) of a GridView rendering many large images?

My application allows users to take photos with their devices camera app, which are saved to an app-specific folder.
Then, users can choose to send any image they choose to one of their contacts, using their choice of their devices email app.
Sadly the "choose images to send" dialog takes a long time to load initially, and is very "choppy" (frequent noticable pauses) when scrolling. I suspect that this is because the images are very large — pictures taken with my camera are around 2.3MB each. The initial screen (15 images displayed) thus needs to pull around 34.5MB off disk before it can render.
GridView layout.xml
<!-- ... -->
<GridView
android:id="#+id/gvSelectImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:numColumns="3"
android:gravity="center" />
<!-- ... -->
Adapter getView()
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
File pic = pics[position];
View checkedImageView =
LayoutInflater.from(context).inflate(R.layout.checked_image,
parent, false);
final ImageView imageView =
(ImageView) checkedImageView.findViewById(R.id.checkableImage);
/* LOAD IMAGE TO DISPLAY */
//imageView.setImageURI(pic.toURI())); // "exceeds VM budget"
Bitmap image;
try {
int imageWidth = 100;
image = getBitmapFromFile(pic, imageWidth); // TODO: slooow...
} catch (IOException e) {
throw new RuntimeException(e);
}
imageView.setImageBitmap(image);
/* SET CHECKBOX STATE */
final CheckBox checkBoxView =
(CheckBox) checkedImageView.findViewById(R.id.checkBox);
checkBoxView.setChecked(isChecked[position]);
/* ATTACH HANDLERS */
checkBoxView.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
CheckableLocalImageAdapter.this.isChecked[position] =
checkBoxView.isChecked();
}
});
imageView.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
checkBoxView.setChecked(!checkBoxView.isChecked());
}
});
return checkedImageView;
}
getBitmapFromFile() is just adapted from the answer given at https://stackoverflow.com/a/823966/633626. I note that the code there calls decodeStream() twice and might be doubling my I/O, but I think the dialog will still be too slow and too jerky even if I halve the amount of time to takes to render.
My testing phone is a standard Samsung Galaxy S II if that's relevant. I suspect older devices will be even slower / choppier.
What's the best way to improve performance here? I'm guessing a combination of an AsyncTask to load each image (so that the dialog can load before all images are rendered) and some sort of caching (so that scrolling doesn't require rerendering images and isn't jerky), but I'm lost as to how to fit those pieces in with the rest of my puzzle.
You'll want to work with thumbnails instead. Loading the whole image from the SD card will always be slow. Only load the full version of the one(s) they really want to send.
This isn't to say that caching and async loading are a bad idea. I'd change the code to work with thumbnails first, recheck performance, and then go the async route. You can show a little spinner over the currently loading thumbnail to indicate that there's more to come.
Here's a built-in class to help you retrieve them.
Try loading your Images with a SampleSize into the GridView:
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 7;
Bitmap bitmap = BitmapFactory.decodeFile(f.getPath(), opts);
More Info about the Options here
And also you can swap the image loading into a Thread or AsyncTask.

Scrolling through ListView with some images very laggy

I've got the below screen that contains some images (6 per visible page). Scrolling up and down seems quite laggy to me. It's like it's rendering the images again. Scrolling back up seems worse than scrolling down.
Anyone know how to increase performance in such an area to create a nice smooth scroll?
Update: The images and text is all retrieved from my SQLite database. The list is created using SimpleCursorAdapter.
private class HistoryViewBinder implements SimpleCursorAdapter.ViewBinder
{
//private int wallpaperNumberIndex;
private int timeIndex;
private int categoryIndex;
private int imageIndex;
private java.text.DateFormat dateFormat;
private java.text.DateFormat timeFormat;
private Date d = new Date();
public HistoryViewBinder(Context context, Cursor cursor)
{
dateFormat = android.text.format.DateFormat.getDateFormat(context);
timeFormat = android.text.format.DateFormat.getTimeFormat(context);
//wallpaperNumberIndex = cursor.getColumnIndexOrThrow(HistoryDatabase.KEY_WALLPAPER_NUMBER);
timeIndex = cursor.getColumnIndexOrThrow(HistoryDatabase.KEY_TIME);
categoryIndex = cursor.getColumnIndexOrThrow(HistoryDatabase.KEY_CATEGORY);
imageIndex = cursor.getColumnIndexOrThrow(HistoryDatabase.KEY_IMAGE);
}
#Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex)
{
Log.d(TAG, "setViewValue");
if (view instanceof TextView)
{
Log.d(TAG, "TextView");
TextView tv = (TextView) view;
if (columnIndex == timeIndex)
{
Log.d(TAG, "timeIndex");
d.setTime(cursor.getLong(columnIndex));
tv.setText(timeFormat.format(d) + " " + dateFormat.format(d));
return true;
}
else if (columnIndex == categoryIndex)
{
Log.d(TAG, "categoryIndex");
tv.setText(cursor.getString(columnIndex));
return true;
}
}
else if (view instanceof ImageView)
{
Log.d(TAG, "ImageView");
ImageView iv = (ImageView) view;
if (columnIndex == imageIndex)
{
Log.d(TAG, "imageIndex");
byte[] image = cursor.getBlob(columnIndex);
Bitmap bitmapImage = BitmapFactory.decodeByteArray(image, 0, image.length);
iv.setImageBitmap(bitmapImage);
return true;
}
}
return false;
}
}
The problem is that each image is decoded at the moment the view is prepared for showing. The ListView will recycle your views that means that at the moment a view leaves the screen it will be reused and therefore the image will be overwritten and garbage collected. If the item reenters the screen the image has to be decoded from the database again.
The decoding is reasonable fast but if the user changes the position in the list very quickly all the decoding calls will make your list very laggy.
I would implment something like an ImageCache. A class that holds a Map with WeakReferences to images. Everytime you want to display an image you take a look if the image is allready in the map and if the WeakReference still points to an object, if this is not the case you need to decode the image and then store it in the map.
Take a look at the lazy loading questions, these will show you how to put the decoding in a background task and then update the list the moment the images are loaded. It is a bit more effort but it can make the list much much more faster. If you are going to use the code example from the lazy loading questions try to use AsyncTasks instead of Threads to do the decoding in.
Here is sth, what you should read, and it should help you to solve problem (whole Displaying Bitmaps Efficiently part):
http://developer.android.com/training/displaying-bitmaps/process-bitmap.html
If your images are not the proper size, they are being scaled on the fly, which is not a cheap operation, particularly for large images. Be sure that you are loading thumbnails, not the wallpaper, out of your database.
Also, you can use Traceview to find out where your time is being spent.

Categories

Resources