Android Question [SOLVED][class] modified B4J class CLVDragger - for b4A

wes58

Active Member
Licensed User
Longtime User
I have tried to modify Erel's Java class from this thread https://www.b4x.com/android/forum/threads/class-clvdragger-drag-to-reorder-items.104261/ for B4A
I have changed mouse events to touch events. The rest of the code is the same as in Erel's code.

The class code is as follows:
B4X:
'v1.00
Sub Class_Globals
    Private xui As XUI
'    Private fx As JFX
    Private list As CustomListView
    Private PressedColor As Int
    Private Top As Int
    Dim pnl As B4XView
    Dim ListStartY As Int
End Sub

Public Sub Initialize (clv As CustomListView)
    list = clv
    PressedColor = list.PressedColor
End Sub

Public Sub Resize
    For i = 0 To list.Size - 1
        Dim p As B4XView = list.GetPanel(i)
        Dim v As B4XView = p.GetView(p.NumberOfViews - 1)
        If v Is Label And v.Tag = list Then
            v.Left = p.Width - 32dip
        End If
    Next
End Sub

Public Sub RemoveDragButtons
    For i = 0 To list.Size - 1
        Dim p As B4XView = list.GetPanel(i)
        Dim b As Boolean = IsLastViewADragLabel(p)
'Log(" b " & b)
        If b Then    'IsLastViewADragLabel(p) Then
            p.GetView(p.NumberOfViews - 1).RemoveViewFromParent
        End If
    Next
    list.PressedColor = PressedColor
End Sub

Private Sub IsLastViewADragLabel (p As B4XView) As Boolean
'Log("no of views " & p.NumberOfViews )
    If p.NumberOfViews > 0 Then
        Dim v As B4XView = p.GetView(p.NumberOfViews - 1)
        Dim b As Boolean
        b = v Is Label
'        Log("no of views " & p.NumberOfViews & " label " & b & " " & v.Tag)
        If v.IsInitialized = False Then
            Return False
        End If
        If v.Tag = Null Then
            Return False
        End If
        Return v Is Label And v.Tag = list
    End If
    Return False
End Sub

Public Sub AddDragButtons
    list.PressedColor = xui.Color_Transparent
    Dim fnt As B4XFont = xui.CreateMaterialIcons(30)
    For i = 0 To list.Size - 1
        Dim p As B4XView = list.GetPanel(i)
        If IsLastViewADragLabel(p) = False Then
            Dim lbl As Label
            lbl.Initialize("")    'Drag")
'            lbl.MouseCursor = fx.Cursors.HAND
            Dim xlbl As B4XView = lbl
            xlbl.Font = fnt
            xlbl.Text = Chr(0xE25D)
            xlbl.TextColor = list.DefaultTextColor
            xlbl.Tag = list
            p.AddView(xlbl, p.Width - 32dip, p.Height / 2 - 15dip, 30dip, 30dip)
            Dim r As Reflector
            r.Target = lbl    'DragAndDrop
            r.SetOnTouchListener("Drag_Touch")
        End If
    Next
End Sub

Sub Drag_Touch(ViewTag As Object, Action As Int, X As Float, Y As Float, EventData As Object) As Boolean
    Dim lbl As B4XView = Sender
    Dim list As CustomListView = lbl.Tag

    If Action = 0 Then                                    'ACTION_DOWN
        Log("action down " & Y)
        Dim r As Reflector
        r.Target = list.sv
        r.RunMethod2("requestDisallowInterceptTouchEvent", True, "java.lang.boolean")
        pnl = list.GetPanel(list.GetItemFromView(lbl)).Parent
        pnl.GetView(0).SetColorAndBorder(xui.Color_Transparent, 3dip, 0xFF503ACD, 0)
        ListStartY = Y + lbl.Top + pnl.Top
        pnl.BringToFront
        Top = pnl.Top
    Else If Action = 1 Then                                'ACTION_UP
        Dim index As Int = list.GetItemFromView(lbl)
        pnl = list.GetPanel(index).Parent
        Dim Offset As Int = pnl.Top + pnl.Height / 2
        Dim NewIndex As Int = list.FindIndexFromOffset(Offset)

        Dim UnderlyingItem As CLVItem = list.GetRawListItem(NewIndex)
        If Offset - UnderlyingItem.Offset > UnderlyingItem.Size / 2 Then
            NewIndex = NewIndex + 1
        End If

        Dim ActualItem As B4XView = pnl.GetView(0)
        ActualItem.SetColorAndBorder(pnl.Color, 0dip, xui.Color_Black, 0)

        Dim RawItem As CLVItem = list.GetRawListItem(index)
        list.RemoveAt(index)
        If NewIndex > index Then
            NewIndex = NewIndex - 1
        End If
        NewIndex = Max(0, Min(list.Size, NewIndex))
        list.InsertAt(NewIndex, ActualItem, RawItem.Value)       
        list.GetRawListItem(NewIndex).TextItem = RawItem.TextItem

    Else If Action = 2 Then                             'ACTION_MOVE
        Dim index As Int = list.GetItemFromView(lbl)
        If pnl.Top < list.sv.ScrollViewOffsetY Then
            list.sv.ScrollViewOffsetY = Max(0, list.sv.ScrollViewOffsetY - 10dip)
        Else If list.sv.ScrollViewOffsetY + list.sv.Height < pnl.Top + pnl.Height Then
            list.sv.ScrollViewOffsetY = list.sv.ScrollViewOffsetY + 10dip
        End If
        Dim ListY As Int = Y + lbl.Top     + pnl.Top
        Dim delta As Int = ListY - ListStartY
        pnl.Top = Top + delta
    Else
        Log("action " & Action)
    End If
    Return True
End Sub

But I have a following error after moving the item:

B4X:
*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **

.customlistview_insertatimpl (B4A line: 113)
list.InsertAt(NewIndex, ActualItem, RawItem.Valu
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
    at android.view.ViewGroup.addViewInner(ViewGroup.java:5958)
    at android.view.ViewGroup.addView(ViewGroup.java:5777)
    at android.view.ViewGroup.addView(ViewGroup.java:5749)
    at anywheresoftware.b4a.objects.PanelWrapper.AddView(PanelWrapper.java:65)
    at anywheresoftware.b4a.objects.B4XViewWrapper.AddView(B4XViewWrapper.java:323)
    at b4a.example3.customlistview._insertatimpl(customlistview.java:567)
    at b4a.example3.customlistview._insertat(customlistview.java:540)
    at b4a.example.clvdragger._drag_touch(clvdragger.java:270)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:213)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:193)
    at anywheresoftware.b4a.agraham.reflection.Reflection$7.onTouch(Reflection.java:1118)
    at android.view.View.dispatchTouchEvent(View.java:14371)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3863)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3551)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3863)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3551)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3863)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3551)
    at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3863)
    at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:3551)

Probably Erel would be the best person to answer this question but answer from anyone else is welcome.
The test project is attached
 

Attachments

  • xCustomListView.zip
    15.6 KB · Views: 289

wes58

Active Member
Licensed User
Longtime User
It has nothing to do with Java. You mean B4J.

It will require some work to get it working in B4A. If it was a simple change then I would have done it.
Well, B4J is for java so, like B4A is for Android. That's why I wrote "Java".

Thanks, I thought that it would be easy for your since xCLV is multi-platform. So I don't know why "list.InsertAt(....) after move (or anything else - list.Add(...) )doesn't work.
I thought that this error message will be easy for you to understand: "java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first"
 
Last edited:
Upvote 0

wes58

Active Member
Licensed User
Longtime User
Solved!!
After you declare ActualItem, you have to add line to remove view from parent.

B4X:
        Dim ActualItem As B4XView  = pnl.GetView(0)
        ActualItem.RemoveViewFromParent
        ActualItem.SetColorAndBorder(pnl.Color, 0dip, xui.Color_Black, 0)
I have attached fixed example
 

Attachments

  • xCustomListView.zip
    15.6 KB · Views: 307
Upvote 0
Top