Convert ArrayList to Observable List for JavaFX program?

Pete picture Pete · Apr 14, 2016 · Viewed 22.8k times · Source

I am a semi-beginner Java programmer, learning Java FX from a variety of sources. In my program, I would like to create a ComboBox, and populate the choices with the toString() output of a series of objects from an ArrayList. Here, pizza toppings are defined as an object. They are created and stored in a PizzaMgr object, basically a glorified wrapper for an ArrayList:

public class Topping{

  private String name;

  public Topping(String a){
    this.name=a;
  }
  public String toString(){
    return this.name;
  }
}

//=================================================

import java.util.ArrayList;

public class PizzaMgr{

  private ArrayList<Topping> OrderedToppings;

  public PizzaMgr(){
      OrderedToppings = new ArrayList<Topping>();
      OrderedToppings.add(new Topping("Pepperoni"));
      OrderedToppings.add(new Topping("Mushrooms"));
      OrderedToppings.add(new Topping("Onions"));
  }

  public ArrayList<Topping> getList(){
      return OrderedToppings;
  }
}

So far, so good. But the hitch I hit is when I want a ComboBox to list all of those items in the PizzaMgr's ArrayList. Ideally, I'd like to use this ComboBox constructor:

ComboBox<T>(ObservableList<T> items)

The problem? How to extract all the ArrayList items into an Observable List? I've been reading up on Arraylists, Observable Lists, interfaces in general, but I can't figure out how to get this to work. I've read that an ArrayList is a Collection, and Observable List can be an interface to a Collection, so I thought I was home free. However, when I try to implement the ComboBox constructor:

import javafx.scene.control.*;

public class Menu{

  public static void main(String[] args){
      PizzaMgr m = new PizzaMgr();
      ComboBox<Topping> topMenu = new ComboBox<Topping>(m.getList());
  }
}

I get the compiler error:

Menu.java:18: error: incompatible types: ArrayList<Topping> cannot be converted to ObservableList<Topping>
            ComboBox<Topping> topMenu = new ComboBox<Topping>(m.getList());
                                                                       ^

So obviously my ArrayList isn't seen as an Observable List.

What stumps me is: How can I present my ArrayList to the ComboBox constructor, making it seem like an Observable List? Is it a syntax slight-of-hand? Or do I have to convert the ArrayList into another data structure in advance?

Many thanks, -RAO

Answer

James_D picture James_D · Apr 14, 2016

ObservableList is a sub interface (specialized version of) List. (ObservableList adds functionality for observing changes to the list.) ArrayList is a particular implementation of List, but is not an implementation of ObservableList. Hence you can pass an ArrayList anywhere a List is expected, but you cannot pass an ArrayList if an ObservableList is expected.

As something of an aside, note it's not really recommended to expose the implementation type, but you should really just expose the interface type:

import java.util.List;
import java.util.ArrayList;

public class PizzaMgr{

  private List<Topping> orderedToppings;

  public PizzaMgr(){
      orderedToppings = new ArrayList<Topping>();
      orderedToppings.add(new Topping("Pepperoni"));
      orderedToppings.add(new Topping("Mushrooms"));
      orderedToppings.add(new Topping("Onions"));
  }

  public List<Topping> getList(){
      return orderedToppings;
  }
}

To create an ObservableList from a regular List, you can use

ComboBox<Topping> topMenu 
    = new ComboBox<Topping>(FXCollections.observableList(m.getList()));

which creates a new ObservableList that "wraps" the array list you provide. I.e. calling get(index) or add(item) on the observable list simply delegates to the list you provide.

You could also do

ComboBox<Topping> topMenu 
    = new ComboBox<Topping>(FXCollections.observableArrayList(m.getList()));

which would create a new observable list and copy all the elements from the list you provide into it. So subsequently manipulating one list would not change the other.