B4A Library [Lib+Class] CustomGallery

CustomGallery is a very small library (BitmapPlus) and a class (ClsCustomGallery) for B4A v2.0.
Its goal is to display thumbnails in a row, column or grid. It's highly customizable as you can see in the screenshots.

It needs two other libraries: ScrollView2D and Reflection

Tutorial: How they do #2

img1.jpg img2.jpg img3.jpg img4.jpg img5.jpg
List of functions:
Initialize(Parent As Object, Left As Int, Top As Int, Width As Int, Height As Int, GalleryStyle As Int, Callback As Object, subTouch As String, subClick As String, subLongClick As String, subScroll As String)
ClearGallery
RemoveColoredBorders
(bmpToCrop As Bitmap, Color As Int) As Bitmap
NumberOfThumbnails As Int
ProcessBitmap(SrcBmp As Bitmap) As Bitmap
AddThumbnail(SrcBmp As Bitmap, Tag As Object)
AddFolder(Dir As String, MaxNbFiles As Int)
InsertThumbnailAt(SrcBmp As Bitmap, Position As Int, Tag As Object)
InsertOnTop(SrcBmp As Bitmap, Position As Int, Tag As Object)
InsertFileAt(Dir As String, FileName As String, InsertPosition As Int, Tag As Object)
MoveThumbnail(FromPos As Int, ToPos As Int)
RemoveThumbnailAt(Position As Int)
RemoveThumbnailWithTag(Tag As Object)
GetThumbnailAt(Position As Int) As Panel
GetThumbnailWithTag(Tag As Object) As Panel
JumpTo(Position As Int)
SetLabelInGrid(Thumbnail As Panel, LabelText As String, LabelColor As Int, LabelSize As Int)
LoadDrawable(Name As String) As Object
AddDrawableTo(Thumbnail As Panel, Drawable As Object)

List of options:
BorderColor As Int
BorderSize As Int
ForceColorReduction As Boolean: if true, reduces the bitmap colors and removes the alpha channel to save memory
MinTiltAngle As Int
MaxTiltAngle As Int
AlternateAngle As Boolean: if true, tilt angles are alternately < 0 and > 0
HorizontalOffset As Int: shifts the thumbnails to the right (+) or to the left (-)
VerticalOffset As Int: shifts the thumbnails to the bottom (+) or to the top (-)
PressedDrawable As Object: drawable displayed when a thumbnail is clicked
RescaleOnlyIfBigger As Boolean: if true, the rescaling function is called only if the source image is bigger than the thumbnail
LabelHeightInGrid As Int: height of labels below thumbnails
SizeInGrid As Int: size of thumbnails inside a grid
SizeInPile As Int: size of thumbnails with the pile style
SpaceBetweenThumbnails As Int
StillVisible As Int: if there's an offset, defines the minimum visible size of thumbnails

v1.1: see details here
v1.11: Fixed three minor bugs
v1.2: see details here
v1.3: Fixed a bug and added the ForceColorReduction option
v1.31: Compatible with obfuscation and JellyBean
v1.32: No more warnings with B4A v2.7.
v1.321: Minor change. I added two entries to the Manifest of examples to enable the hardware acceleration and a large heap.

If you want to download the library and the class, you need only the file CustomGallery#1.zip. If you want to run the full demo, download the five files. After unzipping, you should have nineteen images (18 jpg + 1 png) in the Files folder of the demo.

Another demo (with the MediaBrowser library) is available here.

Fred
 

Attachments

  • CustomGallery#2.zip
    362.3 KB · Views: 1,417
  • CustomGallery#3.zip
    375.5 KB · Views: 1,256
  • CustomGallery#4.zip
    296.8 KB · Views: 1,273
  • CustomGallery#5.zip
    331.9 KB · Views: 1,717
  • CustomGallery#1 v1.321.zip
    283.2 KB · Views: 1,526
Last edited:

Informatix

Expert
Licensed User
Longtime User
New version

While I was fixing a bug, I realized it would be fairly easy to add some features and remove the restrictions on grid styles. So, here's a new version (v1.1):
- Fixed two bugs with the pile style (now, inner panels larger than the visible area are properly handled)
- Fixed a bug in RescaleBmp (images smaller than thumbnails are no longer rescaled)
- Added two options: BorderColor & BorderSize with anti-aliasing
- Added the option SizeInPile
- PressedDrawable becomes public and can be changed
- Added functions AddDrawableTo and LoadDrawable
- InsertFolderAt becomes AddFolder(Dir As String, MaxNbFiles As Int)
- Added the function InsertOnTop
- Removed the restrictions on grid styles in some functions
- Improved the three demos and added a new one

Et voilà !
Fred
 

Informatix

Expert
Licensed User
Longtime User
With a gallery, you will face two problems sooner or later:
- Exception "Out of memory"
- Loading time

There are different methods to get rid of them:
1) The easy way:
Use the MediaBrowser library to get access to the thumbnails created by the system for each image on the device. These thumbnails exist in two sizes (micro and mini). The micro thumbnail is suited for a squared thumbnail. The mini thumbnail will be used in the other cases. With these thumbnails, you reduce the loading time, especially for large images. However, if you want to load 300 thumbnails in your gallery, this will take a noticeable time even on a fast device. Since it's difficult to get better loading times, you should use DoEvents during the loading to allow the user to do something.
If your pictures come from a database or a website, try to load a scaled down version of them. It's not worth loading a 1024x768 pixel image if you're going to display a 128x96 pixel thumbnail.
To minimize the risk of an "Out of memory", you can split the thumbnails into groups. You can group them by folder, by date or by quantity (e.g. a group of 100 thumbnails).
This easy method is implemented in the joined example (this example needs the MediaBrowser library).

2) The less easy way:
In some situations, you don't want to use or you can't use DoEvents, thus you have to rely upon background threads. There's a threading library available for B4A here.
When the thumbnails are loaded in the background, the user may not notice that a work is in a progress. The most obvious way to make it noticeable is to create empty (colored) thumbnails to fill the ScrollView and replace them each time a background thread loads a picture. The user can see how many thumbnails are pending.
Some graphic libraries allow you to reduce the quality of a picture (e.g. its number of colors) to lower its memory consumption. Some of them provide faster functions to draw or rotate a picture. Feel free to replace my library "BitmapPlus" by a more efficient library. EDIT: in version 1.2, the functions have been optimized, so another library won't help a lot.
Loading thousands of thumbnails without hitting an "Out of memory" is possible, but not easy. Since the user can only see a few of them at a time, why keeping in memory the other thumbnails ? The idea is simple: you load only the visible thumbnails and, when they are no longer visible, you replace them by empty thumbnails. You have to preload some thumbnails located before and after the visible area to ensure a smooth scroll. This way, the loading time is reduced (if the user is not interested by the last thumbnails of your gallery, they will never be loaded) and the memory consumption is greatly reduced. You may have problems if you allow the user to jump quickly from a position to another in the gallery (the pictures are not loaded yet).

Read here what Google says on the subject.
 

Attachments

  • CustomGallery+MediaBrowser.zip
    15.4 KB · Views: 742
Last edited:

Informatix

Expert
Licensed User
Longtime User
New version (v1.2):

Two new options:
RescaleOnlyIfBigger As Boolean: if true, the rescaling function is called only if the source image is bigger than the thumbnail
LabelHeightInGrid As Int: height of labels below thumbnails

Two new functions:
SetLabelInGrid: modify the label below the thumbnail in a grid
ProcessBitmap: creates the bitmap to display but does not paint it on a thumbnail (you can use it in your own views)

The memory consumption has been greatly reduced with color optimization (ReduceColor function in the BitmapPlus library).

Rotations are now 2 times faster due to a new algorithm using matrix (Rotate function in the BitmapPlus library).

The internal layout of the thumbnail has been changed. The image is now stored in an imageview.
 

hvkaniti

Member
Licensed User
Longtime User
Hello Informatix, Thanks a million for all of your great libraries. I still surprise how you guys Erel and yourself manage your time and develop these great products for us. I have a question for you...can I use these libraries in my applications for my clients or do I need any license for commercial use. I am sorry if it is a stupid question, but honestly I have no idea about usage restrictions. Can you please let me know? Thank you again.
 
Last edited:

Informatix

Expert
Licensed User
Longtime User
My classes and libraries are free for any kind of use, including commercial use on a large scale. If you want to reward me, just provide a link to my creations on your "about" screen.
I don't spend as much time as you think behind my screen. I work fast (too maybe, sometimes).
 

Informatix

Expert
Licensed User
Longtime User
Version 1.3

New version:

The color reduction didn't work as expected. That works well now, but it's disabled by default. You have to set the new option ForceColorReduction to True to enable it.

The angle of a tilted image is now stored in the Tag of the ImageView inside the thumbnail. If you used a random angle, you can know its value now.

I added a bit of comments to BitmapPlus and 2 functions (for advanced users only):
Recycle
IsRecycled
 

bloxa69

Active Member
Licensed User
Longtime User
demos crashed when I compiled them obfuscated

Informatix,
thanks for your efforts. I hate to say it, but all demos crashed when I compiled them obfuscated. Probably, it changes the name of a call or variable that cannot be changed. When running it without obfuscation, it works fine. The crash happens when I tap on any icon in the scroller (all demos). I've tested it on Acer Tablet A500 - ICS 4.03, 800x1200 screen.

I/B4A ( 6418): java.lang.NullPointerException
I/B4A ( 6418): at anywheresoftware.b4a.agraham.reflection.Reflection$7.onTouch(Reflection.java:1100)
I/B4A ( 6418): at android.view.View.dispatchTouchEvent(View.java:5541)
I/B4A ( 6418): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1951)
I/B4A ( 6418): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1712)
I/B4A ( 6418): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957)
I/B4A ( 6418): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1684)
I/B4A ( 6418): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957)
I/B4A ( 6418): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1684)
I/B4A ( 6418): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957)
I/B4A ( 6418): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1684)
I/B4A ( 6418): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957)
I/B4A ( 6418): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1684)
I/B4A ( 6418): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957)
I/B4A ( 6418): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1684)
I/B4A ( 6418): at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:1957)
I/B4A ( 6418): at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1684)
I/B4A ( 6418): at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1938)
I/B4A ( 6418): at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1375)
I/B4A ( 6418): at android.app.Activity.dispatchTouchEvent(Activity.java:2364)
I/B4A ( 6418): at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1886)
I/B4A ( 6418): at android.view.View.dispatchPointerEvent(View.java:5726)
I/B4A ( 6418): at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:2890)
I/B4A ( 6418): at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2466)
I/B4A ( 6418): at android.view.ViewRootImpl.processInputEvents(ViewRootImpl.java:845)
I/B4A ( 6418): at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2475)
I/B4A ( 6418): at android.os.Handler.dispatchMessage(Handler.java:99)
I/B4A ( 6418): at android.os.Looper.loop(Looper.java:137)
I/B4A ( 6418): at android.app.ActivityThread.main(ActivityThread.java:4424)
I/B4A ( 6418): at java.lang.reflect.Method.invokeNative(Native Method)
I/B4A ( 6418): at java.lang.reflect.Method.invoke(Method.java:511)
I/B4A ( 6418): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
I/B4A ( 6418): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
I/B4A ( 6418): at dalvik.system.NativeStart.main(Native Method)
 

Informatix

Expert
Licensed User
Longtime User
Informatix,
thanks for your efforts. I hate to say it, but all demos crashed when I compiled them obfuscated. Probably, it changes the name of a call or variable that cannot be changed. When running it without obfuscation, it works fine. The crash happens when I tap on any icon in the scroller (all demos). I've tested it on Acer Tablet A500 - ICS 4.03, 800x1200 screen.

That's easy to fix. I will do it in a couple of days.
 

bloxa69

Active Member
Licensed User
Longtime User
Thanks Informatix, looking forward to it. I appreciate you guys sharing your work and efforts with others.
 

bloxa69

Active Member
Licensed User
Longtime User
Will try it ASAP

thanks a lot Informatix, will try it ASAP.:sign0098:
 

shashkiranr

Active Member
Licensed User
Longtime User
Hi Infromatrix,

I got this Out Of memory error when i tried to load the gallery with Images of very small size.

B4X:
clscustomgallery_squarebmp (B4A line: 409)
bmp = BmpPlus.CreateScaledBitmap(bmp, Round(FixedDimension / bmp.Height * bmp.Width), FixedDimension, True)
java.lang.OutOfMemoryError
    at android.graphics.Bitmap.nativeCreate(Native Method)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:640)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:586)
    at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:466)
    at flm.b4a.bitmapplus.BitmapPlus.createScaledBitmap(BitmapPlus.java:60)
    at com.bayalu.mankutimma.clscustomgallery._squarebmp(clscustomgallery.java:2694)
    at com.bayalu.mankutimma.clscustomgallery._addthumbnail(clscustomgallery.java:227)
    at com.bayalu.mankutimma.kagga._mankugallry_initialization(kagga.java:1140)
    at com.bayalu.mankutimma.kagga._lvmenu_itemclick(kagga.java:1071)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:169)
    at anywheresoftware.b4a.BA$2.run(BA.java:272)
    at android.os.Handler.handleCallback(Handler.java:615)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:4898)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1008)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:775)
    at dalvik.system.NativeStart.main(Native Method)
** Activity (main) Resume **
** Activity (main) Pause, UserClosed = false **
** Activity (main) Resume **
 

Informatix

Expert
Licensed User
Longtime User
That may happen when you have a lot of small bitmaps. The only solutions that I know are:
- using UltimateListView instead, but it's not free and not suited for horizontal galleries (only vertical);
- trying to display less bitmaps;
- using the library Native DirectBuffer but it's for experts only. You have to understand what it does and how it does it.
 

shashkiranr

Active Member
Licensed User
Longtime User
Thank you Informatix,

I realised my mistake i was using the below code to initialize

B4X:
MyGallery1.Initialize(Activity, 0, Top, 100%x, 100%y, 1, Me, "", "Gallery_OnClick", "", "")

when i changed it to 50%y i realised this is how i wanted it to look :)

Thank you so much :):)

Regards,
SK
 
Top