B4J Question ComboBox, How to insert nodes with cell factory

jmon

Well-Known Member
Licensed User
Longtime User
Hello,

I am trying to add nodes to a combobox. The problem is that once I select an item in the list, then it disappears from the list. (Example of the problem included with this post)
cbx.jpg

The item is still selectable though. I have read an understood from this page where the problem lies. Basically the node is moved from the list view to the button and doesn't come back to the list view.
A warning about inserting Nodes into the ComboBox items list

ComboBox allows for the items list to contain elements of any type, including Node instances. Putting nodes into the items list is strongly not recommended. This is because the default cell factory simply inserts Node items directly into the cell, including in the ComboBox 'button' area too. Because the scenegraph only allows for Nodes to be in one place at a time, this means that when an item is selected it becomes removed from the ComboBox list, and becomes visible in the button area. When selection changes the previously selected item returns to the list and the new selection is removed.

There is a proposed work around that uses the cellFactory.
B4X:
The recommended approach, rather than inserting Node instances into the items list, is to put the relevant information into the ComboBox, and then provide a custom cell factory. For example, rather than use the following code:

ComboBox<Rectangle> cmb = new ComboBox<Rectangle>();
cmb.getItems().addAll(
    new Rectangle(10, 10, Color.RED),
    new Rectangle(10, 10, Color.GREEN),
    new Rectangle(10, 10, Color.BLUE));
You should do the following:


ComboBox<Color> cmb = new ComboBox<Color>();
cmb.getItems().addAll(
    Color.RED,
    Color.GREEN,
    Color.BLUE);

cmb.setCellFactory(new Callback<ListView<Color>, ListCell<Color>>() {
    @Override public ListCell<Color> call(ListView<Color> p) {
        return new ListCell<Color>() {
            private final Rectangle rectangle;
            {
                setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
                rectangle = new Rectangle(10, 10);
            }
           
            @Override protected void updateItem(Color item, boolean empty) {
                super.updateItem(item, empty);
               
                if (item == null || empty) {
                    setGraphic(null);
                } else {
                    rectangle.setFill(item);
                    setGraphic(rectangle);
                }
            }
      };
  }
});

I have tried several things, like creating events, a bit like that:
B4X:
    Dim e As Object = we.CreateEvent("javafx.event.EventHandler", "wv", False)
    we.RunMethod("setCellFactory", Array(e))
but I have the feeling that what I need is a way to create callbacks. Again I tried to create callback based on this page and the createEvent method of javaObject, but no luck...

I can't figure this one out. Does anyone know a way to implement that in B4J?

Thanks,
Jmon.
 

Attachments

  • ComboCustomCell.zip
    11.7 KB · Views: 399

jmon

Well-Known Member
Licensed User
Longtime User
Thanks.

I think I don't understand how to convert the example from oracle to B4J surely because I don't understand how to implement the callback.
In the example below, the callback is called only once at application start, but not for each cell. Also the MethodName returns "equals" and args() contains null.
B4X:
Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Main") 'Load the layout file.
    MainForm.Show

    Dim jocb As JavaObject = cb
    Dim e As Object = jocb.CreateEvent("javafx.util.Callback", "cb", False)
    jocb.RunMethod("setCellFactory", Array(e))   
   
    UpdateListItems
End Sub

Sub UpdateListItems
    cb.Items.Clear
    For i = 1 To 5
        cb.Items.Add(i)           
    Next
End Sub

Sub cb_Event(MethodName As String, Args() As Object) As Object
    Log("MethodName: " & MethodName)
   
    If Args.Length > 0 Then
        For Each a As Object In Args       
            Log("Args: " & a)
        Next
    End If   
   
'    Dim joCell As JavaObject
'    joCell.InitializeNewInstance("javafx.scene.control.cell.ComboBoxListCell", Null)
   
    Return True   
End Sub   

Sub btnRefresh_Action
    UpdateListItems
End Sub

From what I understand, I need to:
  1. set a callback "setCellFactory" for the combobox
  2. Fill the items with string values
  3. each time the callback is called, I get the ComboBoxListCell from the comboBox
  4. In ComboBoxListCell I set the graphic to my image
So it seems that with the script I have written, I set the callback to the comboBox, but maybe I need to set the callBack to each of the newly created items in the UpdateListItem sub? If so, how can I access the listview from the comboBox?
 
Upvote 0
Top