B4A Library TouchImageView

TouchImageView is an updated version of ImageView.
It adds support for touch events - drag and pinch zoom.

On devices with an Android version older than Eclair 2.0 and on devices without multitouch support only drag will be supported.
The same also applies when using the emulator.


Reference

Events:
TouchImageView generates no events.
Note however that the Click and LongClick events generated by the standard ImageView WILL be generated if the TouchImageView TouchEnabled property is False.
If TouchEnabled is set to True then the standard Click and LongClick events will NOT be generated.

Methods and properties:

CreateLog

Creates log entries with details about the current state of the TouchImageView, for debugging purposes:

** TouchImageView.CreateLog **
Original image: width = 962, height = 962
Scaled image: width = 294, height = 294
Translate position: x = 172, y = 346
ScaleX = 0.30614918, ScaleY = 0.30614918

MinScale As Float

Get or set the minimum scale that the image can be reduced by (zoomed out).
Default MinScale is 0.5.

B4X:
TouchImageView1.MinScale = 0.25

MaxScale As Float

Get or set the maximum scale that the image can be enlarged by (zoomed in).
Default MaxScale is 2.5.

B4X:
TouchImageView1.MaxScale = 3.75

GetScaleRect As anywheresoftware.b4a.objects.drawable.CanvasWrapper.RectWrapper

Returns a Rect that describes the current scale and position of the image.
You can get use this ScaleRect to restore a TouchImageView state on orientation change.

B4X:
Dim ScaleRect As Rect
ScaleRect = TouchImageView1.GetScaleRect

SetScaleRect (Rect as android.graphics.Rect, String as ScaleToFit)

Scale and position the image to the area of the TouchImageView defined by ScaleRect.
ScaleToFit controls how the image should be aligned in the Rect:

"CENTER" - Center and maximise the image to fit the Rect maintaining the aspect ratio.
"END" - Maximise the image to fit the Rect maintaining the aspect ratio, align the image with the bottom and right edges of the Rect.
"FILL" - Maximise the image to completely fill the Rect, the aspect ratio may not be maintained.
"START" - Maximise the image to fit the Rect maintaining the aspect ratio, align the image with the top and left edges of the Rect.

Note that if as a result of fitting the image to the Rect, the image has been scaled less than the current MinScale value then MinScale will be set to the current scale.
Likewise if when fitting the image to the Rect, the image has been scaled more than the current MaxScale value then MaxScale will be set to the current scale.

Documentation for the B4A Rect can be found here: Basic4android - Drawing (Core)
Documentation for Android ScaleToFit can be found here: Matrix.ScaleToFit

Example to position and scale the image so that it centers within a rectangle Left 20dip, Top 30dip, Right 100dip, Bottom 120dip, maximised but maintains it's aspect ratio:

B4X:
Dim ScaleRect As Rect
ScaleRect.Initialize(20dip, 30dip, 100dip, 120dip)
TouchImageView1.SetScaleRect(ScaleRect, "CENTER")

TouchEnabled as Boolean

Get or set whether touch events are currently enabled in the TouchImageView.

If touch events are enabled then the TouchImageView will NOT generate the standard ImageView Click and LongClick events.
These standard ImageView events will however be generated if touch events are NOT enabled.

TranslatePadding As Int

Get or set the minimum number of pixels that the image will always display within the TouchImageView.
Default TranslatePadding is 10 pixels - the image can not be dragged out of visibility, at least 10 pixels of width and/or height will always be visible.

B4X:
TouchImageView1.TranslatePadding = 50

I couldn't decide whether to implement setter and getter methods for the translate position and zoom scale - methods that would enable you to programmatically pan and zoom the image.
If anyone wants such methods then make a post in this thread and i'll see what i can do.


All other methods and properties of TouchImageView are inherited from the standard ImageView.
Documentation for the standard ImageView can be found here: Basic4android - Views (Core)

The attached demo shows basic usage and how to save and restore the TouchImageView state on orientation change.

TouchImageView updated to version 2.00, version 2.00 is not compatible with previous versions.
Methods and properties have changed.
Click here to read more


Martin.
 

Attachments

  • TouchImageView_v1_1.zip
    177.8 KB · Views: 2,109
  • TouchImageView_v_2_00.zip
    200.9 KB · Views: 4,503
Last edited:

Smee

Well-Known Member
Licensed User
Longtime User
Hi sorry for not replying sooner - been busy with lots of programming.

I keep posting in this thread that i'll have an update released at some point but end up sidetracked with other code and haven't got around to the new TouchImageView yet.

I'll make a note on my calendar not to forget about the update and spend some time getting it online this week.

Martin.

Note to self;

Self dont forget.:D

The request for rotation is no longer needed
i just use the cnv.DrawBitmapRotated command

Cheers
 

psciga

Active Member
Licensed User
Longtime User
Hello Martin,

sorry for that postig, but in this way, I want renew my question:

Hi Martin,

thanks a lot for this great lib. Have you any idea how to connect the image coordinates with a database? What I mean is, to get the touch (or click) position and search objekts near this point in a database.

All further hints would be appreciated

Regards - Peter

Sorry for this silly question, I'm a newbie.

Regards - Peter
 

BarrySumpter

Active Member
Licensed User
Longtime User
Hello Martin,
sorry for that postig, but in this way, I want renew my question:
Regards - Peter

Hi Peter,

I'm thinking the db of coordinates and objects nearby is more of a mapping app.

Have a look at the Google Maps API samples.
Somewhere in those samples is a search for business or tourist sights etc.
One had a view of all kinds of photographs poping up all over it.
I'm thinking the DB you want to search is already provided by Google Maps.

Google Maps Javascript API V3 Examples
https://developers.google.com/maps/documentation/javascript/examples/

Select which would be best suited for your query.

Then have a look at:
osmdroid-mapview-b4a-tutorial
http://www.b4x.com/forum/basic4andr...-osmdroid-mapview-b4a-tutorial.html#post92644

and

gps-example
http://www.b4x.com/forum/basic4android-share-your-creations/13345-gps-example.html#post75209


hth
 
Last edited:

psciga

Active Member
Licensed User
Longtime User
Hi Barry,

thanks for your response.

I'm thinking the db of coordinates and objects nearby is more of a mapping app.

You're right! I know the Google Maps API and tested also the osmdroid example. It's a great solution. However I found no way to use my own maps. These are PNG's with with fix dimensions and known coordiates (top, left, bottom, right) in the reality.

I'm thinking the DB you want to search is already provided by Google Maps.

No, I'm not using Google Maps or OSM. My spatial data are stored in MySQL. For the future is planed to replace MySQL with Sybase SQL Anywhere. But that's not the point.

At the moment I use an ImageView to show a map and a transparent Panel to catch the touch events. In this way I can move the ImageView. But how to zoom it?

My current solution for zooming is, to load a more or less detailed pic from the server. These communication needs a lot of time. No problem at home using WLAN, a big problem outdoor with low transfer rates.

That was the reason to try TouchImgaeView. It's really great, particularly with regard to the image quality. But I don't know how to get the picture coordinates.

For better understanding ... I don't want to display a whole town or so in one picture, but 200 x 200 meters for an overview (a typical situation). To see details (e.g. small symbols or dimensions) I want to zoom in without loading a new picture. This is possible with TouchImageView. And to select data from database, I want pick a position in the image. I know, how to recalculate the real (spatial) position and how to select values from the DB nearby. But not, how to get the position on the image.

Any ideas?

Thanks - Peter
 

robh

Member
Licensed User
Longtime User
Default Zoom Level

Great library, exactly what I was looking for, thanks for the work you have put into this.

Just a quick question, I've got my images working with min scale being calcualted at runtime, everything working as expected howevber when I view my image, the default display seems to jump to the max scale. Is there anyway to open the image so that its initially display at the min scale?

Many thanks in advance.
 

warwound

Expert
Licensed User
Longtime User
As i've posted before i have a new version of TouchImageView which bar a few changes will have new methods and properties to keep you all happy...

But i have to dedicate my time to work which pays rather than my hobby B4A stuff so a deadline for finishing and uploading the new version is technically 'undefined' LOL.

I'll make an effort though - i've written most of the base code and it just needs a few updates before i can upload it.

Martin.
 

warwound

Expert
Licensed User
Longtime User
Hi all.

I've been busy and have a new version more or less ready.

I've spent a while trying to fix the new Click and LongClick events.
No matter what i did the coordinates of the click were just incorrect.

Anyway that was testing on my ZTE Blade - high density screen means Android has automatically upscaled my Bitmap so that accounts for the wrong coordinate click position.

Testing on a medium density emulator shows the correct coords but only when the Bitmap is not zoomed in or out.

Anyway i have that to fix but wanted to ask you all...

When you load a Bitmap into TouchImageView do you want Android to automatically scale that Bitmap size depending on the device screen density?

Have a read of the documentation here: Supporting Multiple Screens | Android Developers

If you have a JPG image and the user of your app has a high density device, auto-upscaling of that JPG could well display a pixelated image.
It all depends on the quality of the original image to be displayed.

Another thing is logic - let's your screen is 480 x 800 pixels and you display an image that is 480 x 240.

Would you logically expect it to fit the screen width, regardless of the device density?

I'm thinking i'd code the new TouchImageView to NOT scale any image that it displays.

But that goes against the Android default behaviour so i want some input from the members that are following this thread before i decide what to do.

Martin.
 

Roger Garstang

Well-Known Member
Licensed User
Longtime User
Not sure if I like auto scaling images. Usually I make my images the size needed myself. I often have an image large enough to display on High DPI deices and more, but use LoadBitmapSample to save memory. With something like a Touch Imageview or anything that can be zoomed I'd probably use a high quality image anyway. If each stage of the zoom does something like LoadBitmapSample to conserve memory I'm fine with that, but I'd think that would zoom slowly. I wouldn't want it upscaling a small image though since that would look blocky and ugly.
 

warwound

Expert
Licensed User
Longtime User
Ok two responses so far and they both agree with me - we don't want TouchImageView to auto scale an image based on device screen density.

I'll see what other responses get posted then get the new version complete and uploaded.

Martin.
 

BarrySumpter

Active Member
Licensed User
Longtime User
I think I recall in VB we could tell it to stretch (to size) whether smaller or larger.
i.e. it wouldn't stretch until I told it to.

If it's not too hard how about myImage.Stretch = True/False?
 
Last edited:

warwound

Expert
Licensed User
Longtime User
I think I recall in VB we could tell it to stretch (to size).
i.e. it woldn't stretch until I told it to.

If it's not too hard how about myImage.Stretch?

Android by default will rescale a Bitmap based on device screen density.

Are you suggesting the new TouchImageView has an option to enable/disable auto scaling when adding a Bitmap?

That might be the best solution.

Martin.
 

BarrySumpter

Active Member
Licensed User
Longtime User
Oh! Well, if its the best then yes its my suggestion and my idea and I take full credit.

;)

LOL

I wish.

No, not my idea so I can't take credit for it.



I'm just posting what I think MS vb6 did.

Why have one way or the other when we can have an option for both.

If it is automaticly on then have a flag for off.
If it is automaticly off then have to flag for on.

If its possible.
May to have re/un calculate to undo an automatic calculation.
Then back again.
Outta my league.

Love watchin you work man. :D
 
Last edited:

warwound

Expert
Licensed User
Longtime User
TouchImageView updated to version 2.00

At last i managed to get TouchImageView updated and here it is!

I decided to prevent the default Android auto-scaling depending on the device screen density.
TouchImageView has methods that enable you to scale an image so it's up to you if you want to do so.

TouchImageView now raises two events:

Click (X As Int, Y As Int)

LongClick (X As Int, Y As Int)

X and Y being the coordinates on the image that has been clicked or long-clicked, not the coordinates of the TouchImageView display.

Version 2.00 of TouchImageView is not backwards compatible with previous versions.

The code is a complete re-write.
This is not an extended B4A ImageView but an entirely new View i have created.

  • TouchImageView
    Events:
    • Click(X As Int, Y As Int)
    • LongClick(X As Int, Y As Int)
    Properties:
    • Background As Drawable
    • BackgroundImage As Bitmap
      Get or Set the BackgroundImage.
    • ClickPeriod As Int
      Get or Set the ClickPeriod in milliseconds.
      The ClickPeriod is the threshold between a click being treated as a Click or a LongClick.
      The default ClickPeriod is 200 milliseconds.
    • Color As
    • Enabled As Boolean
    • Gravity As Int
      Get or Set the Gravity of the BackgroundImage.
    • Height As Int
    • Left As Int
    • MaxScale As Float
      Get or set the maximum scale that the image can be enlarged by (zoomed in).
      Default MaxScale is 1.5.
    • MinScale As Float
      Get or set the minimum scale that the image can be reduced by (zoomed out).
      Default MinScale is 0.5.
    • ScaleX As Float
      Get the factor which the image is currently scaled by on it's X axis.
    • ScaleY As Float
      Get the factor which the image is currently scaled by on it's Y axis.
    • Tag As Object
    • Top As Int
    • TouchEnabled As Boolean
      Get or Set whether touch events are enabled.
    • TranslatePadding As Int
      Get or set the minimum number of pixels that the image will always display within the TouchImageView.
      Default TranslatePadding is 64 pixels.
      The image can not be dragged out of visibility, at least 64 pixels of width and height will always be visible.
    • Visible As Boolean
    • Width As Int
    Methods:
    • BringToFront
    • CreateLog
      Creates log entries with details about the current state of the TouchImageView.
    • GetDestRect As RectWrapper
      Returns a Rect that describes the current scale and position of the image within the TouchImageView.
    • Initialize (EventName As String)
    • Invalidate
    • Invalidate2 (arg0 As Rect)
    • Invalidate3 (arg0 As Int, arg1 As Int, arg2 As Int, arg3 As Int)
    • IsInitialized As Boolean
    • PreScale (Scale As Float, PointX As Int, PointY As Int)
      Change the current image scale by Scale, centering the change on (X, Y).
      For example a Scale value of 1.5 will increase the current scale by 150%.
    • RemoveView
    • RequestFocus As Boolean
    • ScaleSrcRectToDestRect (SrcRect As Rect, DestRect As Rect, ScaleToFit As String)
      Scale and position the area of the (foreground) image defined by SrcRect to the area of the TouchImageView defined by DestRect.
      ScaleToFit controls how the image should be aligned in the DestRect:
      CENTER - Center and maximise the image to fit the DestRect maintaining the aspect ratio.
      END - Maximise the image to fit the DestRect maintaining the aspect ratio, align the image with the bottom and right edges of the DestRect.
      FILL - Maximise the image to completely fill the DestRect, the aspect ratio may not be maintained.
      START - Maximise the image to fit the DestRect maintaining the aspect ratio, align the image with the top and left edges of the DestRect.
    • SendToBack
    • SetBackgroundImage (arg0 As Bitmap)
    • SetBitmap (aBitmap As Bitmap)
      Set the (foreground) image.
    • SetLayout (arg0 As Int, arg1 As Int, arg2 As Int, arg3 As Int)
    • buildNativeView (arg0 As Context, arg1 As Class, arg2 As HashMap, arg3 As Boolean) As T

Here's the sample code:

B4X:
'Activity module
Sub Process_Globals
   Dim SourceImageRect, TouchImageViewRect As Rect
End Sub

Sub Globals
   Dim TouchImageView1 As TouchImageView
End Sub

Sub Activity_Create(FirstTime As Boolean)
   Activity.AddMenuItem("Create Log", "CreateLog")
   
   TouchImageView1.Initialize("TouchImageView1")
   Activity.AddView(TouchImageView1, 0, 0, 100%x, 100%y)
   
   TouchImageView1.MinScale=0.25         '   default is 0.5
   TouchImageView1.MaxScale=2            '   default is 1.5
   TouchImageView1.TranslatePadding=128dip   '   default is 64dip
   
   TouchImageView1.Gravity=Gravity.FILL
   TouchImageView1.SetBackgroundImage(LoadBitmap(File.DirAssets, "b4a_logo.png"))
   
   Dim Bitmap1 As Bitmap
   Bitmap1.Initialize(File.DirAssets, "cromer_pier.jpg")
   TouchImageView1.SetBitmap(Bitmap1)
   
   If FirstTime Then
      '   Rect parameters are: left, top, right, bottom
      SourceImageRect.Initialize(0, 0, Bitmap1.Width, Bitmap1.Height)
      TouchImageViewRect.Initialize(0, 0, TouchImageView1.Width, TouchImageView1.Height)
   End If
   
   '   if the scaling process scales the image to less than the current MinScale or more than the current MaxScale then MinScale or MaxScale will be adjusted
   TouchImageView1.ScaleSrcRectToDestRect(SourceImageRect, TouchImageViewRect, "CENTER")   '   make string a static constant in library
   
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)
   TouchImageViewRect=TouchImageView1.GetDestRect
End Sub

Sub CreateLog_Click
   TouchImageView1.CreateLog
End Sub

Sub TouchImageView1_Click(X As Int, Y As Int)
   Log("Click X="&X&", Y="&Y)
   
   '   zoom the image in by 5% on the clicked point
   '   the new scale factor will be the previous scale factor * 1.05
   
   
   TouchImageView1.PreScale(1.05, X, Y)
End Sub

Sub TouchImageView1_LongClick(X As Int, Y As Int)
   Log("LongClick X="&X&", Y="&Y)
   
   '   set the zoom/scale to 100%
   '   in this demo ScaleX and ScaleY will always be equal
   TouchImageView1.PreScale(1/TouchImageView1.ScaleX, X, Y)
   
End Sub

All pretty much the same in use as the last versions but the new Click and LongClick events should be useful, along with the method to scale the image in your B4A code.

The only problem i've found is that the LongClick event is sometimes raised when the image is slightly dragged.
I'll hopefully find a fix for that.
(Once a drag or zoom has started, no events should then be raised when drag or zoom ends).

Version 2.00 is attached to the first post in this thread.

Martin.
 
Last edited:

BarrySumpter

Active Member
Licensed User
Longtime User
Very nice.

Need to see if I can snap a
gMap zoom level 19
with lot number/addresses
and magnify.

Or touchMove a panel to magnify like moving a magnifying glass.
Kinda like moving a miniMap around to magnify.

I'd also like to test to see if this will fix the android resize issue
when I roatate an imageView direction arrow icon based on GPS heading.
 
Last edited:

BarrySumpter

Active Member
Licensed User
Longtime User
Hi Martin,

Thanks for the excellent work.

I've attempted to implement the TouchImageView v2 to see if we can correnct the android based problem of resizing when rotating.

Resizing does NOT happen with TIV2.
But does not stay centred as I would expect.

I've attached a project that illustrates the the issue.

To me the red cross hairs centre post should not move.

If there is anything you could do with TIV2 to help compensate,
it would be greatly appreciated.

And no prob if not.

Thanks again.
And all my best.
 

Attachments

  • Sensors2.zip
    13.3 KB · Views: 257

warwound

Expert
Licensed User
Longtime User
I added a MenuItem to your project so i could call the TouchImageView CreateLog method.

I started your project and press the MenuItem, the log output was:
** TouchImageView.CreateLog **
Original width=54, height=54
Current width=648, height=648
Current position: x=0, y=122
Current scale: x=12.0, y=12.0

Gave my device a spin and press MenuItem again:
** TouchImageView.CreateLog **
Original width=57, height=57
Current width=684, height=684
Current position: x=0, y=122
Current scale: x=12.0, y=12.0

When BitmapExtended rotates the Bitmap it changes the size of the Bitmap - here it changes from 54 pixels square to 57 pixels square.

I'd guess that change in original Bitmap size and the scale applied accounts for the change of the Bitmap centre.

If you want to programmatically rotate the Bitmap displayed by TouchImageView there are Matrix methods i can implement.
That would hopefully work as expected as the Matrix methods allow you to specify the point within the Bitmap to rotate it around.

So you'd add a Bitmap to your TouchImageView once and apply translations (movement), scaling (zooming) and rotation to that Bitmap, instead of adding a new Bitmap (with a different size) to acheive the rotation which seems to be where your code is failing.

I'll see what i can do - but i won't even try to add touch detection for rotation.
Any rotation methods will be executed in B4A code only, trying to add touch events to rotate the Bitmap is something that i just don't have time to do...

Martin.
 

Attachments

  • Sensors2_CreateLog.zip
    13.3 KB · Views: 266

Smee

Well-Known Member
Licensed User
Longtime User
Hi Martin

Have not fully read all the thread yet but for wiw i agree with no autoscaling.

Thanks for your efforts
 
Top