I want to write a custom JavaFX custom control like DatePicker and ColorPicker, but instead of showing a Calendarview or color palette, i need to show a GridPane that contains a TableView and a TextField. I'm not sure how to make this generic to work with any data model, how i can set columns for the TableView.
for this i have extended from MVC classes (ComboBoxBase,ComboBoxPopupControl,ComboBoxBaseBehavior). But it seems to complicated to me on how to get this custom control implemented correctly.
Here is my code so far:
public class ComboBoxTablePopup<S> extends ComboBoxBase {
/***************************************************************************
* *
* Static properties and methods *
* *
**************************************************************************/
private static <T> StringConverter<T> defaultStringConverter() {
return new StringConverter<T>() {
#Override
public String toString(T t) {
return t == null ? null : t.toString();
}
#Override
public T fromString(String string) {
return (T) string;
}
};
}
/***************************************************************************
* *
* Constructors *
* *
**************************************************************************/
/**
* Creates a default ComboboxTablePopup instance with an empty
* {#link #itemsProperty() items} list and default
* {#link #selectionModelProperty() selection model}.
*/
public ComboBoxTablePopup() {
this(FXCollections.<S>emptyObservableList());
}
/**
* Creates a default ComboboxTablePopup instance with the provided items list and
* a default {#link #selectionModelProperty() selection model}.
*/
public ComboBoxTablePopup(ObservableList<S> items) {
setItems(items);
getStyleClass().add(DEFAULT_STYLE_CLASS);
setEditable(true);
setPromptText("Plz Search for a pirticular term");
}
private static final String DEFAULT_STYLE_CLASS = "combobox-table-popup";
private ReadOnlyObjectWrapper<TextField> editor;
private ObjectProperty<ObservableList<S>> items = new SimpleObjectProperty<ObservableList<S>>(this, "items");
public final void setItems(ObservableList<S> value) {
itemsProperty().set(value);
}
public final ObservableList<S> getItems() {
return items.get();
}
public ObjectProperty<ObservableList<S>> itemsProperty() {
return items;
}
// Converter
public ObjectProperty<StringConverter<S>> converterProperty() {
return converter;
}
private ObjectProperty<StringConverter<S>> converter =
new SimpleObjectProperty<StringConverter<S>>(this, "converter", ComboBoxTablePopup.<S>defaultStringConverter());
public final void setConverter(StringConverter<S> value) {
converterProperty().set(value);
}
public final StringConverter<S> getConverter() {
return converterProperty().get();
}// Create a symmetric (format/parse) converter with the default locale.
// Editor
public TextField getEditor() {
return editorProperty().get();
}
public ReadOnlyObjectProperty<TextField> editorProperty() {
if (editor == null) {
editor = new ReadOnlyObjectWrapper<TextField>(this, "editor");
editor.set(new ComboBoxListViewSkin.FakeFocusTextField());
}
return editor.getReadOnlyProperty();
}
#Override
protected Skin<?> createDefaultSkin() {
return new ComboBoxTablePopupSkin<>(this);
}
}
public class ComboBoxTablePopupBehavior extends ComboBoxBaseBehavior {
/**
* #param comboBox
*/
public ComboBoxTablePopupBehavior(ComboBoxBase comboBox) {
super(comboBox, COMBOBOX_TABLE_POPUP_BINDINGS);
}
/***************************************************************************
* *
* Key event handling *
* *
**************************************************************************/
protected static final List<KeyBinding> COMBOBOX_TABLE_POPUP_BINDINGS = new ArrayList<KeyBinding>();
static {
COMBOBOX_TABLE_POPUP_BINDINGS.add(new KeyBinding(KeyCode.ENTER, "togglePopup"));
COMBOBOX_TABLE_POPUP_BINDINGS.addAll(COMBO_BOX_BASE_BINDINGS);
}
}
public class ComboBoxTablePopupSkin extends ComboBoxPopupControl {
private ComboBoxTablePopup comboBoxTablePopup;
private ObservableList<S> comboboxTablePopupItems;
private TextField displayNode;
private TableView<S> tableViewPopupContent;
private ObservableList<S> tableViewItems;
public ComboBoxTablePopupSkin(ComboBoxTablePopup comboBoxTablePopup) {
super(comboBoxTablePopup, new ComboBoxTablePopupBehavior(comboBoxTablePopup));
this.comboBoxTablePopup = comboBoxTablePopup;
updateComboBoxTablePopupItems();
tableViewPopupContent = createTableView();
getChildren().add(tableViewPopupContent);
registerChangeListener(getEditor().textProperty(), "text");
}
#Override
protected Node getPopupContent() {
return this.tableViewPopupContent;
}
#Override
protected TextField getEditor() {
return ((ComboBoxTablePopup) getSkinnable()).getEditor();
}
#Override
protected StringConverter getConverter() {
return ((ComboBoxTablePopup) getSkinnable()).getConverter();
}
#Override
public Node getDisplayNode() {
if (displayNode == null) {
displayNode = getEditableInputNode();
displayNode.getStylesheets().add("ComboBoxTable-display-node");
updateDisplayNode();
}
displayNode.setEditable(comboBoxTablePopup.isEditable());
return displayNode;
}
#Override
protected void handleControlPropertyChanged(String p) {
if ("text".equalsIgnoreCase(p)) {
if (getEditor().textProperty().get().isEmpty()) {
hide();
} else
show();
} else {
super.handleControlPropertyChanged(p);
}
}
public void updateComboBoxTablePopupItems() {
comboboxTablePopupItems = comboBoxTablePopup.getItems();
comboboxTablePopupItems = comboboxTablePopupItems == null ? FXCollections.<S>emptyObservableList() : comboboxTablePopupItems;
}
private TableView<S> createTableView() {
final TableView<S> tableView = new TableView<>();
tableView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
tableView.setFocusTraversable(false);
tableView.getSelectionModel().selectedItemProperty().addListener(o -> {
int index = tableView.getSelectionModel().getSelectedIndex();
System.out.println("selected item changed");
});
return tableView;
}
}
Related
I have Created a custom control call it ComboBoxTablePopup extending Comboboxbase class. I have used a tableview as a popup content. Everything works fine,
update value, show popup, hide popup. After switching the focus from ComboBoxTablePopup to another control like a TextField or Spinner, it updates it self with null value.
So, I don't know what makes this happens. So here is my implementation on self executable class.
import com.sun.javafx.scene.control.behavior.ComboBoxBaseBehavior;
import com.sun.javafx.scene.control.behavior.KeyBinding;
import com.sun.javafx.scene.control.skin.ComboBoxListViewSkin;
import com.sun.javafx.scene.control.skin.ComboBoxPopupControl;
import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.WeakInvalidationListener;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.WeakListChangeListener;
import javafx.event.ActionEvent;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class TestComboboxTablePopup extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
ComboBoxTablePopupControl<dataModel> comboBoxTablePopup = new ComboBoxTablePopupControl<>();
TableColumn<dataModel, Integer> tcId = new TableColumn<>("Id");
TableColumn<dataModel, String> tcName = new TableColumn<>("Name");
tcId.setCellValueFactory(new PropertyValueFactory<dataModel, Integer>("id"));
tcName.setCellValueFactory(new PropertyValueFactory<dataModel, String>("name"));
comboBoxTablePopup.setColumns(FXCollections.observableArrayList(tcId, tcName));
comboBoxTablePopup.setItems(FXCollections.observableArrayList(
new dataModel(1, "Data Model object 1"),
new dataModel(2, "Data Model object 2"),
new dataModel(3, "Data Model object 3")
));
VBox vBox = new VBox(comboBoxTablePopup);
Scene scene = new Scene(vBox);
primaryStage.setScene(scene);
primaryStage.setWidth(400);
primaryStage.setHeight(300);
primaryStage.show();
}
public class dataModel {
private int id;
private String name;
public dataModel(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
private static <S> StringConverter<S> defaultStringConverter() {
return new StringConverter<S>() {
#Override
public String toString(S t) {
return t == null ? "abood fait" : t.toString();
}
#Override
public S fromString(String string) {
return null;
}
};
}
private class ComboBoxTablePopupControl<S> extends ComboBoxBase {
/***************************************************************************
* *
* Static properties and methods *
* *
**************************************************************************/
private static final String DEFAULT_STYLE_CLASS = "combobox-table-popup";
private ObjectProperty<ObservableList<S>> items = new SimpleObjectProperty<ObservableList<S>>(this, "items");
public final void setItems(ObservableList<S> value) {
itemsProperty().set(value);
}
public final ObservableList<S> getItems() {
return items.get();
}
public ObjectProperty<ObservableList<S>> itemsProperty() {
return items;
}
public ObjectProperty<StringConverter<S>> converterProperty() {
return converter;
}
private ObjectProperty<StringConverter<S>> converter =
new SimpleObjectProperty<StringConverter<S>>(this, "converter", defaultStringConverter());
public final void setConverter(StringConverter<S> value) {
converterProperty().set(value);
}
public final StringConverter<S> getConverter() {
return converterProperty().get();
}
// Editor
private ReadOnlyObjectWrapper<TextField> editor;
public final TextField getEditor() {
return editorProperty().get();
}
public final ReadOnlyObjectProperty<TextField> editorProperty() {
if (editor == null) {
editor = new ReadOnlyObjectWrapper<TextField>(this, "editor");
editor.set(new ComboBoxListViewSkin.FakeFocusTextField());
}
return editor.getReadOnlyProperty();
}
private
ObservableList<TableColumn<S, ?>> columns = FXCollections.observableArrayList();
public ObservableList<TableColumn<S, ?>> getColumns() {
return columns;
}
public void setColumns(ObservableList<TableColumn<S, ?>> columns) {
this.columns = columns;
}
/***************************************************************************
* *
* Constructors *
* *
**************************************************************************/
/**
* Creates a default ComboboxTablePopup instance with an empty
* {#link #itemsProperty() items} list and default
* {#link #selectionModelProperty() selection model}.
*/
public ComboBoxTablePopupControl() {
this(FXCollections.<S>emptyObservableList());
}
/**
* Creates a default ComboboxTablePopup instance with the provided items list and
* a default { selection model}.
*/
public ComboBoxTablePopupControl(ObservableList<S> items) {
setItems(items);
getStyleClass().add(DEFAULT_STYLE_CLASS);
setEditable(true);
valueProperty().addListener((observable, oldValue, newValue) -> {
System.out.println(newValue);
});
}
public ComboBoxTablePopupControl(ObservableList<S> items, ObservableList<TableColumn<S, ?>> columns) {
this(items);
this.columns = columns;
}
#Override
protected Skin<?> createDefaultSkin() {
return new ComboBoxTablePopupControlSkin<>(this);
}
}
public class ComboBoxTablePopupControlSkin<S> extends ComboBoxPopupControl {
private ComboBoxTablePopupControl comboBoxTablePopup;
private ObservableList<S> comboboxTablePopupItems;
private TableView<S> tableViewPopupContent;
private ObservableList<S> tableViewPopupItems;
private Predicate<S> predicate;
private final InvalidationListener itemsObserver;
private final ListChangeListener<S> tableViewItemsListener = new ListChangeListener<S>() {
#Override
public void onChanged(ListChangeListener.Change<? extends S> c) {
getSkinnable().requestLayout();
}
};
private final WeakListChangeListener<S> weakListViewItemsListener =
new WeakListChangeListener<S>(tableViewItemsListener);
public ComboBoxTablePopupControlSkin(ComboBoxTablePopupControl comboBoxTablePopup) {
super(comboBoxTablePopup, new ComboBoxBaseBehavior(comboBoxTablePopup, null));
this.comboBoxTablePopup = comboBoxTablePopup;
updateComboBoxTablePopupItems();
itemsObserver = observable -> {
updateComboBoxTablePopupItems();
updateTableViewItems();
};
this.comboBoxTablePopup.itemsProperty().addListener(new WeakInvalidationListener(itemsObserver));
tableViewPopupContent = createTableView();
tableViewPopupContent.setManaged(false);
getChildren().add(tableViewPopupContent);
updateTableViewItems();
registerChangeListener(comboBoxTablePopup.converterProperty(), "CONVERTER");
registerChangeListener(comboBoxTablePopup.itemsProperty(), "ITEMS");
registerChangeListener(comboBoxTablePopup.valueProperty(), "VALUE");
registerChangeListener(comboBoxTablePopup.editorProperty(), "EDITABLE");
}
private void updateTableViewItems() {
this.tableViewPopupItems = comboBoxTablePopup.getItems();
this.tableViewPopupContent.setItems(this.tableViewPopupItems);
if (tableViewPopupItems != null) {
tableViewPopupItems.removeListener(weakListViewItemsListener);
}
this.tableViewPopupItems = comboboxTablePopupItems;
tableViewPopupContent.setItems(tableViewPopupItems);
if (tableViewPopupItems != null) {
tableViewPopupItems.addListener(weakListViewItemsListener);
}
getSkinnable().requestLayout();
}
public void updateComboBoxTablePopupItems() {
comboboxTablePopupItems = comboBoxTablePopup.getItems();
comboboxTablePopupItems = comboboxTablePopupItems == null ? FXCollections.<S>emptyObservableList() : comboboxTablePopupItems;
}
private TableView<S> createTableView() {
final TableView<S> tableView = new TableView<>();
tableView.setId("table-view");
tableView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
tableView.setFocusTraversable(false);
for (TableColumn tblColumn : tableColumns()) {
tableView.getColumns().add(tblColumn);
}
tableView.getSelectionModel().selectedItemProperty().addListener(o -> {
S selectedItem = tableView.getSelectionModel().getSelectedItem();
comboBoxTablePopup.setValue(selectedItem);
});
tableView.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ENTER ||
e.getCode() == KeyCode.SPACE) {
S selectedItem = tableView.getSelectionModel().getSelectedItem();
comboBoxTablePopup.setValue(selectedItem);
comboBoxTablePopup.hide();
}
});
return tableView;
}
private ObservableList<TableColumn> tableColumns() {
return ((ComboBoxTablePopupControl) getSkinnable()).getColumns();
}
#Override
protected Node getPopupContent() {
return this.tableViewPopupContent;
}
#Override
protected TextField getEditor() {
return ((ComboBoxTablePopupControl) getSkinnable()).getEditor();
}
#Override
protected StringConverter<S> getConverter() {
return ((ComboBoxTablePopupControl) getSkinnable()).getConverter();
}
#Override
public Node getDisplayNode() {
Node displayNode;
displayNode = getEditableInputNode();
updateDisplayNode();
return displayNode;
}
#Override
protected void handleControlPropertyChanged(String p) {
if ("VALUE".equals(p)) {
updateDisplayNode();
System.out.println(comboBoxTablePopup.getValue());
comboBoxTablePopup.fireEvent(new ActionEvent());
} else if ("CONVERTER".equals(p)) {
updateDisplayNode();
System.out.println("Conveter peroptery");
} else if ("ITEMS".equals(p)) {
updateComboBoxTablePopupItems();
updateTableViewItems();
} else if ("EDITOR".equals(p)) {
getEditableInputNode();
} else
super.handleControlPropertyChanged(p);
}
}
}
If someone still has trouble with this issue, here is a workaround for a choicebox. I assume you already have a table with columns and an EventHandler. This example is not tested with any editable (isEditable(true)) object.
First set a custom Factory and an EventHandler for setOnEditStart
yourColumn.setCellFactory(value -> new CustomEditFactory(yourObsList));
In your CustomEditFactory class you have to extend TreeTableCell<KnotenObs, String> and #Override four methods
startEdit()
#Override
public void startEdit() {
super.startEdit();
choiceBox.getSelectionModel().select(getItem());
oldValue = choiceBox.getSelectionModel().getSelectedItem();
setGraphic(choiceBox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
commitEdit()
#Override
public void commitEdit(String newValue) {
if(newValue == null) {
super.commitEdit(oldValue);
} else {
super.commitEdit(newValue);
}
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
cancelEdit()
#Override
public void cancelEdit() {
super.cancelEdit();
if(getItem() == null) {
setText(oldValue);
} else {
setText(getItem());
}
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
updateItem()
#Override
public void updateItem(String item, boolean empty) {
if(item == null) {
super.updateItem(oldValue, empty);
setText(oldValue);
} else {
super.updateItem(item, empty);
setText(item);
}
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
I want to add the toStrings from items in an Arraylist of Objects to a Jlist through a for loop. I tried this.
for (int i = 0; i < customer.slist.size(); i++) {
((DefaultListModel) checkOutList.getModel()).addElement(customer.slist.toString());//slist is an array of objects.
}
But it didnt work. Also NetBeans tells me .addElement()is not a method for a JList when I tried
checkOutList.addElement("anything")
instead of
customer.slist.toString()
try:
customer.slist.get(i).toString();
in your code.
I have this implementation:
This is custom JList:
#SuppressWarnings("serial")
public class JListCustomered<T extends JListPresentedInterface> extends JList<T>{
private List<T> list;
private List<T> selectedList;
public JListCustomered(List<T> list){
super();
this.list=list;
setCellRenderer(new JListRenderer());
setModel(new ListModel());
}
public void loadData(List<T> list){
getSelectedList().clear();
this.list=list;
setModel(new ListModel());
}
/** {#link JListCustomered#list} */
public List<T> getList() {
if(list==null)
list=new ArrayList<T>();
return list;
}
/** {#link JListCustomered#selectedList} */
public List<T> getSelectedList() {
if(selectedList==null)
selectedList=new ArrayList<T>();
return selectedList;
}
public class JListRenderer extends JLabel implements ListCellRenderer<T>{
JSeparator separator;
private JListRenderer(){
separator=new JSeparator(JSeparator.HORIZONTAL);
setOpaque(true);
}
/** #see javax.swing.ListCellRenderer#getListCellRendererComponent(javax.swing.JList, java.lang.Object, int, boolean, boolean) */
public Component getListCellRendererComponent(JList<? extends T> list, T value, int index, boolean isSelected, boolean cellHasFocus) {
if(value.isSeparator()){
return separator;
}
List<T> selected = getSelectedList();
if (isSelected) {
if (!selected.contains(value)) {
getSelectedList().add(value);
}
this.setBackground(getSelectionBackground());
} else {
if (selected.contains(value)) {
getSelectedList().remove(value);
}
setBackground(Color.WHITE);
}
setFont(list.getFont());
setText(value.getJListName());
return this;
}
}
private class ListModel implements javax.swing.ListModel<T>{
/** #see javax.swing.ListModel#getSize() */
public int getSize() {
return getList().size();
}
/** #see javax.swing.ListModel#getElementAt(int) */
public T getElementAt(int index) {
return getList().get(index);
}
/** #see javax.swing.ListModel#addListDataListener(javax.swing.event.ListDataListener) */
public void addListDataListener(ListDataListener l) {
}
/** #see javax.swing.ListModel#removeListDataListener(javax.swing.event.ListDataListener) */
public void removeListDataListener(ListDataListener l) {
}
}
}
below interface for object, which you want add to JList:
public interface JListPresentedInterface {
public String getJListName();
public boolean isSeparator();
}
you can reaload data on JList using method loadData, get selected list getList
I am developing an app in which i am adding and deleting fields based on setting flag to true or false.but what i am trying to do is if i click on field that particular field should expand while others are collapsed(even if it is expanded)
i googled it but i didnt get solution please help me i am new to blackberry java
i have used below code
public final class MyScreen extends MainScreen implements
FieldChangeListener
{
/**
* Creates a new MyScreen object
*/
private VerticalFieldManager main_manager;
private HorizontalFieldManager parentNodes;
private LabelField parent_lables[];
private Bitmap bitmap,upbitmap;
private BitmapField bitmap_field[];
private VerticalFieldManager submanager[];
private int sizeOfParentNodes=3;
private int sizeOfChildNodes=5;
private static boolean flag[];
public MyScreen()
{
// Set the displayed title of the screen
bitmap=Bitmap.getBitmapResource("arrow.png");
upbitmap=Bitmap.getBitmapResource("uparrow.png");
main_manager=new
VerticalFieldManager(Manager.VERTICAL_SCROLL|VERTICAL_SCROLLBAR){
protected void sublayout(int maxWidth, int maxHeight) {
super.sublayout(Display.getWidth(), Display.getHeight());
setExtent(Display.getWidth(), Display.getHeight());
};
};
parent_lables=new LabelField[sizeOfParentNodes];
flag=new boolean[sizeOfParentNodes];
submanager=new VerticalFieldManager[sizeOfParentNodes];
bitmap_field=new BitmapField[sizeOfParentNodes];
for(int i=0;i<sizeOfParentNodes;i++)
{
submanager[i]=new VerticalFieldManager();
updateGUI(i);
main_manager.add(submanager[i]);
}
add(main_manager);
}
public void fieldChanged(Field field, int context) {
// TODO Auto-generated method stub
synchronized (UiApplication.getEventLock()) {
for(int i=0;i<sizeOfParentNodes;i++)
{ if(field==parent_lables[i])
{
if(flag[i]==true){
flag[i]=false;
submanager[i].deleteAll();
updateGUI(i);
parent_lables[i].setFocus();
}else{
flag[i]=true;
bitmap_field[i].setBitmap(upbitmap);
submanager[i].invalidate();
}
}
}
}
}
public void updateGUI(int index)
{
parentNodes=new HorizontalFieldManager(USE_ALL_WIDTH);
bitmap_field[index]=new BitmapField(bitmap);
parentNodes.add(bitmap_field[index]);
parent_lables[index]=new LabelField("Day"+index,Field.FOCUSABLE){
protected boolean navigationClick(int status, int time) {
fieldChangeNotify(1);
return true;
};
};
parentNodes.add(parent_lables[index]);
parent_lables[index].setChangeListener(this);
submanager[index].add(parentNodes);
}
}
I knocked this together, and will likely need some tweaks by you to get it exactly how you want it, but it should be something you can work from. Assuming I've understood your question correctly.
You'll need to create a base field, a helper able to manage a list of them, and a callback. The fact that you need a base field is the biggest downfall, because it immediately excludes all other widgets you have, as they need to be made from scratch with the paint method. You can use a VerticalFieldManager instead of field by extending VerticalFieldManager instead of Field.
On to the java classes.
My CollapsableField.java looks as follows:
public abstract class CollapsableField extends Field
{
// We make use of a different listener than the FieldChangeListener, since you can only attach one and we will most likely want to do so, we can't "occupy" the listener.
private CollapseListener listener;
private boolean collapsed;
protected int collapsedWidth;
protected int collapsedHeight;
public CollapsableField()
{
collapsed = true;
// Field is completely collapsed by default
collapsedWidth = 0;
collapsedHeight = 0;
}
public void setCollapseListener(CollapseListener listener)
{
this.listener = listener;
}
public final boolean isCollapsed()
{
return collapsed;
}
public final void collapse()
{
this.collapsed = true;
if (listener != null)
{
listener.onCollapse(this);
}
fieldChangeNotify(0); // Notify that the field has changed, so that attached field change listeners will fire
updateLayout(); // re-call layout
}
public final void expand()
{
this.collapsed = false;
if (listener != null)
{
listener.onExpand(this);
}
fieldChangeNotify(0); // Notify that the field has changed, so that attached field change listeners will fire
updateLayout(); // re-call layout
}
protected void layout(int width, int height)
{
if (collapsed)
{
// Set dimensions to collapsed
setExtent(collapsedWidth, collapsedHeight);
}
else
{
// Set dimensions to what the extending class specified
setExtent(width, height);
}
}
protected final void paint(Graphics graphics)
{
if (collapsed)
{
paintCollapsed(graphics);
}
else
{
paintExpanded(graphics);
}
}
protected abstract void paintCollapsed(Graphics graphics);
protected abstract void paintExpanded(Graphics graphics);
}
The Group then takes a list of these, applies a listener to each field as it's added. When a field signals that it has expanded, the group will tell all other fields to collapse themselves.
CollapsableGroup.java:
public class CollapsableGroup
{
private Vector fields;
private CollapseListener listener;
public CollapsableGroup()
{
fields = new Vector();
listener = new CollapseListener()
{
public void onExpand(CollapsableField source)
{
for (int i = 0; i < fields.size(); i++)
{
CollapsableField field = (CollapsableField) fields.elementAt(i);
if ((!field.isCollapsed()) && (field != source))
{
field.collapse();
}
}
}
public void onCollapse(CollapsableField source)
{
// Don't need to handle this. Method is here just for completeness
}
};
}
public void add(CollapsableField field)
{
field.setCollapseListener(listener);
fields.addElement(field);
}
public void remove(CollapsableField field)
{
field.setCollapseListener(null);
fields.removeElement(field);
}
/**
* Returns the currently expanded field. Returns <b>null</b> if all fields are collapsed.
*
* #return
*/
public CollapsableField getExpandedField()
{
for (int i = 0; i < fields.size(); i++)
{
CollapsableField field = (CollapsableField) fields.elementAt(i);
if (!field.isCollapsed())
{
return field;
}
}
return null;
}
public void expand(CollapsableField field)
{
field.expand(); // Listeners should take care of the rest
}
public void collapseAll()
{
for (int i = 0; i < fields.size(); i++)
{
CollapsableField field = (CollapsableField) fields.elementAt(i);
if (!field.isCollapsed())
{
field.collapse();
}
}
}
}
And finally the listener interface.
CollapseListener.java:
interface CollapseListener
{
public void onExpand(CollapsableField source);
public void onCollapse(CollapsableField source);
}
Those three classes should be all that you need. The classes that follow are my example/test classes.
TestLabel.java is an example of an extended class:
public class TestLabel extends CollapsableField
{
private String text;
private String collapsedText;
public TestLabel(String text, String collapsedText)
{
this.text = text;
this.collapsedText = collapsedText;
// Tells the layout method to collapse to the size of this text
collapsedWidth = getFont().getAdvance(collapsedText);
collapsedHeight = getFont().getHeight();
}
public int getPreferredWidth()
{
return getFont().getAdvance(text);
}
public int getPreferredHeight()
{
return getFont().getHeight();
}
protected void layout(int width, int height)
{
super.layout(getPreferredWidth(), getPreferredHeight());
}
protected void paintCollapsed(Graphics graphics)
{
// Paints only the collapsedText
graphics.drawText(collapsedText, 0, 0);
}
protected void paintExpanded(Graphics graphics)
{
// Paints the full Text
graphics.drawText(text, 0, 0);
}
protected boolean touchEvent(TouchEvent message)
{
// Toggle on mouse press
if (message.getEvent() == TouchEvent.CLICK)
{
if (isCollapsed())
{
expand();
}
else
{
collapse();
}
return true;
}
return super.touchEvent(message);
}
}
The following screen contains some of the fields to show that both the widgets themselves and the group can manipulate the fields.
MyScreen.java:
public final class MyScreen extends MainScreen
{
public MyScreen()
{
// Set the displayed title of the screen
setTitle("MyTitle");
final CollapsableGroup group = new CollapsableGroup();
final TestLabel label1 = new TestLabel("Label1", "L1");
label1.setBackground(BackgroundFactory.createSolidBackground(0x999999));
group.add(label1);
final TestLabel label2 = new TestLabel("Label2", "L2");
label2.setBackground(BackgroundFactory.createSolidBackground(0xBBBBBB));
group.add(label2);
final TestLabel label3 = new TestLabel("Label3", "L3");
label3.setBackground(BackgroundFactory.createSolidBackground(0xDDDDDD));
group.add(label3);
ButtonField collapseAll = new ButtonField("Collapse All")
{
protected boolean navigationClick(int status, int time)
{
group.collapseAll();
return true;
}
};
add(collapseAll);
ButtonField expand1 = new ButtonField("Expand1")
{
protected boolean navigationClick(int status, int time)
{
group.expand(label1);
return true;
}
};
add(expand1);
ButtonField expand2 = new ButtonField("Expand2")
{
protected boolean navigationClick(int status, int time)
{
group.expand(label2);
return true;
}
};
add(expand2);
ButtonField expand3 = new ButtonField("Expand3")
{
protected boolean navigationClick(int status, int time)
{
group.expand(label3);
return true;
}
};
add(expand3);
add(label1);
add(label2);
add(label3);
}
}
First, let me discuss the data model :
I've compiled an HL7 definition in an tree-like object model, with 3 levels : Segment, Element and Subelement. The segment contains a List, and the Element contains a List. The relationship between those is kinda like "Continent-Country-City"
Second, i've created a GUI component composed of 3 combobox, each having a different ComboBoxModel accessing the object model described earlier.
I've applied the Observer pattern on the ComboBoxModel, so that each model is aware of which List<> it has to access :
- the HL7DefinitionElementModel observes the HL7DefinitionSegmentModel
- the HL7DefinitionSubelementModel observes the HL7DefinitionElementModel
When you pick a value in the first combobox (the Segment), the HL7DefinitionElementModel updates and store which Segment have been picked. The data is correctly stored. The first value is correctly displayed in the "closed" JComboBox.
But when you try to open that JComboBox (the Element), if the number of elements is too big (around 12), the items in the invoked Popup Menu are blank.
I'll gladly post any code.
I could send the entire project if needed, but haven't been able to reduce it to a SSCEE.
EDIT : here is an illustration of the problem
(http://imgur.com/AiJ8mu0)
EDIT : here is an SSCCE :
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
import java.util.Vector;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.event.ListDataListener;
public class Application extends JFrame {
/**
*
*/
private static final long serialVersionUID = -2517616123689799182L;
public Application() {
add(new HL7FieldSelector());
}
public static void main(String[] args) {
Application a = new Application();
a.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
a.setSize(new Dimension(400, 100));
a.setVisible(true);
}
}
class HL7FieldSelector extends JPanel implements ActionListener {
/**
*
*/
private static final long serialVersionUID = 6135074279269049773L;
private JComboBox<HL7DefinitionSegment> segment;
private JComboBox<HL7DefinitionElement> element;
private HL7DefinitionSegmentModel segmentModel;
private HL7DefinitionElementModel elementModel;
public HL7FieldSelector() {
segment = new JComboBox<HL7DefinitionSegment>();
element = new JComboBox<HL7DefinitionElement>();
segmentModel = new HL7DefinitionSegmentModel();
elementModel = new HL7DefinitionElementModel();
segmentModel.addObserver(elementModel);
segment.setModel(segmentModel);
element.setModel(elementModel);
segment.addActionListener(this);
element.addActionListener(this);
initUI();
}
private void initUI() {
this.setLayout(new GridLayout(1, 3));
this.add(segment);
this.add(element);
this.setMinimumSize(new Dimension(500, 30));
this.setPreferredSize(new Dimension(500, 30));
this.setMaximumSize(new Dimension(500, 30));
segment.setMaximumRowCount(10);
element.setMaximumRowCount(10);
}
#Override
public void actionPerformed(ActionEvent arg0) {
setVisible(!isVisible());
setVisible(!isVisible());
}
}
class HL7DefinitionSegment implements Comparable<HL7DefinitionSegment>, Serializable {
/**
*
*/
private static final long serialVersionUID = 7922798285885405647L;
private List<HL7DefinitionElement> elements;
private String name;
public HL7DefinitionSegment(String name, ArrayList<HL7DefinitionElement> elements) {
this.name = name;
this.elements = elements;
}
public HL7DefinitionSegment() {
elements = new Vector<HL7DefinitionElement>();
}
public boolean addElement(HL7DefinitionElement element) {
return elements.add(element);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<HL7DefinitionElement> getElements() {
return elements;
}
#Override
public String toString() {
return this.name;
}
#Override
public int compareTo(HL7DefinitionSegment segment) {
return name.compareTo(segment.getName());
}
}
class HL7DefinitionElement implements Comparable<HL7DefinitionElement>, Serializable {
/**
*
*/
private static final long serialVersionUID = -5344721929162039227L;
private String name;
private String number;
public HL7DefinitionElement(String name, String number) {
this.name = name;
this.number = number;
}
public HL7DefinitionElement() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
#Override
public String toString() {
return number + " " + name;
}
#Override
public int compareTo(HL7DefinitionElement element) {
return number.compareTo(element.getNumber());
}
}
class HL7DefinitionModel implements Serializable {
/**
*
*/
private static final long serialVersionUID = -8180582371776223436L;
private List<HL7DefinitionSegment> segments;
private static HL7DefinitionModel _instance;
public HL7DefinitionModel(Vector<HL7DefinitionSegment> segments) {
this.segments = segments;
}
public HL7DefinitionModel() {
segments = new Vector<HL7DefinitionSegment>();
}
public boolean addSegment(HL7DefinitionSegment segment) {
return segments.add(segment);
}
public List<HL7DefinitionSegment> getSegments() {
return segments;
}
public void setSegments(List<HL7DefinitionSegment> segments) {
this.segments = segments;
}
public static HL7DefinitionModel getInstance() {
if (_instance == null) {
_instance = new HL7DefinitionModel();
for (int i = 0; i < 2; i++) {
HL7DefinitionSegment s = new HL7DefinitionSegment();
s.setName("SEG" + i);
for (int j = 0; j < 20; j++) {
s.addElement(new HL7DefinitionElement("" + j + j + j, "" + j));
}
_instance.addSegment(s);
}
}
return _instance;
}
}
class HL7DefinitionSegmentModel extends Observable implements javax.swing.ComboBoxModel<HL7DefinitionSegment> {
private HL7DefinitionModel model;
private HL7DefinitionSegment selectedItem;
public HL7DefinitionSegmentModel() {
this.model = HL7DefinitionModel.getInstance();
this.selectedItem = getElementAt(0);
}
public void addListDataListener(ListDataListener l) {}
public void removeListDataListener(ListDataListener l) {}
#Override
public HL7DefinitionSegment getElementAt(int index) {
return model.getSegments().get(index);
}
#Override
public int getSize() {
return model.getSegments().size();
}
#Override
public Object getSelectedItem() {
return selectedItem;
}
#Override
public void setSelectedItem(Object anItem) {
this.selectedItem = (HL7DefinitionSegment) anItem;
setChanged();
notifyObservers();
}
}
class HL7DefinitionElementModel extends Observable implements javax.swing.ComboBoxModel<HL7DefinitionElement>, Observer {
private HL7DefinitionSegment segment;
private HL7DefinitionElement selectedItem;
public HL7DefinitionElementModel() {
this.segment = null;
}
#Override
public void update(Observable o, Object arg1) {
if (o instanceof HL7DefinitionSegmentModel) {
segment = (HL7DefinitionSegment) ((HL7DefinitionSegmentModel) o).getSelectedItem();
setSelectedItem(getElementAt(0));
}
}
public void addListDataListener(ListDataListener l) {}
public void removeListDataListener(ListDataListener l) {}
#Override
public HL7DefinitionElement getElementAt(final int index) {
return segment.getElements().get(index);
}
#Override
public int getSize() {
return (segment != null ? segment.getElements().size() : 0);
}
#Override
public Object getSelectedItem() {
return selectedItem;
}
#Override
public void setSelectedItem(Object anItem) {
this.selectedItem = (HL7DefinitionElement) anItem;
setChanged();
notifyObservers();
}
}
To reproduce the error :
- Launch the application
- Click on second combobox
- Click on the first, select option 2
- Click on second combobox
- Notice how the list is empty
if you do :
- Launch the application
- Click on the first combobox, select option 2
- Click on second combobox
- Notice how the list is populated
EDIT : Here is something else that I've found : The popup menu is only blank if there is more elements in it than in the initial "fill". That is, if the combobox is filled with 20 elements in the first pick, on the next picks the popup menu will be blank if the number of elements is 21 or more, displayed if the number of elements is 20 or less.
I am a begginer with java programming.
I am trying to make a JTable that reads collection set hashSet, and refreshes eachtime set is changed, also want to make autorefresh every 3 sec repaint on JPanel where table will be on (which works in original program).
Also Class Mats changes Boolean take over time, i want to change color its writhing Mats.name in JTable depending on the value Mats.take in that mat in set which is the reason for autorefresh.
This is a sample for table its remakeing table from scrach with each time new line is added throws java.lang.NullPointerException.
I simply do not see why?
Thx for the help in advance
public class Table extends JFrame {
private static JTable table;
private static JButton addbuttin;
private static TableModel model;
private static Set<Mats> set = null;
public static String colorString(String str, Mats a) {
if (a.getTake().equals(new Boolean(true))) {
return "<html><body><font color=red>" + str
+ "</font></body></html>";
} else {
return "<html><body><font color=blue>" + str
+ "</font></body></html>";
}
}
public static void main(String[] argv) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addbuttin = new JButton("Dodaj");
Mats a = new Mats(10, 10, 5, "WOOD", true);
Mats b = new Mats(10, 12, 5, "WOOD", false);
set = new HashSet<Mats>();
set.add(a);
set.add(b);
addbuttin.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
Mats c = new Mats(10, 12, 6, "WOOD", true);
set.add(c);
}
});
JPanel p = new JPanel();
model = new DefaultTableModel() {
public boolean isCellEditable(int rowIndex, int vColIndex) {
return false;
}
};
/**here is java.lang.NullPointerException*/
table.getModel().addTableModelListener(new TableModelListener() {
#Override
public void tableChanged(TableModelEvent e) {
if (e.equals(TableModelEvent.INSERT)) {
model = new DefaultTableModel();
addColumns();
adddata(set);
}
}
});
addColumns();
adddata(set);
table = new JTable(model);
JScrollPane scrollPane = new JScrollPane(table,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
p.add(scrollPane, BorderLayout.CENTER);
p.add(addbuttin, BorderLayout.EAST);
p.setSize(500, 400);
frame.add(p);
frame.setSize(600, 600);
frame.setVisible(true);
}
private static void addColumns() {
((DefaultTableModel) model).addColumn("NAME");
}
private static void adddata(Set<Mats> set) {
for (Iterator iterator = set.iterator(); iterator.hasNext();) {
Mats mats = (Mats) iterator.next();
String n = colorString(mats.getName(), mats);
((DefaultTableModel) model).insertRow(model.getRowCount(), new Object[] { n });
}
}
}
This is Class Mats simplified
public class Mats implements Comparable<Mats> {
private String name;
private Boolean take;
/**
*
*/
public Mats() {
}
/**
*
* #param name
* #param uzet
*/
public Mats(String name, boolean take) {
this.name = name.toUpperCase();
this.take = take;
taken();
}
/**
*
* #param Material
*/
public Mats(Mats Material) {
this.name = Material.getName().toUpperCase();
this.take = Material.getTake();
Material.taken();
}
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #param name
* the name to set
*/
public void setName(String name) {
this.name = name.toUpperCase();
}
/**
* #param take
* the take to set
*/
public void setTake(Boolean take) {
this.take = take;
}
/**
* #return the take
*/
public Boolean getTake() {
return take;
}
/**
*
* #return
*/
#Override
public String toString() {
return "Material: ".toUpperCase() + getName() + "\n";
}
/**
*
* #param obj
* #return
*/
#Override
public boolean equals(Object obj) {
Mats a = (Mats) obj;
if (obj == null) {
return false;
}
if (a.getName() == this.getName()) {
return true;
}
return false;
}
/**
*
* #return
*/
#Override
public int hashCode() {
int hash = 3;
hash = 83 * hash + this.getX();
hash = 83 * hash + this.getY();
hash = 83 * hash
+ (this.getName() != null ? this.getName().hashCode() : 0);
return hash;
}
/**
*
*/
public void taken() {
MatRespawn s = new MatRespawn(1000, take);
s.start();
}
#Override
public int compareTo(Mats o) {
Integer a = this.getName();
if (a.compareTo(o.getName()) != 0)
return a.compareTo(o.getName());
return 0;
}
}
Class MatRespawn which changes Mats.take
public class MatRespawn extends Thread {
private int time;
private Boolean taken;
public MatRespawn(int time, Boolean take) {
this.time = time;
this.taken = take;
}
public int getTime() {
return time;
}
public void setTime(int time) {
this.time = time;
}
public Boolean getTaken() {
return taken;
}
public void setTaken(Boolean taken) {
this.taken = taken;
}
#Override
public void run() {
try {
while (true) {
if (taken.equals(new Boolean(true))) {
sleep(time * 10);
//JOptionPane.showMessageDialog(null, "dostupnost: " + taken);
taken = false;
}
sleep(time);
//JOptionPane.showMessageDialog(null, "dostupnost: " + taken);
taken = true;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
At the time you call table.getModel(), you haven't yet called table = new JTable(model);
That means that table is still null, and so you get an exception. Try moving the line that constructs table to before the line that accesses it.
Found what else was wrong
private static TableModel model;
later i made
model = new DefaultTableModel() {
public boolean isCellEditable(int rowIndex, int vColIndex) {
return false;
}
};
Should have been
private static DefaultTableModel model;