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,098
  • TouchImageView_v_2_00.zip
    200.9 KB · Views: 4,489
Last edited:

BarrySumpter

Active Member
Licensed User
Longtime User
...
I'll see what i can do ...
......

No pressure from me.
Anything you have the interest and time for would be great.

I've changed the direction_arrow8.png
from 40x40
to 41x41
so it can have an exact centre to pivot on
marked by the centre point of a red cross.
 

Attachments

  • direction_arrow8.png
    direction_arrow8.png
    2.6 KB · Views: 408

HarveyKwok

Member
Licensed User
Longtime User
Hi,
could I put more than one draggable object (bitmap) on the touchimageview?
If answer if answer is yes, how?

And

How could we know the position of the draggable object?

could anyone show me a sample code, display the position of the object on the title bar.

Thanks
 

BarrySumpter

Active Member
Licensed User
Longtime User
I usually start with searching this forum.
(where I should really start with the Documentation)

I do like it when I find my exact question first and the answer already posted.
And reading how others have had the same issues and discuss them, etc.

But find that the Documemtation page is a wealth of info.

Click the Documantation button above
and seach the page for key words.

Pretty much anything you want to do is here on this site.
Including Getting Started Guides and Beginners Guides, etc.

In here you sould find how to search and find, download, unzip n where to place libraries.

I'll go back thru those guides every once in a while and find something I forgot. Or something that clears up a nagging question I didn't know I had.

Let us know how you go.


P.S.

I don't know why but I find that once I post a question here I find the answer within 5 mins.
Must be some sort of process purge to clear me brain.
And I try to post what I find and the answer for others
and a reminder to myself. (And not just 'solved')
 

warwound

Expert
Licensed User
Longtime User
Hi,
could I put more than one draggable object (bitmap) on the touchimageview?
If answer if answer is yes, how?

And

How could we know the position of the draggable object?

could anyone show me a sample code, display the position of the object on the title bar.

Thanks

Take a look at this post: http://www.b4x.com/forum/additional...updates/15616-touchimageview-4.html#post98692
There's no way to add more than one image to an ImageView.

And the reference in this post mentions the GetDestRect method:

Returns a Rect that describes the current scale and position of the image within the TouchImageView.

The Rect object has Left, Right,Top and Bottom properties which tell you whereabouts the image is positioned and it's current dimensions.

Martin.
 

ceballot

Member
Licensed User
Longtime User
Hi. Very nice work.

I'm testing version 2.00 and I have a question.

Can I block translation (movement) when the bitmap is not scaled? I want to do something like Android's Gallery do when you view a photo. That is, if the picture is not enlarged, you can scroll left or right to view more photos.

Thanks
 

warwound

Expert
Licensed User
Longtime User
Hi.

The library has two properties: ScaleX and ScaleY, if both of these methods return 1 then the image is not scaled.
(If you have added the original image to the TouchImageView with the original aspect ratio then ScaleX and ScaleY will always be equal).

You can enable or disable touch events with the TouchEnabled property.

Finally you can listen for the Click and/or LongClick events.

So can you detect when the image is touched, check the current scale and enable or disable touch based on the scale?

That probably won't work perfectly though - once an image is not scaled all touch events would remain disabled...

Let me have a think and i'll post again when i have a better solution.

Martin.
 

johnaaronrose

Active Member
Licensed User
Longtime User
How to use

Apologies for this newbie question. I've just been trying the TouchImageViewDemo in Basic4android v2.00. All the TouchImageView stuff is shown in red. But I see no Lib relating to TouchImage nor any Additional Library files (e.g. .jar or .xml) in the 2.0 zip file. What am I missing?
 

warwound

Expert
Licensed User
Longtime User
Hi.

In TouchImageView_2_00.zip there are three folders: library_files is the folder containg the library .jar and .xml files and these are the files you need to copy to your B4A Additional Libraries folder.

Can you not see the library_files folder in the downloaded archive?

Martin.
 

johnaaronrose

Active Member
Licensed User
Longtime User
Hi.

In TouchImageView_2_00.zip there are three folders: library_files is the folder containg the library .jar and .xml files and these are the files you need to copy to your B4A Additional Libraries folder.

Can you not see the library_files folder in the downloaded archive?

Martin.

Thanks, warwound. I didn't notice the library_files directory as I extracted the files to a directory with lots of other files & directories in it!
 

warwound

Expert
Licensed User
Longtime User
Hi. Very nice work.

I'm testing version 2.00 and I have a question.

Can I block translation (movement) when the bitmap is not scaled? I want to do something like Android's Gallery do when you view a photo. That is, if the picture is not enlarged, you can scroll left or right to view more photos.

Thanks

Try the attached modified verion of TouchImageView.
When the image scale is 1 no dragging occurs - but you can still pinch to zoom.
And pinching changes the image scale so dragging can again be enabled.

Here's the java source:

B4X:
if (getScaleX() != 1 && getScaleY() != 1) {
   mMatrix.set(mSavedMatrix);
   float transX = pMotionEvent.getX() - mStartPoint.x;
   float transY = pMotionEvent.getY() - mStartPoint.y;
   if (Math.abs(transX) >= 10f || Math.abs(transY) >= 10f) {
      mMatrix.postTranslate(pMotionEvent.getX() - mStartPoint.x, pMotionEvent.getY() - mStartPoint.y);
   }
}

I could change that so that if the user tries to drag the image and the image scale is 1 then raise an event in the B4A Activity:

B4X:
if (getScaleX() != 1 && getScaleY() != 1) {
   mMatrix.set(mSavedMatrix);
   float transX = pMotionEvent.getX() - mStartPoint.x;
   float transY = pMotionEvent.getY() - mStartPoint.y;
   if (Math.abs(transX) >= 10f || Math.abs(transY) >= 10f) {
      mMatrix.postTranslate(pMotionEvent.getX() - mStartPoint.x, pMotionEvent.getY() - mStartPoint.y);
   }
} else {
   mBA.raiseEvent(this, mEventName + "_dragignored", new Object[] {});
}

The library would then raise a DragIgnored event when the user tries to drag the image but image scale is 1 - is that useful?
Would your code make any use of the attempted drag start and end coordinates - the code could pass those coordinates to the event Sub if so.
(I'll put together a modified version just for you but won't add the new code to the 'official' library).

Martin.
 

Attachments

  • modified_touch_image_view.zip
    9.4 KB · Views: 275

ceballot

Member
Licensed User
Longtime User
Hi Martin.

Thank you very much for your help. :)

I've been testing your library changes with your TouchImageViewDemo example and does not work as I expected. :BangHead:

Here I copied your sample program and I added some modifications and comments for you to see how I want it to work (green text)

My mistake was to ask that the image does not move when scales = 1, but what I need is not to move when scale is equal that the initials values (XScaleInitial and YScaleInitial in the example below)


'Activity module
Sub Process_Globals
Dim SourceImageRect, TouchImageViewRect As Rect

Dim XScaleInitial ,YScaleInitial As Float
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.TouchEnabled=True

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

XScaleInitial=TouchImageView1.ScaleX
YScaleInitial=TouchImageView1.ScaleY

TouchImageView1.MinScale=XScaleInitial 'I just want to zoom-in


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)

'restore initial scale and position
TouchImageView1.ScaleSrcRectToDestRect(SourceImageRect, TouchImageViewRect, "CENTER")

End Sub


Thanks
 

warwound

Expert
Licensed User
Longtime User
OK, look at this modified version of the demo 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.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
   
   TouchImageView1.MinScale=TouchImageView1.ScaleX   '   prevent zooming out
   
   '   set the new MinDragScale property
   TouchImageView1.MinDragScale=TouchImageView1.MinScale
   
   TouchImageView1.MaxScale=2            '   default is 1.5
   TouchImageView1.TranslatePadding=128dip   '   default is 64dip
   
   
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)
   'restore initial scale and position
   TouchImageView1.ScaleSrcRectToDestRect(SourceImageRect, TouchImageViewRect, "CENTER")   
End Sub

Sub TouchImageView1_DragIgnored
   Log("DragIgnored")
   '   from here you could assume a swipe action and change the image?
End Sub

Two bits of code to look at:

B4X:
   '   set the new MinDragScale property
   TouchImageView1.MinDragScale=TouchImageView1.MinScale

The new MinDragScale property - if the image scale is NOT greater than this scale then dragging is disabled.

B4X:
Sub TouchImageView1_DragIgnored
   Log("DragIgnored")
   '   from here you could assume a swipe action and change the image?
End Sub

A new event DragIgnored is raised when the user tries to drag the image but due to the scale, dragging is disabled.

Demo code and modded library files attached.

Martin.
 

Attachments

  • TouchImageViewDemo_ceballot.zip
    178.9 KB · Views: 341

tamadon

Active Member
Licensed User
Longtime User
Thanks for the library!

I was trying to emulate Android gallery behaviour.

When an image is zoomed in, and then dragged, the dragging is disabled when the edge of the zoomed image is at the edge of the screen and can never dragged further to reveal the black background.

So I think I need to have a dynamic TranslatePadding value of some sort but not sure how to do it.

Any ideas?

Thanks.
 

warwound

Expert
Licensed User
Longtime User
That sounds like a good idea - better than the current TranslatePadding.

Could we have TranslatePaddingX and TranslatePaddingY properties?

Set TranslatePaddingX to the TouchImageView width and TranslatePaddingY to the TouchImageView height.

I shall look at the code tomorrow and experiment and then post again with my results (or hopefully a library update).

Martin.
 

warwound

Expert
Licensed User
Longtime User
Here's an (incomplete) update, i've added two new properties TranslatePaddingX and TranslatePaddingY.

In B4A i set TranslatePaddingX to the TouchImageView width and TranslatePaddingY to the TouchImageView height.

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.TranslatePaddingX=100%x
   TouchImageView1.TranslatePaddingY=100%y
   
   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

Now while the image is zoomed and is larger than the TouchImageView size this works exactly how you requested.
However as soon as the image is zoomed to be smaller than the TouchImageView in either width or height, the image snaps to one of the corners of the TouchImageView.

I've looked at the library source code and cannot see a solution, and have to get busy with other things now.
So i'll make time over the weekend to complete the update hopefully and post again then.

Martin.
 

Attachments

  • TouchImageView_v_2_10_update_test.zip
    178.6 KB · Views: 297

tamadon

Active Member
Licensed User
Longtime User
Thanks for the update Martin!

Tried the update library and it works exactly as I wanted.

Although, it would be great if I can still work with the MinDragScale property while preventing zooming out.

I don't know if this can help preventing the snapping issue.
 

bgsoft

Well-Known Member
Licensed User
Longtime User
memory error

First of all thanks for your great contribution. :sign0098:

I'm using your TouchImageView.
From a ListView select an image and charge in the TouchImageView. Goes perfectly.
The problem is when loading and unloading several large images, the final application for giving a memory error.
Every time I go to see TouchImageView, the release and use
TouchImageView1.RemoveView

It may be that TouchImageView not properly manage memory?

Thanks for everything

Jesus
 

warwound

Expert
Licensed User
Longtime User
I think the problem is more likely that you are creating many Bitmap objects and these Bitmap objects are not releasing the memory used.

TouchImageView is just an ImageView that responds to touch events - it does not create and maintain any references to the image(s) that are passed to it's SetBitmap method.

If you search the forum for bitmap recycle you'll find that creating many Bitmaps frequently causes memory problems.
A solution is usually to call the Bitmap recycle() method, BUT the B4A Bitmap object does not allow you to (directly) call recycle().
Instead you have to use the Reflector library to call the Bitmap recycle method.
Calling recycle() forces the Bitmap to release the memory it is using instead of allowing the Bitmap to release this memory at some arbitary point in time.

Again a forum search will find code examples of using Reflector to call the recycle() method.

Martin.
 
Top