B4J Question [New Title:] Combination of CLVExpandable and CLVDragger

Midimaster

Active Member
Licensed User
I try to understand how the xCustomListView and the different expanding classes like CLVExpandable or CLVDragger are working. How are you using such libraries and classes. Do you understand or only use the code as it is?

I want to combine both functionalities in one ListView and therefore I should understand what's happening. Perhaps somebody can teach me in depper understanding of Views and Items...

This is the central code of CLVExpandable:
B4X:
Private Sub ResizeItem (Index As Int, Collapse As Boolean)
    ' this is the item at this place
    Dim item As CLVItem = mCLV.GetRawListItem(Index)
   
    ' this is the view at this place
    Dim p As B4XView = item.Panel.GetView(0)
    If p.NumberOfViews = 0 Or (item.Value Is ExpandableItemData) = False Then Return
   
    ' create a new similar view
    Dim NewPanel As B4XView = xui.CreatePanel("")
    MoveItemBetweenPanels(p, NewPanel)
   
    ' find out current size
    Dim id As ExpandableItemData = item.Value
   
    ' why this?
    mCLV.sv.ScrollViewInnerPanel.AddView(NewPanel, 0, item.Offset, p.Width, id.ExpandedHeight)
   
    Dim NewSize As Int
    If Collapse Then NewSize = id.CollapsedHeight Else NewSize = id.ExpandedHeight
   
    'resize also the item
    mCLV.ResizeItem(Index, NewSize)
   
    'why this?
    NewPanel.SendToBack

    Sleep(mCLV.AnimationDuration)

    ' why copying it back to p?
    If p.Parent.IsInitialized Then ' can the parent be NOT initialized?
        MoveItemBetweenPanels(NewPanel, p)
    End If

    ' why this?
    NewPanel.RemoveViewFromParent

End Sub


Private Sub MoveItemBetweenPanels (Src As B4XView, Target As B4XView)
    Dim i As Int=0
    ' making a copy of the elements of the view
    Do While Src.NumberOfViews > 0
        i=i+1
       log("Iteration " & i )
        Dim v As B4XView = Src.GetView(0)
        v.RemoveViewFromParent
        Target.AddView(v, v.Left, v.Top, v.Width, v.Height)
    Loop
End Sub

I wrote some ideas as comment between the code. But am I right?

Why can I observe two iterations in Do-Loop of MoveItemBetweenPanels()? If I allow only one, I can see only a gray area. If I allow two all 5 elements are visible immediately.
Why is it so important to first copy all to a temp. View NewPanel and then copy it back to the other temp view p?

How is the structure of a ListView? Okay there are items. And each item contains a object ( a Panel?). But it looks like that the structure is much more complex.
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
Do you understand or only use the code as it is?
You don't need to understand the classes code in order to use them. Exactly like you use many other libraries without learning their code.

How is the structure of a ListView?
xCustomListView source is here: https://www.b4x.com/android/forum/t...-cross-platform-customlistview.84501/#content

Why can I observe two iterations in Do-Loop of MoveItemBetweenPanels()?
Number of iterations = number of views in the panel. Not very important.

Why is it so important to first copy all to a temp. View NewPanel and then copy it back to the other temp view p?
This is a good question. It is related to the way animations work. When a panel is animated, a snapshot of its layout is captured and this image is animated. The result in this case will be bad. The item layout will be stretched during animation and it will be ugly.
So instead, we add another panel, move the views to that panel and move the views back to the original panel.

' why copying it back to p? If p.Parent.IsInitialized Then ' can the parent be NOT initialized?
Many things can happen during the sleep call. The list can be cleared, the item can be removed. We need to check.
 
Upvote 0

Midimaster

Active Member
Licensed User
As I want to combine the Dragger and the Expandable features I have to understand what all these codelines are good for.

The sourcecode will help. The structure does not look that complicated I expected, when I looked at the CLVExpandable class code.

at the moment I try to find out more by commenting out there several lines and see what happens.

Can I say:

A CustomListView is combination of a List named Items and an object mBase as B4XView
First node is sv as a ScrollView
Now all Children are added twice: in the List for logic as a CLVItem and added asB4XView in ScrollViewInnerPanel for Design
When adding a new child, you always add at first a container Panel, where the new child is added as a sub element

This is the reason, why in the code there is often necessary to search for the parent? The Parent points to the container Panel
I can observe two iteration, because of this container panel. The first is the container (gray) the second is the users view

If I now would move elements not by removing/adding them, but by replacing the content, the CustomListView would refresh the display?

How is the relation between the List and the View? You search for the CLVItem, which you want to manipulate and call it f.e. temporary ReplaceItem. Its sub variable PANEL give you the relation to the views. Now you can remove the view from the container, destroy the item. And then you add a new item but use the old container for it's view. right?

I'm highly motivated to continue. Thanks for your support.

 
Upvote 0

Midimaster

Active Member
Licensed User
But this means, that the index that is returned by the click has always to correspond to the position of the CLVItems in the list
B4X:
Public Sub GetItemFromView(v As B4XView) As Int
    Dim parent = v As Object, current As B4XView
    Do While sv.ScrollViewInnerPanel <> parent
        current = parent
        parent = current.Parent
    Loop
    v = current
    Return v.Tag
End Sub

In my test code I try to move entries smoothly from one position to anouther. f.e. entry 5 slides to position between 6 and 7. So I slided the container panel with the tag "5". later the sequence was
B4X:
'before
A-B-C-D-E-F-G-H
1-2-3-4-5-6-7-8

'after moving 5 to 6
A-B-C-D-F-E-G-H
1-2-3-4-6-5-7-8

'insert new at 3
A-B-J-C-D-F-E-G-H
1-2-9-3-4-6-5-7-8
But you are using fixed positions for the container and sequence is always continous.
B4X:
'before
A-B-C-D-E-F-G-H
1-2-3-4-5-6-7-8

'after moving 5 to 6
A-B-C-D-F-E-G-H
1-2-3-4-5-6-7-8

'insert new at 3
A-B-J-C-D-F-E-G-H
1-2-3-4-6-5-7-8-9
Inserting one new item causes a renumbering of all the tags of the others.

This means that i cannot store the index for later operations. My entry can have a new index number after a operation.

I will have a look on the CLVDragger again. It is a good oportunity for me to learn more about B4XViews.

thank you
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
I will have a look on the CLVDragger again. It is a good oportunity for me to learn more about B4XViews.
There is no relation between CLVDragger and B4XView.

xCLV assumes that the order of the items in the list and in the scrollview is the same. If you change it directly then you need to make sure that you change them both.
 
Upvote 0

Midimaster

Active Member
Licensed User
am I right?
CLVExpandable is not a new View. It is only a class, which "knows" a CustomListView.
So I cannot write...
B4X:
Sub expandable_ItemClick(Index As Int, Value As Object)
    Log("click")
    expandable.ToggleItem(Index)
End Sub
instead of:
B4X:
Sub clv1_ItemClick (Index As Int, Value As Object)
    expandable.ToggleItem(Index)
End Sub

But I can cover methodes like this:
B4X:
    For i = 1 To 20
        Dim p As B4XView = CreateItem(Rnd(0xFF000000, 0xFFFFFFFF), "Item #" & i, 200dip)
        clv1.Add(p, expandable.CreateValue(p, i))
    Next
with that:
B4X:
    For i = 1 To 20
        Dim p As B4XView = CreateItem(Rnd(0xFF000000, 0xFFFFFFFF), "Item #" & i, 200dip)
        expandable.add(p,i)
    Next
as long as I add a new sub in the Class:
B4X:
public Sub Add(pnl As B4XView,value As Object )
    mCLV.Add(pnl, CreateValue(pnl, "some value"))
End Sub
 
Upvote 0

wes58

Active Member
Licensed User
Longtime User
am I right?
CLVExpandable is not a new View. It is only a class, which "knows" a CustomListView.
So I cannot write...
B4X:
Sub expandable_ItemClick(Index As Int, Value As Object)
    Log("click")
    expandable.ToggleItem(Index)
End Sub
instead of:
B4X:
Sub clv1_ItemClick (Index As Int, Value As Object)
    expandable.ToggleItem(Index)
End Sub

But I can cover methodes like this:
B4X:
    For i = 1 To 20
        Dim p As B4XView = CreateItem(Rnd(0xFF000000, 0xFFFFFFFF), "Item #" & i, 200dip)
        clv1.Add(p, expandable.CreateValue(p, i))
    Next
with that:
B4X:
    For i = 1 To 20
        Dim p As B4XView = CreateItem(Rnd(0xFF000000, 0xFFFFFFFF), "Item #" & i, 200dip)
        expandable.add(p,i)
    Next
as long as I add a new sub in the Class:
B4X:
public Sub Add(pnl As B4XView,value As Object )
    mCLV.Add(pnl, CreateValue(pnl, "some value"))
End Sub
Did you have a look at my examples: https://www.b4x.com/android/forum/threads/expanded-clv-inside-an-expanded-clv.121229/
I haven't used an CLVExpandable class in there
 
Upvote 0

Midimaster

Active Member
Licensed User
I was able to combine CLVExpandable and CLVDragger to a common class SortExpandListView. This is no new usable class or libryary nor a real working ListView for other users, but only my first study to understand whats possible with Listviews. Of course you can use the code for developing your own ListView or study the steps. The class is based on Erels CLVExpandable and CLVDragger and uses his code.

The new public Subs are:
B4X:
Public Sub OpenItem(lbl As B4XView)
'drops down or closes the additional area of an item'

Public Sub PickItem(MouseY As Int,lbl As B4XView)
'picks up one item and starts a dragging action'

Public Sub MoveItem(MouseY As Int,lbl As B4XView)
'handles the dragging during movent of the mouse'

Public Sub DropItem(lbl As B4XView)
'finish the dragging and sorts the list new'

1602146954091.png



The MAIN module is very short:
B4X:
#Region Project Attributes
    #MainFormWidth: 600
    #MainFormHeight: 600
#End Region

Sub Process_Globals
    Private fx As JFX,xui As XUI, MainForm As Form
    Private clv1 As CustomListView, Listview As SortExpandListView
    Private lblTitle, pnlTitle, pnlExpanded, Dragger As B4XView
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("1") 'Load the layout file.
    MainForm.Show
    Listview.Initialize(clv1)
    For i = 1 To 20
        Dim p As B4XView = CreateItem(0xFF005555, "Item #" & i,i,200dip)
        Listview.add(p,i)
    Next
End Sub


Sub CreateItem(clr As Int, Title As String, Nr As Int , ExpandedHeight As Int) As B4XView
    Dim p As B4XView = xui.CreatePanel("")
    p.SetLayoutAnimated(0, 0, 0, clv1.AsView.Width, ExpandedHeight)
    p.LoadLayout("Item")
    p.SetLayoutAnimated(0, 0, 0, p.Width, p.GetView(0).Height) 'resize it to the collapsed height
    lblTitle.Text = Title
    pnlTitle.Color = clr
    pnlExpanded.Color = clr
    Dragger.tag=Nr
    Return p
End Sub


Private Sub lblTitle_MouseClicked (EventData As MouseEvent)
    Listview.OpenItem(Sender)
End Sub


Private Sub Drag_MousePressed (EventData As MouseEvent)
    Listview.PickItem(EventData.Y,Sender)
End Sub


private Sub Drag_MouseDragged (EventData As MouseEvent)
    Dim lbl As B4XView= Sender
    Do While True
        Wait For (lbl) Drag_MouseDragged (EventData As MouseEvent)
        Listview.MoveItem(EventData.Y,lbl)
    Loop
End Sub


Private Sub Drag_MouseReleased (EventData As MouseEvent)
    Listview.DropItem(Sender)
End Sub
At the moment I dont know, how to handle the Events inside the class. So the Events are still in the MAIN and forward to the Class.

And this is the Class:
B4X:
'version 1.00
Sub Class_Globals
    Type ExpandableItemData (CollapsedHeight As Int, ExpandedHeight As Int, Value As Object, Expanded As Boolean)
    Private mCLV As CustomListView
    Private xui As XUI
    Private DragTop, DragY As Int
End Sub

Public Sub Initialize (CLV As CustomListView)
    mCLV=CLV
End Sub

public Sub Add(pnl As B4XView,value As Object )
    mCLV.Add(pnl, CreateValue(pnl, value))
End Sub

Public Sub CreateValue(pnl As B4XView, Value As Object) As ExpandableItemData
    Dim e As ExpandableItemData
    e.Initialize
    e.CollapsedHeight = pnl.GetView(0).Height
    e.ExpandedHeight = pnl.GetView(0).Height + pnl.GetView(1).Height
    e.Value = Value
    Return e
End Sub

Public Sub GetValue (Index As Int) As Object
    If mCLV.GetValue(Index) Is ExpandableItemData Then
        Dim e As ExpandableItemData = mCLV.GetValue(Index)
        Return e.Value
    End If
    Return mCLV.GetValue(Index)

End Sub

Private Sub ResizeItem (Index As Int, Collapse As Boolean)
    Dim item As CLVItem = mCLV.GetRawListItem(Index)
    Dim p As B4XView = item.Panel.GetView(0)
    If p.NumberOfViews = 0 Or (item.Value Is ExpandableItemData) = False Then Return
    Dim NewPanel As B4XView = xui.CreatePanel("")
    MoveItemBetweenPanels(p, NewPanel)
    Dim id As ExpandableItemData = item.Value
    id.Expanded = Not(Collapse)
    mCLV.sv.ScrollViewInnerPanel.AddView(NewPanel, 0, item.Offset, p.Width, id.ExpandedHeight)
    Dim NewSize As Int
    If Collapse Then NewSize = id.CollapsedHeight Else NewSize = id.ExpandedHeight
    mCLV.ResizeItem(Index, NewSize)
    NewPanel.SendToBack
    Sleep(mCLV.AnimationDuration)
    If p.Parent.IsInitialized Then
        MoveItemBetweenPanels(NewPanel, p)
    End If
    NewPanel.RemoveViewFromParent
End Sub


Private Sub MoveItemBetweenPanels (Src As B4XView, Target As B4XView)
    Do While Src.NumberOfViews > 0
        Dim v As B4XView = Src.GetView(0)
        v.RemoveViewFromParent
        Target.AddView(v, v.Left, v.Top, v.Width, v.Height)
    Loop
End Sub


Public Sub OpenItem(lbl As B4XView)
    Dim index As Int=mCLV.GetItemFromView(lbl)
    Dim data As ExpandableItemData = mCLV.GetValue(index)
    ResizeItem(index,data.Expanded)
End Sub


Public Sub PickItem(MouseY As Int,lbl As B4XView)
    Dim pnl As B4XView = mCLV.GetPanel(mCLV.GetItemFromView(lbl)).Parent
    pnl.GetView(0).SetColorAndBorder(xui.Color_Transparent, 3dip, 0xFF503ACD, 0)
    DragY  = MouseY+ lbl.Top + pnl.Top
    pnl.BringToFront
    DragTop = pnl.Top
End Sub


Public Sub MoveItem(MouseY As Int,lbl As B4XView)
    Dim pnl As B4XView     = mCLV.GetPanel(mCLV.GetItemFromView(lbl)).Parent
    CheckScrollViewOffset(pnl)
    Dim clvY As Int = MouseY + lbl.Top + pnl.Top
    Dim delta As Int = clvY - DragY
    pnl.Top = DragTop + delta
End Sub


Public Sub DropItem(lbl As B4XView)
    Dim index As Int = mCLV.GetItemFromView(lbl)
    Dim pnl As B4XView = mCLV.GetPanel(index).Parent
    Dim Offset As Int = pnl.Top '+ pnl.Height / 2
    Dim NewIndex As Int = mCLV.FindIndexFromOffset(Offset)
    Dim UnderlyingItem As CLVItem = mCLV.GetRawListItem(NewIndex)
    If Offset - UnderlyingItem.Offset > UnderlyingItem.Size / 2 Then NewIndex = NewIndex + 1
    Dim ActualItem As B4XView = pnl.GetView(0)
    Dim RawItem As CLVItem = mCLV.GetRawListItem(index)
    mCLV.RemoveAt(index)
    If NewIndex > index Then NewIndex = NewIndex - 1
    NewIndex = Max(0, Min(mCLV.Size, NewIndex))
    mCLV.InsertAt(NewIndex, ActualItem, RawItem.Value)
    mCLV.GetRawListItem(NewIndex).TextItem = RawItem.TextItem
End Sub



Private Sub CheckScrollViewOffset(pnl As B4XView)
    If pnl.Top < mCLV.sv.ScrollViewOffsetY Then
        mCLV.sv.ScrollViewOffsetY = Max(0, mCLV.sv.ScrollViewOffsetY - 10dip)
    Else If mCLV.sv.ScrollViewOffsetY + mCLV.sv.Height < pnl.Top + pnl.Height Then
        mCLV.sv.ScrollViewOffsetY = mCLV.sv.ScrollViewOffsetY + 10dip
    End If
End Sub

I add the complete B4J-project as a ZIP-file here:
 

Attachments

  • SortExpandListView.zip
    5.1 KB · Views: 299
Last edited:
Upvote 0
Top