There is a very similar question to this already, but mine's a bit different. I am using properties and on observable list to change it, it won't update.
Original question is here.
So when I am transferring rows between tables, like this:
The first row would appear, but when adding more than one would cause the ones after the first row to not update, like this:
They only reappear when I move around the columns though.
//Loot identification
TableColumn lootIdentCol = new TableColumn<>("Identification");
TableColumn<ItemDef, Integer> lootIDCol = new TableColumn<>("ID");
lootIDCol.setCellValueFactory(
new PropertyValueFactory<ItemDef, Integer>("id"));
TableColumn<ItemDef, String> lootNameCol = new TableColumn<>("Name");
lootNameCol.setCellValueFactory(
new PropertyValueFactory<ItemDef, String>("name"));
lootIdentCol.getColumns().addAll(lootNameCol, lootIDCol);
//Loot price
TableColumn<ItemDef, Integer> lootPriceCol = new TableColumn<>("Price");
lootPriceCol.setCellValueFactory(
new PropertyValueFactory<ItemDef, Integer>("price"));
//To loot items table
toLootItemsTableView.getColumns().addAll(lootIdentCol, lootPriceCol);
grid.add(toLootItemsTableView, 0, 1);
//Lootable items table
lootableItemsTableView.getColumns().addAll(lootIdentCol, lootPriceCol);
grid.add(lootableItemsTableView, 2, 1);
toLootItemsTableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
lootableItemsTableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
lootableItemsTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
toLootItemsTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
lootableTableList.add(new ItemDef("Ab", 141, false, false));
lootableTableList.add(new ItemDef("Ac", 25, false, false));
lootableTableList.add(new ItemDef("AD", 262, false, false));
AddRemoveButtons<ItemDef> addRemoveLootButtons = new AddRemoveButtons<>(
lootableTableList, lootableItemsTableView.getSelectionModel(),
toLootTableList, toLootItemsTableView.getSelectionModel()
);
Code for AddRemoveButtons:
private final ObservableList<E> fromList;
private final ObservableList<E> toList;
public AddRemoveButtons(final ObservableList<E> fromList, final SelectionModel<E> from,
final ObservableList<E> toList, final SelectionModel<E> to) {
this.fromList = fromList;
this.toList = toList;
setAlignment(Pos.CENTER);
setPadding(new Insets(5, 5, 5, 5));
setSpacing(15);
ObservableList<Node> children = getChildren();
Button moveInto = new Button("Add");
moveInto.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (from instanceof MultipleSelectionModel) {
MultipleSelectionModel<E> multipleFrom = (MultipleSelectionModel<E>) from;
ObservableList<Integer> selectedIndices = multipleFrom.getSelectedIndices();
for (int i : selectedIndices)
transfer(i, true);
} else
transfer(from.getSelectedIndex(), true);
}
});
Button delete = new Button("Del");
delete.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (to instanceof MultipleSelectionModel) {
MultipleSelectionModel<E> multipleFrom = (MultipleSelectionModel<E>) to;
ObservableList<Integer> selectedIndices = multipleFrom.getSelectedIndices();
for (int i : selectedIndices)
transfer(i, false);
} else
transfer(to.getSelectedIndex(), false);
}
});
children.addAll(moveInto, delete);
}
private void transfer(int index, boolean forward) {
if (forward)
toList.add(fromList.remove(index));
else
fromList.add(toList.remove(index));
}
ItemDef which implements Identifiable, Serializable, Comparable:
private final String name;
private final int id;
private final boolean members;
private final boolean stackable;
private int price;
public ItemDef(JSONObject jsonObject) {
this(
(String) jsonObject.get("name"),
Integer.parseInt((String) jsonObject.get("id")),
Boolean.parseBoolean((String) jsonObject.get("members")),
Boolean.parseBoolean((String) jsonObject.get("stackable"))
);
}
public ItemDef(String name, int id, boolean members, boolean stackable) {
this.name = name;
this.id = id;
this.members = members;
this.stackable = stackable;
price = -1;
}
public String getName() {
return name;
}
#Override
public int getId() {
return id;
}
public boolean isMembers() {
return members;
}
public boolean isStackable() {
return stackable;
}
public int getPrice() {
return price != -1 ? price : updatePrice();
}
//Other methods not relevant
Figured out why it kept doing that.
You just can't have the same TableColumn being referenced on multiple tables.
You should not share columns in multiple tables if you want data to update in multiple tables share the data set between them not the columns.
Related
I have a particular TreeTableView that displays a hierarchical tree of mixed types. These types do not necessarily have overlapping columns and as such the columns for some rows will be empty. As an example, consider the following classes:
public class Person {
private final StringProperty nameProperty;
private final StringProperty surnameProperty;
public Person() {
this.nameProperty = new SimpleStringProperty();
this.surnameProperty = new SimpleStringProperty();
}
public StringProperty nameProperty() {
return this.nameProperty;
}
public void setName(String value) {
this.nameProperty.set(value);
}
public String getName() {
return this.nameProperty.get();
}
public StringProperty surnameProperty() {
return this.surnameProperty;
}
public void setSurname(String value) {
this.surnameProperty.set(value);
}
public String getSurname() {
return this.surnameProperty.get();
}
}
public class Dog {
private final StringProperty nameProperty;
private final IntegerProperty ageProperty;
private final StringProperty breedProperty;
public Dog() {
this.nameProperty = new SimpleStringProperty();
this.ageProperty = new SimpleIntegerProperty();
this.breedProperty = new SimpleStringProperty();
}
public StringProperty nameProperty() {
return this.nameProperty;
}
public void setName(String value) {
this.nameProperty.set(value);
}
public String getName() {
return this.nameProperty.get();
}
public IntegerProperty ageProperty() {
return this.ageProperty;
}
public void setAge(int value) {
this.ageProperty.setValue(value);
}
public int getAge() {
return this.ageProperty.get();
}
public StringProperty breedProperty() {
return this.breedProperty;
}
public void setBreed(String breed) {
this.breedProperty.set(breed);
}
public String getBreed() {
return this.breedProperty.get();
}
}
If I construct the TreeTableView as follows:
TreeTableView<Object> treeTableView = new TreeTableView<>();
treeTableView.setEditable(true);
List<TreeTableColumn<Object, ?>> columns = treeTableView.getColumns();
TreeTableColumn<Object, String> nameColumn = new TreeTableColumn<>("Name");
nameColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
nameColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
columns.add(nameColumn);
TreeTableColumn<Object, String> surnameColumn = new TreeTableColumn<>("Surname");
surnameColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
surnameColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("surname"));
columns.add(surnameColumn);
TreeTableColumn<Object, Integer> ageColumn = new TreeTableColumn<>("Age");
ageColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn(new IntegerStringConverter()));
ageColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("age"));
columns.add(ageColumn);
TreeTableColumn<Object, String> breedColumn = new TreeTableColumn<>("Breed");
breedColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
breedColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("breed"));
columns.add(breedColumn);
TreeItem<Object> rootItem = new TreeItem<>();
treeTableView.setRoot(rootItem);
treeTableView.setShowRoot(false);
List<TreeItem<Object>> rootChildren = rootItem.getChildren();
Person john = new Person();
john.setName("John");
john.setSurname("Denver");
TreeItem<Object> johnTreeItem = new TreeItem<>(john);
rootChildren.add(johnTreeItem);
List<TreeItem<Object>> johnChildren = johnTreeItem.getChildren();
Dog charlie = new Dog();
charlie.setName("Charlie");
charlie.setAge(4);
charlie.setBreed("Labrador");
TreeItem<Object> charlieTreeItem = new TreeItem<>(charlie);
johnChildren.add(charlieTreeItem);
Dog daisy = new Dog();
daisy.setName("Daisy");
daisy.setAge(7);
daisy.setBreed("Bulldog");
TreeItem<Object> daisyTreeItem = new TreeItem<>(daisy);
johnChildren.add(daisyTreeItem);
I will get a TreeTableView that looks like:
The Age and Breed columns are empty for the TreeItems that contains Person objects. However, nothing stops me from editing Age or Breed cell for the top-most Person row. Setting a value in one of those cells doesn't change the Person object, but the value still hangs around there like it is committed.
Is there any way to prevent this from happening? I know that I could check for nulls in a custom TreeTableCell subclass and prevent the editing from kicking off in the startEdit() method. However, there are circumstances where a null-value is valid and preventing editing by checking nulls is not a feasible solution for all situations. Also, creating a custom TreeTableCell subclass for every datatype and corresponding columns is painful. It would have been nice if TreeItemPropertyValueFactory could provide for a way to abort the edit when no value is present for a particular cell.
Ok, I scraped together something by looking at the TreeItemPropertyValueFactory class itself for inspiration. This gives me the desired functionality, although I'm not sure if it is 100% correct or what the implications are of using it.
It basically comes down to installing a new cell-factory that checks if the cell-value-factory is of type TreeItemPropertyValueFactory. If it is the case, a new cell-factory is installed that delegates to the original but adds listeners for the table-row and tree-item properties. When the TreeItem changes, we get the row-data and see if we can access the desired property (via a PropertyReference that is cached for performance). If we can't (and we get the two exceptions) we assume that the property cannot be accessed and we set the cell's editable-property to false.
public <S, T> void disableUnavailableCells(TreeTableColumn<S, T> treeTableColumn) {
Callback<TreeTableColumn<S, T>, TreeTableCell<S, T>> cellFactory = treeTableColumn.getCellFactory();
Callback<CellDataFeatures<S, T>, ObservableValue<T>> cellValueFactory = treeTableColumn.getCellValueFactory();
if (cellValueFactory instanceof TreeItemPropertyValueFactory) {
TreeItemPropertyValueFactory<S, T> valueFactory = (TreeItemPropertyValueFactory<S, T>)cellValueFactory;
String property = valueFactory.getProperty();
Map<Class<?>, PropertyReference<T>> propertyRefCache = new HashMap<>();
treeTableColumn.setCellFactory(column -> {
TreeTableCell<S, T> cell = cellFactory.call(column);
cell.tableRowProperty().addListener((o1, oldRow, newRow) -> {
if (newRow != null) {
newRow.treeItemProperty().addListener((o2, oldTreeItem, newTreeItem) -> {
if (newTreeItem != null) {
S rowData = newTreeItem.getValue();
if (rowData != null) {
Class<?> rowType = rowData.getClass();
PropertyReference<T> reference = propertyRefCache.get(rowType);
if (reference == null) {
reference = new PropertyReference<>(rowType, property);
propertyRefCache.put(rowType, reference);
}
try {
reference.getProperty(rowData);
} catch (IllegalStateException e1) {
try {
reference.get(rowData);
} catch (IllegalStateException e2) {
cell.setEditable(false);
}
}
}
}
});
}
});
return cell;
});
}
}
For the example listed in the question, you can call it after you created all your columns as:
...
columns.forEach(this::disableUnavailableCells);
TreeItem<Object> rootItem = new TreeItem<>();
treeTableView.setRoot(rootItem);
treeTableView.setShowRoot(false);
...
You'll see that cells for the Age and Breed columns are now uneditable for Person entries whereas cells for the Surname column is now uneditable for Dog entries, which is what we want. Cells for the common Name column is editable for all entries as this is a common property among Person and Dog objects.
I want to populate a dynamic tableview from database with 32 columns,First column contain the name of employee and remaining 31 columns for marking employee attendence from day1 to day31(checkbox).But I can able to populate tableview from database with 2 columns(name,checkbox) using get and set methods.Here is my code
String sql ="SELECT * FROM attendence";
pst = (PreparedStatement) con.prepareStatement(sql);
rs=pst.executeQuery();
while (rs.next()) {
//get string from db,whichever way
String name=rs.getString(3);
int day=rs.getInt(6);
data.add(new User(name,day!=0)); //converting integer to boolean and storing on data(Observable list)
}
etname.setCellValueFactory(new PropertyValueFactory<>("name"));
col.setCellValueFactory(new PropertyValueFactory<>("day1));
col.setCellFactory(new Callback<TableColumn<User, Boolean>, TableCell<User, Boolean>>() {
public TableCell<User, Boolean> call(TableColumn<User, Boolean> p) {
return new CheckBoxTableCell<User, Boolean>();
}
});
jTable.getColumns().add(etname,col);
jTable.setItems(data);
}
This is User.java
public class User {
private final SimpleStringProperty ename;
private BooleanProperty day1;
User(String Ename,boolean day1)
{
this.ename = new SimpleStringProperty(Ename);
this.day1 = new SimpleBooleanProperty(day1);
this.day1.addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1) {
System.out.println(enameProperty().get() + " invited: " + t1);
System.out.println();
}
});
}
public String getEname() {
return ename.get();
}
public void setEname(String Ename) {
ename.set(Ename);
}
public BooleanProperty day1Property() {
return day1;
}
public StringProperty enameProperty() {
return ename;
}
CheckBoxTableCell.java
public class CheckBoxTableCell<S, T> extends TableCell<S, T> {
private final CheckBox checkBox;
private ObservableValue<T> ov;
public CheckBoxTableCell() {
this.checkBox = new CheckBox();
this.checkBox.setAlignment(Pos.CENTER);
setAlignment(Pos.CENTER);
setGraphic(checkBox);
}
#Override public void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setGraphic(checkBox);
if (ov instanceof BooleanProperty) {
checkBox.selectedProperty().unbindBidirectional((BooleanProperty) ov);
}
ov = getTableColumn().getCellObservableValue(getIndex());
if (ov instanceof BooleanProperty) {
checkBox.selectedProperty().bindBidirectional((BooleanProperty) ov);
}
}
}
}
But no idea how i can do with 32 columns.
So i need a big help from anyone for my 2 problems.
1) How i can populate dynamic tableview from database with checkboxes 2) when i press a button should read all the names along with checkbox status(isSelected or not selected) like
jhon true true false ... ....
rose false false true ... ..
george true true true false
Answers will be appreciated.Thank you in advance.
.. ..
try with this code.
private static final List<String> groups = Arrays.asList("Group 1", "Group 2", "Group 3", "Group 4"); //declared an array
TableView<AttributeRow> attributeTable = new TableView<>(); //new Table
for (String group : groups) { //Creating dynamic column
TableColumn<AttributeRow, Boolean> groupColumn = new TableColumn<>(group);
groupColumn.setCellFactory(CheckBoxTableCell.forTableColumn(groupColumn));
groupColumn.setCellValueFactory(cellData -> cellData.getValue().activeProperty(group));
attributeTable.getColumns().add(groupColumn);
}
and AttributeRow class
class AttributeRow {
private final Map<String, BooleanProperty> activeByGroup = new HashMap<>();
public AttributeRow(List<String> companyGroups) {
for (String group : companyGroups) {
activeByGroup.put(group, new SimpleBooleanProperty()) ;
}
}
public final BooleanProperty activeProperty(String group) {
return activeByGroup.get(group) ;
}
public final boolean isActive(String group) {
return activeProperty(group).get();
}
public final void setActive(String group, boolean active) {
activeProperty(group).set(active);
}
For loading checkbox data(true or false) we can use following method
String sql ="SELECT * FROM attendence";
pst = (PreparedStatement) con.prepareStatement(sql);
rs=pst.executeQuery();
while (rs.next()) {
AttributeRow row = new AttributeRow(groups);
for(int j=0;j<5;j++) //here j<5 because array size =5
{
int i=j+6;
int day=rs.getInt(i); //getting Integer value(note:sqlite does not support boolean datatype so i stored as integer with 0 or 1)
row.setActive(day!=0); //converted into boolean useing day!=0
}
jTable.getItems().add(row);
}
Ok, so I know this is a common problem that has been posted about a lot but as much as I try to follow the advice given, my TableView till displays no data... I'll reduce my object a bit to keep things as short as possible. Here is my Object:
public SimpleStringProperty itemCode, itemName;
public ResourceItem(String code, String name) {
this.itemCode = new SimpleStringProperty(code);
this.itemName = new SimpleStringProperty(name);
}
public String getItemCode() {
return itemCode.get();
}
public void setItemCode(String code) {
itemCode.set(code);
}
public SimpleStringProperty itemCodeProperty() {
return itemCode;
}
public SimpleStringProperty itemNameProperty() {
return itemName;
}
public String getItemName() {
return itemName.get();
}
public void setItemName(String name) {
itemName.set(name);
}
And here is where I create the TableColumns:
TableColumn<ResourceItem, String> code = new TableColumn("Item Code");
code.setCellValueFactory(new PropertyValueFactory("itemCode"));
TableColumn<ResourceItem, String> code = new TableColumn("Item Name");
name.setCellValueFactory(new PropertyValueFactory("itemName"));
I add Resource Items to the ObservableList through a for loop and set my items of the TableView to that list:
ObservableList<ResourceItem> data = FXCollections.observableArrayList();
....
itemsInDB.setItems(data);
itemsInDB.getColumns().addAll(code, name);
And then nothing is added. Can someone help me out please?
EDIT:
Here is a testable version. It does require you set up a database called ims, a table called im_resoureitem_br with two columns: IMItemCode Varchar(4) and IMItemName Varchar(30).
public class TableViewTest extends Application {
final String DRIVER = "com.mysql.jdbc.Driver";
String urlHead = "jdbc:mysql://localhost/ims";
final String USER = "root";
final String PASS = "";
Connection connection;
Statement statement;
private TableView<ResourceItem> table = new TableView<ResourceItem>(); //creates table to hold Course objects
private final ObservableList<ResourceItem> data
= FXCollections.observableArrayList();
#Override
public void start(Stage stage) throws ClassNotFoundException {
Scene scene = new Scene(new Group());
stage.setTitle("Fall 2015 Schedule"); //title of stage, appears at top bar
stage.setWidth(700);
stage.setHeight(500);
final Label label = new Label("Brenna Morss-Fish Fall Schedule 2015");
table.setEditable(true);
table.setItems(data); //sets rows of table as data from course arraylist
TableColumn<ResourceItem, String> code = new TableColumn<ResourceItem, String>("Code:");//creates first column
code.setMinWidth(100);
code.setCellValueFactory(
new PropertyValueFactory("itemCode"));
TableColumn<ResourceItem, String> name = new TableColumn<ResourceItem, String>("Name:");//creates first column
name.setMinWidth(100);
name.setCellValueFactory(
new PropertyValueFactory("itemName")); //defines what column holds according to name field of Course class
String query = "select * from ims.im_resourceItem_br; ";
ArrayList<String[]> items = new ArrayList<String[]>();
TableView<ResourceItem> itemsInDB = new TableView();
items = getQueryResult(query);
//itemsInDB.setEditable(false);
ResourceItem item = new ResourceItem("", "");
ObservableList<ResourceItem> data = FXCollections.observableArrayList();
data.removeAll(data);
//System.out.println(items.get(0).toString());
for (int i = 0; i < items.size(); i++) {
item.setItemCode(items.get(i)[1]);
item.setItemName(items.get(i)[2]);
data.add(item);
}
code.setCellValueFactory(new PropertyValueFactory("itemCode"));
name.setCellValueFactory(new PropertyValueFactory("itemName"));
itemsInDB.setItems(data);
System.out.println(itemsInDB.getItems());
itemsInDB.getColumns().addAll(code, name);
table.getColumns().addAll(code, name);
//adds previously defined columns to the table in the order they will appear
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.getChildren().addAll(label, table); //adds label and course table to VBox layout container
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene); //adds scene to the stage
stage.show(); //displays stage
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public ArrayList getQueryResult(String stmt) throws ClassNotFoundException {
String results = "";
ResultSet resultSet = null;
String row = "";
ArrayList<String[]> list = new ArrayList<String[]>();
try {
Class.forName(DRIVER);
connection = DriverManager.getConnection(urlHead, USER, PASS);
statement = connection.createStatement();
resultSet = statement.executeQuery(stmt);
int columnCount = resultSet.getMetaData().getColumnCount();
while (resultSet.next()) {
String delims = "[%]";
row = "";
for (int i = 1; i <= columnCount; i++) {
row += resultSet.getString(i) + "%";
}
String[] array = row.split(delims);
list.add(array);
}
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
public static class ResourceItem {
public SimpleStringProperty itemCode, itemName;
public ResourceItem(String code, String name) {
this.itemCode = new SimpleStringProperty(code);
this.itemName = new SimpleStringProperty(name);
}
public String getItemCode() {
return itemCode.get();
}
public void setItemCode(String code) {
itemCode.set(code);
}
public SimpleStringProperty itemCodeProperty() {
return itemCode;
}
public SimpleStringProperty itemNameProperty() {
return itemName;
}
public String getItemName() {
return itemName.get();
}
public void setItemName(String name) {
itemName.set(name);
}
public String toString() {
String print = itemCode + " " + itemName + " ";
return print;
}
}
}
You have created two tables: one called table, which you display in the UI when you add it to vbox, and another called itemsInDB, which you populate but never display. Since the table you do display has no data in it, and the table you populate is never displayed, you never see the data.
You have another logical error when you populate the observable list: you add the same item over and over to the list, and update its properties each time. So if it were displayed you would see the same item repeatedly in the table.
There may be other errors I haven't noticed, but since you haven't created a MCVE, I can't actually run it and test it.
I have a listview with a getCount() of 7. I want all 7 items to be shown regardless if any data from my database is available to populate them. If no data is available then an item should just be blank with predetermined text.
When I have not hardcoded 7 database entries beforehand to go into the 7 views then I get an indexoutofbound exception when running the app due to the 7 items not being able to be populated accordingly. This happens in ListMealsAdapter.java when method Meal currentItem = getItem(position); is called and triggers public Meal getItem(int position).
I am looking for a condition statement that I can use for my listview/adapter that can handle an empty database so that the index does not go out of bounds. Also, is the BaseAdapter suited for what I want to do?
MainActivity.java
public class MainActivity extends BaseActivity {
public static final String TAG = "MainActivity";
private ListView mListviewMeals;
private MealDAO mMealDao;
private List<Meal> mListMeals;
private ListMealsAdapter mAdapter;
private SQLiteDatabase mDatabase;
DatabaseHelper mDbHelper;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activateToolbar(1);
// initialize views
initViews();
// fill the dailyListView
mMealDao = new MealDAO(this);
mListMeals = mMealDao.getAllMeals();
mAdapter = new ListMealsAdapter(this, mListMeals, MainActivity.this);
mListviewMeals.setAdapter(mAdapter);
}
private void initViews() {
this.mListviewMeals = (ListView) findViewById(R.id.view_daily_list);
}
ListMealsAdapter.java
public class ListMealsAdapter extends BaseAdapter {
public static final String TAG = "ListMealsAdapter";
Activity mActivity;
private List<Meal> mItems;
private LayoutInflater mInflater;
public ListMealsAdapter(Context context, List<Meal> listMeals, Activity activity) {
super();
mActivity = activity;
this.setItems(listMeals);
this.mInflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return 7;
}
#Override
public Meal getItem(int position) {
return (getItems() != null && !getItems().isEmpty()) ? getItems().get(position) : null;
}
#Override
public long getItemId(int position) {
return (getItems() != null && !getItems().isEmpty()) ? getItems().get(position).getId() : position;
}
#Override
public View getView(int position, final View convertView, final ViewGroup parent) {
View v = convertView;
final ViewHolder holder;
if (v == null) {
v = mInflater.inflate(R.layout.list_item_daily, parent, false);
holder = new ViewHolder();
holder.txtDescription = (TextView) v.findViewById(R.id.txtBreakfast);
v.setTag(holder);
} else {
holder = (ViewHolder) v.getTag();
}
// fill row data
Meal currentItem = getItem(position);
if (currentItem != null) {
holder.txtDescription.setText(currentItem.getDescription());
}
return v;
}
public List<Meal> getItems() {
return mItems;
}
public void setItems(List<Meal> mItems) {
this.mItems = mItems;
}
class ViewHolder {
TextView txtDescription;
}
}
Meal.java
public class Meal implements Serializable {
public static final String TAG = "Meal";
private static final long serialVersionUID = -7406082437623008161L;
private long mId;
private int mType;
private String mDescription;
public Meal() {
}
public Meal(int type, String description) {
this.mType = type;
this.mDescription = description;
}
public long getId() {
return mId;
}
public void setId(long mId) {
this.mId = mId;
}
public int getType() {
return mType;
}
public void setType(int mType) {
this.mType = mType;
}
public String getDescription() {
return mDescription;
}
public void setDescription(String mDescription) {
this.mDescription = mDescription;
}
}
MealDAO.java
public class MealDAO {
public static final String TAG = "MealDAO";
private SQLiteDatabase mDatabase;
private DatabaseHelper mDbHelper;
private Context mContext;
private String[] mAllColumns = { DatabaseHelper.COLUMN_MEAL_ID,
DatabaseHelper.COLUMN_MEAL_TYPE, DatabaseHelper.COLUMN_MEAL_DESCRIPTION};
public MealDAO(Context context) {
this.mContext = context;
mDbHelper = new DatabaseHelper(context);
// open the database
try {
open();
} catch (SQLException e) {
Log.e(TAG, "SQLException on opening database " + e.getMessage());
e.printStackTrace();
}
}
public void open() throws SQLException {
mDatabase = mDbHelper.getWritableDatabase();
}
public void close() {
mDbHelper.close();
}
public List<Meal> getAllMeals() {
List<Meal> listMeals = new ArrayList<Meal>();
Cursor query = mDatabase.rawQuery("SELECT * from meal", null);
if(query.moveToFirst()) {
do {
// Cycle through all records
Meal meal = cursorToMeal(query);
listMeals.add(meal);
} while(query.moveToNext());
}
return listMeals;
}
public Meal getMealById(long id) {
Cursor cursor = mDatabase.query(DatabaseHelper.TABLE_MEALS, mAllColumns,
DatabaseHelper.COLUMN_MEAL_ID + " = ?",
new String[] { String.valueOf(id) }, null, null, null);
if (cursor != null) {
cursor.moveToFirst();
}
Meal meal = cursorToMeal(cursor);
return meal;
}
protected Meal cursorToMeal(Cursor cursor) {
Meal meal = new Meal();
meal.setId(cursor.getLong(0));
meal.setType(cursor.getInt(1));
meal.setDescription(cursor.getString(2));
return meal;
}
}
After a LOT of trial and error I finally found an acceptable solution to my problem. What I did was to add a default row to my database for the view items that I wanted to have a predetermined database entry when no data had been entered beforehand.
I then made sure to start at index 2, making sure that index 1 would be reserved for my default value. If the index comes out of bounds then the exception is caught and the default database entry will be added to the array.
public Meal getItem(int position) {
Meal result;
try {
result = (getItems() != null && !getItems().isEmpty()) ? getItems().get(position) : null;
} catch (Exception e) {
Meal default = getItem(0);
return default;
}
return result;
}
Meal currentItem = getItem(position + 1);
if (currentItem != null) {
holder.txtDescription.setText(currentItem.getDescription());
}
With that change things have been running smooth ever since. I hope this can help someone else as well.
I have tables with editable fields item,Description,Quantity,Unit price and Sub Total.
I am creating a cellFactory and Column Update like this:
TableColumn DescriptionCol = new TableColumn("Description");
EditableTableSupport.createEditingColumn(DescriptionCol,"description");
TableColumn QuantityCol = new TableColumn("Quantity");
EditableTableSupport.createEditingColumn(QuantityCol,"quantity");
TableColumn UnitPriceColumn = new TableColumn<>("Unit Price");
EditableTableSupport.createEditingColumn(UnitPriceColumn,"unitPrice");
TableColumn DiscountColumn = new TableColumn<>("Discount");
EditableTableSupport.createEditingColumn(DiscountColumn,"discount");
SubTotalColumn = new TableColumn<>("SubTotal");
EditableTableSupport.createColumn(SubTotalColumn,"subTotal");
TableColumn SubTotalColumn = new TableColumn<>("SubTotal");
EditableTableSupport.createColumn(SubTotalColumn,"subTotal");
DescriptionCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<DUMMY_PurchaseOrderLine, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<DUMMY_PurchaseOrderLine, String> t) {
((DUMMY_PurchaseOrderLine) t.getTableView().getItems().get(t.getTablePosition().getRow())).setDescription(t.getNewValue());
}
});
QuantityCol.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<DUMMY_PurchaseOrderLine, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<DUMMY_PurchaseOrderLine, String> t) {
((DUMMY_PurchaseOrderLine) t.getTableView().getItems().get(t.getTablePosition().getRow())).setQuantity(t.getNewValue());
}
});
UnitPriceColumn.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<DUMMY_PurchaseOrderLine, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<DUMMY_PurchaseOrderLine, String> t) {
((DUMMY_PurchaseOrderLine) t.getTableView().getItems().get(t.getTablePosition().getRow())).setUnitPrice(t.getNewValue());
}
});
DiscountColumn.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<DUMMY_PurchaseOrderLine, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<DUMMY_PurchaseOrderLine, String> t) {
((DUMMY_PurchaseOrderLine) t.getTableView().getItems().get(t.getTablePosition().getRow())).setDiscount(t.getNewValue());
}
});
public class EditableTableSupport {
public static void createEditingColumn(TableColumn Column ,String name){
Callback<TableColumn, TableCell> cellFactory = new Callback<TableColumn, TableCell>() {
#Override
public TableCell call(TableColumn p) {
return new EditingCell();
}
};
Column.setSortable(false);
Column.setCellValueFactory(new PropertyValueFactory<DUMMY_PurchaseOrderLine, String>(name));
Column.setCellFactory(cellFactory);
}
public static void createColumn(TableColumn Column, String name) {
Column.setSortable(false);
Column.setCellValueFactory(new PropertyValueFactory<DUMMY_PurchaseOrderLine, String>(name));
}}
Question:How to Update Subtotal Column When i updating Quantity Column or UnitPrice Column
Thank you..
public class DUMMY_PurchaseOrderLine {
private String name;
private String description;
private BigDecimal quantity;
private BigDecimal unitPrice;
private BigDecimal discount;
private BigDecimal subTotal;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public BigDecimal getQuantity() {
return quantity;
}
public void setQuantity(BigDecimal quantity) {
this.quantity = quantity;
}
public BigDecimal getUnitPrice() {
return unitPrice;
}
public void setUnitPrice(BigDecimal unitPrice) {
this.unitPrice = unitPrice;
}
public BigDecimal getDiscount() {
return discount;
}
public void setDiscount(BigDecimal discount) {
this.discount = discount;
}
public BigDecimal getSubTotal() {
return subTotal;
}
public void setSubTotal(BigDecimal subTotal) {
this.subTotal = subTotal;
}
public DUMMY_PurchaseOrderLine(String name, BigDecimal description, BigDecimal quantity,BigDecimal unitPrice,BigDecimal discount,BigDecimal subTotal) {
this.name = name;
this.description = description;
this.quantity = quantity;
this.unitPrice = unitPrice;
this.discount = discount;
this.subTotal = quantity.multiply(unitPrice).subtract(discount);
}
}
In your DUMMY_PurchaseOrderLine class create a read only property named subTotal and initialize it in the constructor via binding. The combination of the binding and the PropertyValueFactory you use to set the value for the SubTotalColumn will ensure that the correct subtotal is always reflected.
class DUMMY_PurchaseOrderLine {
private IntegerProperty quantity = new SimpleIntegerProperty(0);
private DoubleProperty unitPrice = new SimpleDoubleProperty(0);
private DoubleProperty discount = new SimpleDoubleProperty(0);
private ReadOnlyDoubleWrapper subTotal = new ReadOnlyDoubleWrapper(0);
DUMMY_PurchaseOrderLine() {
subTotal.bind(quantity.multiply(unitPrice).subtract(discount));
}
IntegerProperty quantityProperty() { return quantity; }
IntegerProperty unitPriceProperty() { return unitPrice; }
IntegerProperty discountProperty() { return discount; }
ReadOnlyDoubleProperty subTotalProperty() { return subTotal.getReadOnlyProperty(); }
}
Note the naming conventions used. Using the correct naming convention is key.
I'm assuming here that the subtotal is just the calculated value for a single row (specifically by quantity * unitPrice - discount), not a total of values calculated across multiple rows (which would be quite a difficult problem to solve with a TableView).
Update based on question edit
I see from your update that you are using BigDecimal and JavaFX doesn't have a corresponding BigDecimalProperty, so either you will need to create one (not trivial if you want it to be fully featured) or use one of the existing property types.
Your alternate to using properties is to use the low level binding api to calculate subtotals, but I'd advise using properties if you can.