Android Question Image view that the user can drag around on the screen

rleiman

Well-Known Member
Licensed User
Greetings,

My employer asked me if I can set up 12 image views that the user can drag around on the screen. If B4A can handle this, please direct me to a library that will allow me to set it up. If there's a library that can do it, I hope the library will allow me to catch the instant the user lifts up his/her finger so I can know the x and y coordinates at that instant.

Thanks.
 

DavideV

Active Member
Licensed User
Hi, you don't need any library If you move them one at a time. Use 12 panels with the imgeview inside (or use the images as a background for the panels, without imageviews).
Then use the panels standard touch event to move them; in the event you have touch position and touch up/down....
You could, for example, highlight the border of the touched one to distinguish it from the others, When the user moves it and release the finger you could finalize the movement to the nearest one and exchange them, maybe with a bit of animations.

Cheers
//Davide
 

rleiman

Well-Known Member
Licensed User
Hi, you don't need any library If you move them one at a time. Use 12 panels with the imgeview inside (or use the images as a background for the panels, without imageviews).
Then use the panels standard touch event to move them; in the event you have touch position and touch up/down....
You could, for example, highlight the border of the touched one to distinguish it from the others, When the user moves it and release the finger you could finalize the movement to the nearest one and exchange them, maybe with a bit of animations.

Cheers
//Davide
Hi Davide,

It's nice if I initially tap it then disable the others and wait for the user to tap somewhere on the screen and reposition it but first I want to try a dragging class or library so the user can actually see it moving as they drag with a finger.

Nice idea! :D
Thanks.
 

rleiman

Well-Known Member
Licensed User
Hi Erel,

I love that class. :)

I made a small app to test it with 3 labels and can drag them around.

I do have a few questions.

How do I limit the dragging to within the boundaries of the panel where the labels are placed? I want to make sure the user can't drag them outside the panel.

I also placed a label on the panel that I would like to display the x and y positions when the user lifts the finger after dragging a label view from within the main activity.

I coded it this way with callsub3 in the class and it does return values but I'm sure this is not the best way to do it.

Here's my main activity and your class.

B4X:
#Region  Project Attributes
    #ApplicationLabel: Drag View Test
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

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.

    Private Velocity1 As Label
    Private Velocity2 As Label
    Private Velocity3 As Label
    Private PanelMyPanel As Panel
    Private Label1 As Label
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    Activity.LoadLayout("Main")

    Dim dv1, dv2, dv3 As DraggableView
    dv1.Initialize(PanelMyPanel, Velocity1)
    dv2.Initialize(PanelMyPanel, Velocity2)
    dv3.Initialize(PanelMyPanel, Velocity3)
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Public Sub DraggedPosition (X As Float, Y As Float)
   
    Label1.Text = "X = " & x & "    Y = " & Y
End Sub
B4X:
Sub Class_Globals
   
    Private innerView As View
    Private panel1 As Panel
    Private downx, downy As Int
    Private ACTION_DOWN, ACTION_MOVE, ACTION_UP As Int
End Sub

Sub Initialize(Activity As Activity, v As View)
    innerView = v
    panel1.Initialize("")
    panel1.Color = Colors.Transparent
    Activity.AddView(panel1, v.Left, v.Top, v.Width, v.Height)
    ACTION_DOWN = Activity.ACTION_DOWN
    ACTION_MOVE = Activity.ACTION_MOVE
    ACTION_UP = Activity.ACTION_UP
    Dim r As Reflector
    r.Target = panel1
    r.SetOnTouchListener("Panel1_Touch") 'why reflection instead of the regular Panel_Touch event? Good question which deserves a forum thread of its own (not related to classes)...
End Sub

Private Sub Panel1_Touch (o As Object, ACTION As Int, x As Float, y As Float, motion As Object) As Boolean
    If ACTION = ACTION_DOWN Then
        downx = x
        downy = y
    Else
        innerView.Left = innerView.Left + x - downx
        innerView.Top = innerView.Top + y - downy
        panel1.Left = innerView.Left
        panel1.Top = innerView.Top
       
        CallSub3(Main, "DraggedPosition", innerView.Left + x - downx, innerView.Top + y - downy)
    End If
    Return True
End Sub
 
Last edited:

rleiman

Well-Known Member
Licensed User
I got everything working. Here's the coding based on the class from Erel. If it can be improved, please let me know so I can learn more. :D

The main module:


B4X:
#Region  Project Attributes
    #ApplicationLabel: Drag View Test
    #VersionCode: 1
    #VersionName:
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: True
#End Region

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

    Dim fltDraggedX As Float
    Dim fltDraggedY As Float
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variabtles can only be accessed from this module.

    Private PanelMyPanel As Panel
    Private Label1 As Label
    Private Velocity1 As Label
    Private Velocity2 As Label
    Private Velocity3 As Label
    Private Velocity4 As Label
    Private Velocity5 As Label
    Private Velocity6 As Label
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    Activity.LoadLayout("Main")

    Dim dv1, dv2, dv3, dv4, dv5, dv6 As DraggableView

    dv1.Initialize(PanelMyPanel, Velocity1, "Velocity1", PanelMyPanel.Width, PanelMyPanel.Height, _
        Velocity1.Width, Velocity1.Height)

    dv2.Initialize(PanelMyPanel, Velocity2, "Velocity2", PanelMyPanel.Width, PanelMyPanel.Height, _
        Velocity2.Width, Velocity2.Height)

    dv3.Initialize(PanelMyPanel, Velocity3, "Velocity3", PanelMyPanel.Width, PanelMyPanel.Height, _
        Velocity3.Width, Velocity3.Height)

    dv4.Initialize(PanelMyPanel, Velocity4, "Velocity4", PanelMyPanel.Width, PanelMyPanel.Height, _
        Velocity4.Width, Velocity4.Height)

    dv5.Initialize(PanelMyPanel, Velocity5, "Velocity5", PanelMyPanel.Width, PanelMyPanel.Height, _
        Velocity5.Width, Velocity5.Height)

    dv6.Initialize(PanelMyPanel, Velocity6, "Velocity6", PanelMyPanel.Width, PanelMyPanel.Height, _
        Velocity6.Width, Velocity6.Height)

End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Public Sub DraggedPosition (strDraggedViewNameFromClass As String)
    
    Select strDraggedViewNameFromClass
        
        Case "Velocity1"
            Label1.Text = "Circle 1 X = " & fltDraggedX & "    Y = " & fltDraggedY
            
        Case "Velocity2"
            Label1.Text = "Circle 2 X = " & fltDraggedX & "    Y = " & fltDraggedY
            
        Case "Velocity3"
            Label1.Text = "Circle 3 X = " & fltDraggedX & "    Y = " & fltDraggedY

        Case "Velocity4"
            Label1.Text = "Circle 4 X = " & fltDraggedX & "    Y = " & fltDraggedY

        Case "Velocity5"
            Label1.Text = "Circle 5 X = " & fltDraggedX & "    Y = " & fltDraggedY

        Case "Velocity6"
            Label1.Text = "Circle 6 X = " & fltDraggedX & "    Y = " & fltDraggedY

    End Select
End Sub
The class based on Erel's class:

B4X:
Sub Class_Globals
    
    Private innerView As View
    Private panel1 As Panel
    Private downx, downy As Int
    Private ACTION_DOWN, ACTION_MOVE, ACTION_UP As Int
    Private strDraggedViewName As String
    Private intDraggedPanelWidth As Int
    Private intDraggedPanelHeight As Int
    Private intDraggedViewWidth As Int
    Private intDraggedViewHeight As Int
End Sub

Sub Initialize(Activity As Activity, v As View, strViewName As String, _
        intPanelWidth As Int, intPanelHeight As Int, intViewWidth As Int, _
        intViewHeight As Int)
        
    strDraggedViewName = strViewName
    intDraggedViewHeight = intViewHeight
    intDraggedViewWidth = intViewWidth
    intDraggedPanelHeight = intPanelHeight
    intDraggedPanelWidth = intPanelWidth
    innerView = v
    panel1.Initialize("")
    panel1.Color = Colors.Transparent
    Activity.AddView(panel1, v.Left, v.Top, v.Width, v.Height)
    ACTION_DOWN = Activity.ACTION_DOWN
    ACTION_MOVE = Activity.ACTION_MOVE
    ACTION_UP = Activity.ACTION_UP
    Dim r As Reflector
    r.Target = panel1
    r.SetOnTouchListener("Panel1_Touch") 'why reflection instead of the regular Panel_Touch event? Good question which deserves a forum thread of its own (not related to classes)...
End Sub

Private Sub Panel1_Touch (o As Object, ACTION As Int, x As Float, y As Float, motion As Object) As Boolean
    If ACTION = ACTION_DOWN Then
        downx = x
        downy = y
    Else
        If (innerView.Left + x - downx > 0) And _
            (innerView.Left + x < intDraggedPanelWidth) And _
            (innerView.Top + y - downy + intDraggedViewHeight < intDraggedPanelHeight +2) And _
            (innerView.Top + y - downy > 0) Then
    
            innerView.Left = innerView.Left + x - downx
            innerView.Top = innerView.Top + y - downy
            panel1.Left = innerView.Left
            panel1.Top = innerView.Top

            ' Since callsub has a limit of 3 parameters, I'm populating these global variables
            ' in the main activity. They will be processed by code in that activity.
            '---------------------------------------------------------------------------------
            Main.fltDraggedY = innerView.Top

            Main.fltDraggedX = innerView.Left + x - downx

            If innerView.Left + x - downx < 0 Then
                Main.fltDraggedX = 0
                innerView.Left = Main.fltDraggedX
            End If

            If innerView.Left + x + intDraggedViewWidth > intDraggedPanelWidth +2 Then
                Main.fltDraggedX = intDraggedPanelWidth - intDraggedViewWidth
                innerView.Left = Main.fltDraggedX
            End If
        
            If innerView.Top + y - downy < 0 Then
                Main.fltDraggedY = 0
                innerView.Top = Main.fltDraggedy
            End If

            If innerView.Top + y - downy + intDraggedViewHeight > intDraggedPanelHeight +2 Then
                Main.fltDraggedY = intDraggedPanelHeight - intDraggedViewHeight
                innerView.Top = Main.fltDraggedy
            End If

            ' This will call a sub in the main activity so it can process the position of the
            ' dragged view.
            '--------------------------------------------------------------------------------
            CallSub2(Main, "DraggedPosition", strDraggedViewName)
        End If
    End If
    Return True
End Sub
 
Top