Lists all image media folders, using
getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
It's important to note that the Media external/internal URI has nothing to do with storage location, but actually refer to internal = private to the app, and external = public folders
- Has two instances of the layout/view_image_details.xml, along with a widget.Switch to en-/disable sync between the two
- Manages a list of selected images per folder (see "Data and file storage overview") ** Image selection is NOT a cache, as it cannot be rebuilt from other information
The main actions are
- Handle image swiping with a ViewPager, disallowing the index currently in use on the other ViewPager
- Sync zoom/pan to other view
- Manage image selection
- Provide actions to share selected images
The "pan and zoom listener" on a PhotoView is toggled in two cases:
- During loading of a new image
** Disabled when starting to load a new high-resolution image
** Re-enabled once in the onApplyZoomPanMatrix() implementation of the
ImageDetailViewImpl
- When applying a display matrix
** Disabled at the start of
PhotoViewMediator.copyPanAndZoom()
** Re-enabled in the finally block of above method
When a user changes pan/zoom on one of the images:
- The
SubsamplingScaleImageView
triggers the registeredStateChangedListener
; this will be aImageViewListener
- The
ImageViewListener
notifies all registeredImageViewEventListener
a. TheCrossViewEventHandler
sync's pan/zoom to the other image. There will be 2 instances, one sync'ing top to bottom, and one bottom to top. b. TheZoomPanRestoreHandler
remembers pan/zoom so those settings can be applied to the next image when swiping - The
CrossViewEventHandler
callsPhotoViewMediator.onPanOrZoomChanged()
- The
PhotoViewMediator
checks if synchronization is currently active or not. a. If not active, thePhotoViewMediator
updates its internal offsets b. If active, thePhotoViewMediator
maps the pan/zoom to the otherImageDetailView
ImageDetailView
receives asetPanAndZoomState()
with the new settings
Relevant points to be aware of:
- SubsamplingScaleImageView does not enfore maxScale when using setScaleAndCenter(), but does so on user interaction
The main actions are
- Remove images from selection
- Share images
The only non-Android library used are Glide and Subsampling Scale Image View.
Based on 06. State of the documentation, view state synchronization is based on OnImageEventListener (basic image readiness) and OnStateChangedListener (pan and zoom). The class
ImageViewListener implements SubsamplingScaleImageView.OnImageEventListener, SubsamplingScaleImageView.OnStateChangedListener
wraps these two, and passes the events to our own ImageViewEventListener
, so that multiple listeners can react to event.
SubsamplingScaleImageView
sets a GestureDetector
internally, which runs if panEnabled
is true. The resulting pan has ORIGIN_FLING
,
thus it's important that ImageViewListener
does not restrict event handling on "origin".
According to this github issue,
If you want to zoom an image you need to tell Glide to load a bigger image, otherwise it'll be blurry: .override(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL).
However the ViewPager will load 1 off-screen view per side, ie with a top/bottom view that 6 "original size" images in memory. An image from a EOS 77D is 7-9MB, ie for images alone a heap of around 50MB is required. When scrolling, garbage collection of destroyed views might be a bit delayed, thus increasing required heap size yet even more.
According to this stackoverflow, the standard heap size on an Android device is in the area of 16-24 MB, while android:largeHeap="true" increases that to 48-128MB.