Hi,
I was looking for a method where I could dynamically adjust the layout of a listview and re-use the code for different projects.
While going through the forum I found an example by Eerel (imagedownloader) where he uses FX Scene builder and an imagedownloader sub to generate a listview with images and 1 label.
i;ve made some small adjustments to make it more dynamically. Basically you can specify any fxml scene with an imageview and unlimited number of labels.
1. Create a FXScene with an anchorpane. Within the anchorpane add an imageview and name the ID "ImageView1".
2. Add labels to the anchorpane and name the ID's of the labels. The ID names will be used as KEY in the MAPS
3. In your code create a list.
4. Create a map per item you want to add to the listview
Each map within the list should contain:
6. Call initilize of the fxlistviewdownloadimage with the list, FXML name and the keyname which holds the link to the image.
7. call CreateListview
8. The result is a listview, add it somewhere in your project.
By creating scene's with the scenebuilder and aligning the label ID"s with your code you are now able to create nice listviews on the spot without having to code a lot!
Main
FXListViewWithDownloadImage class
ImageDownloader class (Unchanged)
I was looking for a method where I could dynamically adjust the layout of a listview and re-use the code for different projects.
While going through the forum I found an example by Eerel (imagedownloader) where he uses FX Scene builder and an imagedownloader sub to generate a listview with images and 1 label.
i;ve made some small adjustments to make it more dynamically. Basically you can specify any fxml scene with an imageview and unlimited number of labels.
1. Create a FXScene with an anchorpane. Within the anchorpane add an imageview and name the ID "ImageView1".
2. Add labels to the anchorpane and name the ID's of the labels. The ID names will be used as KEY in the MAPS
3. In your code create a list.
4. Create a map per item you want to add to the listview
Each map within the list should contain:
- a link to an image
- 1 or more values for the labels. The key should correspond to the label id. The mapvalue will be written to the label.text.
6. Call initilize of the fxlistviewdownloadimage with the list, FXML name and the keyname which holds the link to the image.
7. call CreateListview
8. The result is a listview, add it somewhere in your project.
By creating scene's with the scenebuilder and aligning the label ID"s with your code you are now able to create nice listviews on the spot without having to code a lot!
Main
B4X:
#Region Project Attributes
#MainFormWidth: 400
#MainFormHeight: 400
#End Region
Sub Process_Globals
Private fx As JFX
Private MainForm As Form
Private ListView1 As ListView
'Dim Label1 As Label
Private SearchTitleView As FXListViewWithDownloadImage
Private lists As List
End Sub
Sub AppStart (Form1 As Form, Args() As String)
MainForm = Form1
MainForm.Show
lists.Initialize
Dim m As Map
m.Initialize
m.Put("link", "https://file.ac/v4kCnG6NWME//MgAxADIAMgA1ADQA.jpg")
m.Put("Title", "Walking Dead") 'make sure one label matches the key
m.Put("Publisher", "Image")
m.Put("Year", "2012")
m.Put("Volume", "Volume #1")
m.Put("blabla", "This will not become visible") 'as I do not have a label with ID blabla the value of this will not become visible.
lists.Add(m)
Dim m As Map
m.Initialize
m.Put("link", "https://file.ac/Nhw0QyOKfG4/MQA0ADQANgA5ADkA.jpg")
m.Put("Title", "Walking Dead 2 ")
m.Put("Publisher", "Image 2 ")
m.Put("Year", "2012")
m.Put("Volume", "Volume #2")
lists.Add(m)
Dim m As Map
m.Initialize
m.Put("link", "https://file.ac/sCnyZRlh41g/OAAyADIANAA4ADAA.jpg")
m.Put("Title", "Walking Dead 3 ")
m.Put("Publisher", "Image 3")
m.Put("Year", "2012 ")
m.Put("Volume", "Volume #3")
lists.Add(m)
SearchTitleView.Initialize(lists, "SearchResultTitle1", "link")
ListView1=SearchTitleView.CreateListview
MainForm.RootPane.AddNode(ListView1 ,0 ,0, 0, 0)
MainForm.RootPane.SetAnchors(ListView1, 0, 0, 0, 0) 'The ListView will fill the entire form
End Sub
FXListViewWithDownloadImage class
B4X:
Sub Class_Globals
Private fx As JFX
Private c_ListWithMaps As List
Private c_ListView1 As ListView
Private c_itemlayout As String
Private c_LinkKey As String
Dim ImageView1 As ImageView
End Sub
'Initializes the object. You can add parameters to this method if needed.
'Provide a list containing maps.
'One of the key's within the map should contain a link - specify the keyname as linkkey.
'The remaining keys will be matched to the label ID's of the FX Scene. The label.text will be updated with the value of the keymap.
'ItemLayout is the FXML document name.
'LinkKey is the name of the key containing the image link
Public Sub Initialize(l As List, ItemLayout As String, LinkKey As String)
c_ListWithMaps.Initialize
c_ListView1.Initialize("listView1")
c_ListWithMaps=l
c_itemlayout = ItemLayout
c_LinkKey=LinkKey
End Sub
Public Sub CreateListview() As ListView
BuildItems(c_ListWithMaps)
Return c_ListView1
End Sub
Public Sub UpdateListView(l As List, view As ListView) As ListView
c_ListWithMaps=l
c_ListView1=view
BuildItems(c_ListWithMaps)
Return c_ListView1
End Sub
Private Sub BuildItems (links As List)
Dim DownloadMap As Map
DownloadMap.Initialize
c_ListView1.Items.Clear
For Each m As Object In c_ListWithMaps
'the list may contain other types which we don't need now.
If m Is Map Then
Dim m1 As Map = m
Dim link As String
Dim p As AnchorPane
'init
p.Initialize("")
p.LoadLayout(c_itemlayout)
'get link
link = m1.Get(c_LinkKey)
'iterate through the remainder of the keys within the map
For Each k As String In m1.Keys
'ignore the link key as we already retrieved it! and loop through ALL nodes to find matching labels.
If k <> c_LinkKey Then
For Each n As Node In p.GetAllViewsRecursive
'check if the found node is a label and the key matches the id set to the label
If n Is Label And n.Id = k Then
'set the textvalue to the label
Dim label1 As Label = n
label1.Text=m1.Get(k)
n=label1
End If
Next
End If
Next
'Add the
c_ListView1.Items.Add(p)
DownloadMap.Put(ImageView1, link)
End If
Next
'download the images
Dim id As ImageDownloader
id.Initialize
id.Download(DownloadMap)
End Sub
ImageDownloader class (Unchanged)
B4X:
Sub Class_Globals
Private cache As Map
Private tasks As Map
Private ongoingTasks As Map
End Sub
Public Sub Initialize
tasks.Initialize
cache.Initialize
ongoingTasks.Initialize
End Sub
Public Sub Download (ImageViewsMap As Map)
For i = 0 To ImageViewsMap.Size - 1
tasks.Put(ImageViewsMap.GetKeyAt(i), ImageViewsMap.GetValueAt(i))
Dim link As String = ImageViewsMap.GetValueAt(i)
If cache.ContainsKey(link) Then
Dim iv As ImageView = ImageViewsMap.GetKeyAt(i)
iv.SetImage(cache.Get(link))
Else If ongoingTasks.ContainsKey(link) = False Then
ongoingTasks.Put(link, "")
Dim j As HttpJob
j.Initialize(link, Me)
j.Download(link)
End If
Next
End Sub
Sub JobDone(Job As HttpJob)
ongoingTasks.Remove(Job.JobName)
If Job.Success Then
Dim bmp As Image = Job.GetBitmap
cache.Put(Job.JobName, bmp)
If tasks.IsInitialized Then
For i = 0 To tasks.Size - 1
Dim link As String = tasks.GetValueAt(i)
If link = Job.JobName Then
Dim iv As ImageView = tasks.GetKeyAt(i)
iv.SetImage(bmp)
End If
Next
End If
Else
Log("Error downloading image: " & Job.JobName & CRLF & Job.ErrorMessage)
End If
If ongoingTasks.Size = 0 Then tasks.Clear
Job.Release
End Sub