Toggle part of image

Philipp

Member
Licensed User
Longtime User
Hi,

I am referring to this Tutorial, where you have one bitmap for the looks and another one in the background to determine which area has been clicked.

I succeeded in editing the code, so that it resizes to any screen-size, but I'm totally unsure how to add areas where I can change parts of the graphic when clicking a certain area.

There are so many ways, but I don't really know what route to take.

It's importan for me to be able to position the buttons pixelperfect but still not having to create different graphics for every possible screen-size.

I would like to scale the whole thing, like I already implemented in the code.

B4X:
'Activity module
Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.

End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.
   Dim bmp As Bitmap
   Dim bmp1 As Bitmap   ' Placeholder for resized Bitmap
   
   Dim Panel1 As Panel
   Dim Label1 As Label
   Dim Canvas1 As Canvas   ' Canvas to draw resized image to
   Dim DestRect As Rect   ' Size of Fullscreen
End Sub

Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("1")
   
   bmp.Initialize(File.DirAssets, "CircMenuMap32bit.png")
   'Canvas1.Initialize(Activity)
   'Canvas1.DrawColor(Colors.ARGB(255,200,20,20))
   
   ' Define FullScreen Rect
   DestRect.Initialize(0,0,Activity.Width, Activity.Height)
   
   ' Create empty Fullsize-Bitmap
   bmp1.InitializeMutable(Activity.Width, Activity.Height)
   
   ' Init Canvas with size of new Bitmap
   Canvas1.Initialize2(bmp1)
   
   ' Draw orginal Bitmap as new Resized Bitmap on Canvas
   ' Thats where the resizing takes place
   Canvas1.DrawBitmap(bmp, Null, DestRect)
   
   ' Resize Panel to Fullscreen
   ' Panel is where the normal images is drawn onto
   Panel1.Width = -1
   Panel1.Height = -1
   
   
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub Panel1_Touch (Action As Int, X As Float, Y As Float)
   If action = activity.ACTION_DOWN Then
      Dim c As Int

      c = bmp1.GetPixel(x,y)
      
      Select c
      Case Colors.RGB(255,0,0)
         label1.Text = "Clicked on Recycle"
      Case Colors.RGB(255,255,0)
         label1.Text ="Clicked on Film roll"
      Case Colors.RGB(0,255,0)
         label1.Text ="Clicked on Rubik cube"
      Case Colors.RGB(0,255,255)
         label1.Text ="Clicked on Old Radio"
      Case Colors.RGB(0,0,255)
         label1.Text ="Clicked on Record player"
      Case Colors.RGB(255,0,255)
         label1.Text ="Clicked on Box with stuff"
      Case Colors.RGB(255,192,192)
         label1.Text ="Clicked on Old TV"
      Case Colors.RGB(255,255,192)
         label1.Text ="Clicked on Folder with pictures"
      Case Colors.RGB(192,255,192)
         label1.Text ="Clicked on Ladybug"
      Case Colors.RGB(255,255,255)
         label1.Text ="Clicked on Big Button"
      Case Else
         label1.Text =""
      End Select
      Return
   End If
End Sub
 

Attachments

  • circmenu.jpg
    circmenu.jpg
    5.9 KB · Views: 206
  • circmenumap32bit.jpg
    circmenumap32bit.jpg
    5.8 KB · Views: 192

klaus

Expert
Licensed User
Longtime User
If I understand well, you want to change some parts of the graphics like toggle buttons.
What shape and size are these images ?

You resize the images to full screen (width and height), I'm afraid that depending on the height/width ratio which is different on different devices your circle will become an ellipse. You should use square images and resize them either to the activity width in portrait or to the activity height in landscape mode and center them on the screen.

I see two possibilities:
- You could have one image for each segment, only the segemnt has an image the rest is transparent, that you load to a Panel overlaying the original Panel. To go back set this Panel to not visible
- You could have a third image with the same size as the two others with the new images you want to toggle. Define a path for each segment and copy that part to the original image. Copy the whole original image to the Panel to go back.

Best regards.
 
Upvote 0

Philipp

Member
Licensed User
Longtime User
I won't have a round image, so unproportianl stretching shouldn't be a problem, hopefully.

The first version would be the easiest, but would be quite a waste of memory I think.

The second version has the problem, how to find out the correct coordinates of the path.
What about the coordinates between the scaled bitmap and the paths? Wouldn't I need to have the coordinates relative in percent?

Thinking of it, would it be possible to create some sort of mask? I have the same images as in the second version, but instead of paths, I would use the information from the area-image and mask out the corresponding area in the first image, so that the part of the second image would shine through. Is this doable?
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
I was thinking of paths because your image showed round segments.
Are your partial images rectangular ?
Are all the rectangles equal ?
If yes you could define the rectangles proportional to the screen size and copy the partial images like in the second proposal. In this case you don't need the Panel with the color reference, with the coordinates you could calculate to know in what area you are.

Still, if your images are rectangular, you could have one Panel for each image and copy the corresponding image onto it. Here you don't need the Panel with the color reference. I used this method in the GPS Example.

Best regards.
 
Upvote 0

Philipp

Member
Licensed User
Longtime User
No, these are the images from the demo. My images will be handdrawn, more for children with clickable areas. Having the color-area image I'm very flexible with the definition of the clickable areas. This flexibility is very important for me and the type of app I aim.

Still, if your images are rectangular, you could have one Panel for each image and copy the corresponding image onto it. Here you don't need the Panel with the color reference

So for having ten buttons I would need 11 images in the full resolution? Working with xhdpi-devices that would be a lots of memory space needed? Having 10 to 15 Screens that would be 150+ full sized images. Of course I could use jpg with 70% compression, but still...

Masking is not possible?
 
Upvote 0

Philipp

Member
Licensed User
Longtime User
Made a quick kb-calc: one image at 1024x600 ~ 40kb, if I have ~10 buttons and ~ 10 Screens that would blow my app up to ~4.3 MB, only images. Plus the color-mask as 32bit png, plus the sounds... As long as I stay under 20MB (I can't load any app bigger than 20MB with my Samsung Galaxy Tab Froyo) it should be ok, but ....
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
If you have 10 buttons on the screen you would have 10 images with a size 10 times smaller, instead of one big image. So the overall memory would be almost the same. But as your areas are different so this is not the solution.

Masking would be similar to draw a certain area, you must draw a transparent area on the overlay. Getting the information from the color reference image would need to scan all pixels and when they match the given color draw transparent. I'm afraid that this would be a quite long process.

I think that the path solution would be most efficient, you would need to calculate the path vertices according to the screen dimension.

Best regards.
 
Upvote 0

Philipp

Member
Licensed User
Longtime User
If you have 10 buttons on the screen you would have 10 images with a size 10 times smaller, instead of one big image.
Oh ok, then I misunderstood you. I would have to paste the buttons at the correct position and to uncheck repaste the original image or the button as unchecked version.

And what about StateListDrawable? Can they be used with Stretching?
 
Upvote 0

Philipp

Member
Licensed User
Longtime User
Hmm, just tested ToogleButtons, but now I cannot use the flexible color-mask as shown in the first post, which is really nice. And with ToogleButtons I cannot use paths or free areas for selecting.

Hmm, I'll have to think about what I really need, before going on.

Thank you very much for your help!
 
Upvote 0

Philipp

Member
Licensed User
Longtime User
Ok I think I got a valable solution for what I aim for.

The original size is for 480x800 Phones and it now scales to my sgt. I didn't test it yet on smaller deviced (ldpi).

I created a toogleButton with states for the cube and added a sound and a timer.

On the same time, underlaying selection bitmap is still active, where no toggleButton is over. So, clicking on the LadyBug produces the same result as if clicking on the cube.

I had to scale the toogleButton by hand using:
B4X:
Dim facWidth As Float
Dim facHeight As Float
   
facWidth = Activity.Width / bmp.Width
facHeight = Activity.Height / bmp.Height
.
.
.
Activity.AddView(tb_cube, 313 * facWidth, 214* facHeight, 80*facWidth, 75*facHeight )

And that's all the code:
B4X:
'Activity module
Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.

End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.
   Dim bmp, bmp1 As Bitmap
   Dim Panel1 As Panel
   Dim Label1 As Label
   
   Dim Canvas1 As Canvas   ' Canvas to draw resized image to
   Dim DestRect As Rect   ' Size of Fullscreen
   
   Dim tb_cube As ToggleButton
   Dim sd_cube As StateListDrawable
   Dim bd_cube_0, bd_cube_1 As BitmapDrawable
   
   Dim timer1 As Timer
   Dim mediaPlayer1 As MediaPlayer
   
   Dim cube_checked As Boolean
    'tb_cube.Initialize("") 'no events will be caught
   


End Sub

Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("1")
   bmp.Initialize(File.DirAssets, "CircMenuMap32bit.png")
   
   ' Recalc for new Activity-Dimensions
   
   Dim facWidth As Float
   Dim facHeight As Float
   
   facWidth = Activity.Width / bmp.Width
   facHeight = Activity.Height / bmp.Height
   
   Log(facWidth & " / " & facHeight)
   
   ' initialize BitmapDrawable ToggleButton
   tb_cube.Initialize("tb_cubeBitmap")
   
   Activity.AddView(tb_cube, 313 * facWidth, 214* facHeight, 80*facWidth, 75*facHeight )
   tb_cube.TextOff = ""
   tb_cube.TextOn = ""
   
   bd_cube_0.Initialize(LoadBitmap(File.DirAssets, "bt_cube_0.png"))
   bd_cube_1.Initialize(LoadBitmap(File.DirAssets, "bt_cube_1.png"))
   
   sd_cube.Initialize
   
   Dim states(2) As Int
   states(0) = sd_cube.state_unchecked
   states(1) = -sd_cube.state_checked
   sd_cube.addState2(states, bd_cube_0)
   
   Dim states(1) As Int
   states(0) = sd_cube.state_checked
   sd_cube.addState2(states, bd_cube_1)
   
   tb_cube.Background = sd_cube
   
   
   ' Define FullScreen Rect
   DestRect.Initialize(0,0,Activity.Width, Activity.Height)
   
   ' Create empty Fullsize-Bitmap
   bmp1.InitializeMutable(Activity.Width, Activity.Height)
   
   ' Init Canvas with size of new Bitmap
   Canvas1.Initialize2(bmp1)
   
   ' Draw orginal Bitmap as new Resized Bitmap on Canvas
   ' Thats where the resizing takes place
   Canvas1.DrawBitmap(bmp, Null, DestRect)
   
   ' Resize Panel to Fullscreen
   ' Panel is where the normal images is drawn onto
   Panel1.Width = -1
   Panel1.Height = -1
   
   
   mediaPlayer1.Initialize()
   'mediaPlayer1.Load(File.DirAssets,  "hahaha.mp3")
   timer1.Initialize("timer1", 5000)
   
   cube_checked = False
   
   
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub Panel1_Touch (Action As Int, X As Float, Y As Float)
   If action = activity.ACTION_DOWN Then
      Dim c As Int
      
      c = bmp1.GetPixel(x,y)
      Select c
      Case Colors.RGB(255,0,0)
         label1.Text = "Clicked on Recycle"
      Case Colors.RGB(255,255,0)
         label1.Text ="Clicked on Film roll"
      Case Colors.RGB(0,255,0)
         label1.Text ="Clicked on Rubik cube"
      Case Colors.RGB(0,255,255)
         label1.Text ="Clicked on Old Radio"
      Case Colors.RGB(0,0,255)
         label1.Text ="Clicked on Record player"
      Case Colors.RGB(255,0,255)
         label1.Text ="Clicked on Box with stuff"
      Case Colors.RGB(255,192,192)
         label1.Text ="Clicked on Old TV"
      Case Colors.RGB(255,255,192)
         label1.Text ="Clicked on Folder with pictures"
      Case Colors.RGB(192,255,192)
         label1.Text ="Clicked on Ladybug"
         If cube_checked = False Then
            cube_checked = True
         Else
            cube_checked = False
         End If
         tb_cubeBitmap_CheckedChange(cube_checked)
            
      Case Colors.RGB(255,255,255)
         label1.Text ="Clicked on Big Button"
      Case Else
         label1.Text =""
      End Select
      Return
   End If
End Sub


Sub tb_cubeBitmap_CheckedChange(Checked As Boolean)
   Activity.Title = Checked
   label1.Text ="Clicked on Toggle Rubik cube"
   If Checked = True Then
      tb_cube.Checked = True
      cube_checked = True
      timer1.Enabled = True
      mediaPlayer1.Load(File.DirAssets, "hahaha.mp3")
      mediaPlayer1.Play
   Else
      timer1.Enabled = False
      tb_cube.Checked = False
      cube_checked = False
      mediaPlayer1.Stop
   End If
End Sub

' When the timer is through
Sub timer1_Tick
   tb_cube.Checked = False
   cube_checked = False
   mediaPlayer1.Stop
End Sub

The complete project is zipped and attached for anyone wishing to try it out or enhance it ;)

Now I'm happy and can go to bed :D
 
Upvote 0

susu

Well-Known Member
Licensed User
Longtime User
Hi,

I am referring to this Tutorial, where you have one bitmap for the looks and another one in the background to determine which area has been clicked.

I succeeded in editing the code, so that it resizes to any screen-size, but I'm totally unsure how to add areas where I can change parts of the graphic when clicking a certain area.

There are so many ways, but I don't really know what route to take.

It's importan for me to be able to position the buttons pixelperfect but still not having to create different graphics for every possible screen-size.

I would like to scale the whole thing, like I already implemented in the code.

B4X:
'Activity module
Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.

End Sub

Sub Globals
   'These global variables will be redeclared each time the activity is created.
   'These variables can only be accessed from this module.
   Dim bmp As Bitmap
   Dim bmp1 As Bitmap   ' Placeholder for resized Bitmap
   
   Dim Panel1 As Panel
   Dim Label1 As Label
   Dim Canvas1 As Canvas   ' Canvas to draw resized image to
   Dim DestRect As Rect   ' Size of Fullscreen
End Sub

Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("1")
   
   bmp.Initialize(File.DirAssets, "CircMenuMap32bit.png")
   'Canvas1.Initialize(Activity)
   'Canvas1.DrawColor(Colors.ARGB(255,200,20,20))
   
   ' Define FullScreen Rect
   DestRect.Initialize(0,0,Activity.Width, Activity.Height)
   
   ' Create empty Fullsize-Bitmap
   bmp1.InitializeMutable(Activity.Width, Activity.Height)
   
   ' Init Canvas with size of new Bitmap
   Canvas1.Initialize2(bmp1)
   
   ' Draw orginal Bitmap as new Resized Bitmap on Canvas
   ' Thats where the resizing takes place
   Canvas1.DrawBitmap(bmp, Null, DestRect)
   
   ' Resize Panel to Fullscreen
   ' Panel is where the normal images is drawn onto
   Panel1.Width = -1
   Panel1.Height = -1
   
   
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub Panel1_Touch (Action As Int, X As Float, Y As Float)
   If action = activity.ACTION_DOWN Then
      Dim c As Int

      c = bmp1.GetPixel(x,y)
      
      Select c
      Case Colors.RGB(255,0,0)
         label1.Text = "Clicked on Recycle"
      Case Colors.RGB(255,255,0)
         label1.Text ="Clicked on Film roll"
      Case Colors.RGB(0,255,0)
         label1.Text ="Clicked on Rubik cube"
      Case Colors.RGB(0,255,255)
         label1.Text ="Clicked on Old Radio"
      Case Colors.RGB(0,0,255)
         label1.Text ="Clicked on Record player"
      Case Colors.RGB(255,0,255)
         label1.Text ="Clicked on Box with stuff"
      Case Colors.RGB(255,192,192)
         label1.Text ="Clicked on Old TV"
      Case Colors.RGB(255,255,192)
         label1.Text ="Clicked on Folder with pictures"
      Case Colors.RGB(192,255,192)
         label1.Text ="Clicked on Ladybug"
      Case Colors.RGB(255,255,255)
         label1.Text ="Clicked on Big Button"
      Case Else
         label1.Text =""
      End Select
      Return
   End If
End Sub

Hi Philipp,

I tried your code, it scale the image to full screen but when I click on buttons the value returned is not correct. How do I fix it?
 
Upvote 0
Top