Gallery Activity:
==================
TextView (tv)
------------------
| tv | tv | tv |
------------------
GalleryFragment: dynamically created; replaces FrameLayout
FrameLayout: W&H: match_parent
imageView: W&H are match_parent; scaleType: fitCenter;
layout_below: the tv's above;
layout_above: the TextView below;
result: imageView fits snuggly between.
Parent: RelativeLayout W&H: match_parent
------------------
TextView
==================
Gallery Activity description: This activity shows images, one at a time, belonging to a particular collection. User clicks image: new fragment shows next image.
I have been having several days worth of problems figuring out how to:
- A. get the size of the imageView so that I can properly scale the bitmap to fit.
- B. Now I've got an infinite loop happening. And I think I know why, I mean logically the moment I stuff a bitmap into the imageView that probably fires off the
onPreDraw()
again and then my code is off to the races. ...but I don't know how to fix it. - C. I thought
synchronization
might be the answer but I have never used it before and I don't think I'm using it correctly. - D. I thought maybe putting an
if(workerThread == null)
check might work ... but it only slowed it down for a brief moment.
At this point I am lost. I don't know how to obtain the dimensions of an ImageView tucked inside a dynamically added fragment while using those dimensions to down-size a bitmap and then after resizing loading that bitmap into the fragment's ImageView.
If you would like any clarifications please ask.
Included below is the code for GalleryFragment's onCreateView and BitmapWorkerTask. Then below that is a paste of my filtered logcat.
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// inflate view
View view = inflater.inflate(R.layout.frag_gallery_image, container, false);
// get view handles
imageView = (ImageView)view.findViewById(R.id.gallery_image);
// TESTING
ViewTreeObserver vto = imageView.getViewTreeObserver();
vto.addOnPreDrawListener(new OnPreDrawListener() {
@Override
public boolean onPreDraw() {
// TESTING
viewWidth = imageView.getMeasuredWidth();
viewHeight = imageView.getMeasuredHeight();
Log.d(TAG, SCOPE +"onPreDraw viewWidth: " +viewWidth +", viewHeight: " +viewHeight);
// TESTING: Added because "synchronization" attempt didn't work
if(bmwt == null){
loadImage(viewWidth, viewHeight);
}
return true;
}
});
// set view actions
imageView.setOnClickListener(GalleryFragment.this);
return view;
}
/**
* A synchronized method for loading bitmaps in background thread.
* Synchronization: http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html
* ...something isn't working as intended...
*/
private synchronized void loadImage(int viewWidth, int viewHeight){
// TESTING
Log.d(TAG, SCOPE +"loadImage viewWidth: " +viewWidth +", viewHeight: " +viewHeight);
bmwt = new BitmapWorkerTask(imageView, viewWidth, viewHeight);
bmwt.execute(imageUri);
}
/**
* BitmapWorkerTask is a subclass of Asynctask for the purpose of loading
* images off of the UI thread.
*/
private class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap>{
private String uri;
private int imageViewWidth;
private int imageViewHeight;
// Constructor.
public BitmapWorkerTask(ImageView imageView, int width, int height){
imageViewWidth = width;
imageViewHeight = height;
}
// Decode image in background.
@Override
protected Bitmap doInBackground(String... params) {
uri = params[0];
final Bitmap bitmap = ImageUtils.decodeSampledBitmapFromUri(uri, imageViewWidth, imageViewHeight);
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (isCancelled()) {
bitmap = null; // use some default bitmap
}
if (bitmap != null) {
Log.d(TAG, SCOPE +"bitmap kilobyte count: "+ bitmap.getByteCount() / 1024);
imageView.setImageBitmap(bitmap);
// make BitmapWorkerTask reference null again.
bmwt = null;
}// else do nothing.
}
}
Logcat. Note "bitmap kilobyte count" is called in asyncTask's onPostExecute. ...and is kind of where I had hoped the logging would stop.
06-13 01:50:41.442: D/ROSS(12982): GalleryFragment: imageUri: /mnt/sdcard/So so so beautiful.jpg
06-13 01:50:41.522: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:41.522: D/ROSS(12982): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 01:50:41.632: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:41.827: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:43.053: D/ROSS(12982): GalleryFragment: bitmap kilobyte count: 1183
06-13 01:50:43.142: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:43.142: D/ROSS(12982): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 01:50:43.632: D/ROSS(12982): GalleryFragment: bitmap kilobyte count: 1183
06-13 01:50:43.713: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:43.713: D/ROSS(12982): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 01:50:44.023: D/ROSS(12982): GalleryFragment: bitmap kilobyte count: 1183
06-13 01:50:44.101: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:44.101: D/ROSS(12982): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 01:50:44.332: D/ROSS(12982): GalleryFragment: bitmap kilobyte count: 1183
06-13 01:50:44.414: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:44.414: D/ROSS(12982): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 01:50:44.681: D/ROSS(12982): GalleryFragment: bitmap kilobyte count: 1183
06-13 01:50:44.761: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:44.761: D/ROSS(12982): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 01:50:45.151: D/ROSS(12982): GalleryFragment: bitmap kilobyte count: 1183
06-13 01:50:45.227: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:45.231: D/ROSS(12982): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 01:50:45.521: D/ROSS(12982): GalleryFragment: bitmap kilobyte count: 1183
06-13 01:50:45.593: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:45.593: D/ROSS(12982): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 01:50:45.962: D/ROSS(12982): GalleryFragment: bitmap kilobyte count: 1183
EDIT:
After implementing Nicholas' suggestion here's the logcat in total:
06-13 14:51:44.976: D/ROSS(15465): GalleryFragment: imageUri: /mnt/sdcard/RossAndClay - Copy (12).JPG
06-13 14:51:45.146: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:51:45.146: D/ROSS(15465): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 14:51:45.376: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:51:45.422: D/ROSS(15465): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 14:51:45.626: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:51:45.626: D/ROSS(15465): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 14:51:49.336: D/ROSS(15465): GalleryFragment: bitmap kilobyte count: 1183
06-13 14:51:49.356: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:51:51.327: D/ROSS(15465): GalleryFragment: bitmap kilobyte count: 1183
06-13 14:51:51.336: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:51:53.395: D/ROSS(15465): GalleryFragment: bitmap kilobyte count: 1183
06-13 14:51:53.469: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
And after clicking the image, in the fragment, this is the logcat in total for that event:
06-13 14:54:41.315: D/ROSS(15465): GalleryFragment: imageUri: /mnt/sdcard/RossAndClay - Copy (11).JPG
06-13 14:54:41.402: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:54:41.406: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:54:41.406: D/ROSS(15465): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 14:54:41.655: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:54:41.665: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:54:41.665: D/ROSS(15465): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 14:54:44.285: D/ROSS(15465): GalleryFragment: bitmap kilobyte count: 1183
06-13 14:54:44.305: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:54:44.305: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:54:46.965: D/ROSS(15465): GalleryFragment: bitmap kilobyte count: 1183
06-13 14:54:47.036: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:54:47.036: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
Every time I click an image the number of entries in logcat grows longer and longer. My fragment transactions look like:
/*
* Create the fragment that holds the collection image.
* @param imageUri
*/
private void createImageFragment(String imageUri) {
// With each click wipe previous entry, ie: there's no going back.
getFragmentManager().popBackStack();
// create new fragment
GalleryFragment galleryFrag = GalleryFragment.newInstance(imageUri);
// programmatically add new fragment
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.gallery_imageFrame, galleryFrag, GALLERY_FRAG);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.commit();
}
The moment I wrote that here I tried changing popBackStack()
to popBackStackImmediate()
and that stopped the ever growing list of logcat entries. Now, after having done some testing loading various image sizes, it appears like however large the image is determines how many times loadImage() is called, which makes sense (though I'd like to stop that from happening) because (for instance) three asyntasks are being fired off before the imageView's image is actually set. So now the task is to figure out how to ensure loadImage()
is called only once.
Edit 2:
Sometimes a particular problem can freeze dry our brains. Solved the multiple calls to the method simply with class field private boolean thisMethodCalled = false;
and in onPreDraw:
if(!thisMethodCalled){
loadImage(viewWidth, viewHeight);
thisMethodCalled = true;
}
...though this doesn't stop onPreDraw from being called an increasing number of times with each fragment replacement, but not sure I can do anything about that.
Final Edit - the best solution:
With notion gleaned from one of the comments in this answer, just remove the listener near the end with imageView.getViewTreeObserver().removeOnPreDrawListener(this);
, with no listener, no method being called multiple times; and obviously no additional onPreDraw() calls. Finally, working exactly as I intended it to.
ViewTreeObserver vto = imageView.getViewTreeObserver();
vto.addOnPreDrawListener(new OnPreDrawListener() {
@Override
public boolean onPreDraw() {
// TESTING
viewWidth = imageView.getMeasuredWidth();
viewHeight = imageView.getMeasuredHeight();
loadImage(viewWidth, viewHeight);
imageView.getViewTreeObserver().removeOnPreDrawListener(this);
return true;
}
});