I am making a system for a school project , and one part of it is a TableView that is populated with rows using my own data class InventoryData that has properties correspondent to the table columns. I would like to make cells in some columns editable using a TextField, so that when an edit is committed, it will update the InventoryData object's relevant property.
I tried setting TextFieldTableCell.forTableColumn() as the cell factory of the columns. Although, now after committing the edit, the text in the cell will change, I don't think it is changing the property in the InventoryData object. The reason why I think that, is because when I try to edit that cell again ( after already being edited once), the TextField shows the former value ( before the first edit).
Did I do something wrong , or is that normal behavior and I have to implement the commits myself?
Here's the code for InventoryData :
package UILayer.TableData;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import ModelLayer.Product;
public class InventoryData {
// From Product
private Product productObj;
private SimpleIntegerProperty id;
private SimpleStringProperty name;
// Constructor - converts Product obj into InventoryData
public InventoryData(Product product)
{
this.productObj = product;
this.id = new SimpleIntegerProperty(product.getId());
this.name = new SimpleStringProperty(product.getName())
}
// GET & SET
public Product getProduct()
{
return productObj;
}
public int getId() {
return id.get();
}
public void setId(int id) {
this.id.set(id);
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
productObj.setName(name);
System.out.println(productObj.getName());
}
}
You need your InventoryData class to use the JavaFX Properties pattern. Specifically it needs property-type accessor methods in order to retrieve the property in the table cells. Without this, the cell value factory just calls the standard getName() or getId() method, and wraps the result in a ReadOnlyStringWrapper (or ReadOnlyIntegerWrapper): the table cell cannot change the values of those wrappers (since they are read only).
public class InventoryData {
// From Product
private Product productObj;
private IntegerProperty id;
private StringProperty name;
// Constructor - converts Product obj into InventoryData
public InventoryData(Product product)
{
this.productObj = product;
this.id = new SimpleIntegerProperty(product.getId());
this.name = new SimpleStringProperty(product.getName())
this.name.addListener((obs, oldName, newName) ->
productObj.setName(newName));
}
// GET & SET
public Product getProduct()
{
return productObj;
}
public IntegerProperty idProperty() {
return id ;
}
public final int getId() {
return idProperty().get();
}
public final void setId(int id) {
idProperty().set(id);
}
public StringProperty nameProperty() {
return name ;
}
public final String getName() {
return nameProperty().get();
}
public final void setName(String name) {
this.nameProperty().set(name);
// productObj.setName(name);
// System.out.println(productObj.getName());
}
}
Related
I have several classes that all inherit from one super class that need to populate several TableViews related to their class.
The super class is abstract and some of the getters and setters are final but still contains data needed to populate the cells.
Writing a new Callback class for each and every column is doable, but I'm looking for a way to implements this.
sample code
class SuperClass
{
protected String name;
protected double value;
public final void setName(String name)
{
this.name = name;
}
public final void getName()
{
return this.name;
}
public final void setValue(double value)
{
this.value = value;
}
public double getValue()
{
return this.value;
}
}
class SubClass1 extends SuperClass
{
private int id;
public void setId(int id)
{
this.id = id;
}
public int getId()
{
return this.id;
}
}
class SubClass2 extends SuperClass
{
private String location;
public void setLocation(String location)
{
this.location = location;
}
}
class SubClass3 extends SuperClass
{
private ObservableMap<SuperClass> map;
public ObservableMap<SuperClass> map()
{
return this.map;
}
}
TableView
TableColumn<SubClass1, Integer> tc1_id;
TableColumn<SubClass1, String> tc1_name;
TableColumn<SubClass1, Double> tc1_value;
TableColumn<SubClass2, String> tc2_loc;
TableColumn<SubClass2, String> tc2_name;
TableColumn<SubClass2, Double> tc2_value;
TableColumn<SubClass3, String> tc3_name;
TableColumn<SubClass3, Double> tc3_value;
Here's a reference of what I was going to do...
Accessing Subclass properties in a JavaFX TableView ObservableArrayList
But just with the sample code, I'm basically rewriting 2 methods, 3 times each... and there's a bit more than that in the actual program. (Just a smidge more)
I think you are just asking how to reduce the amount of code you have to write. The solution is just the same as any such question: write a method that performs the repetitive part, and parametrize it with the parts that vary. So in this case, you just need to write a generic utility method to generate your table columns, taking the title of the column and the function that produces the property the cell value factory needs.
E.g. you could do something like
private <S,T> TableColumn<S,T> createColumn(String title, Function<S, Property<T>> prop) {
TableColumn<S,T> column = new TableColumn<>(title);
column.setCellValueFactory(cellData -> prop.apply(cellData.getValue()));
return column ;
}
and then if your model classes use JavaFX properties, all you need is
TableColumn<SubClass1, Number> tc1Id = createColumn("Id", SubClass1::idProperty);
etc.
If you are not using JavaFX properties (which is the recommended approach), you can still do
TableColumn<SubClass2, String> tc2Loc =
createColumn("Location", item -> new SimpleStringProperty(item.getLocation()));
or just create a method that accepts a Function<S,T> instead of a Function<S,Property<T>>.
In the JavaFx ComboBox which uses a class object list .I want to select items in the ComboBox programmatically using getSelectionModel().select(object or index). i am not getting the desired result Although the value is set but it is something like this main.dao.Company.Company.CompanyTableData#74541e7b.
The code is somewhat like this.
ComboBox<CompanyTableData> company = new ComboBox<>();
company.setItems(GetCompany.getCompanyTableData());//where Observable list is set..
GetCompany.getCompanyTableData() returns observablelist of CompanyTableData class.
The ComboBox Looks as follows.
The CompanyTableData Class is as.
public class CompanyTableData {
private SimpleStringProperty itemCompanyId;
private SimpleStringProperty itemCompanyName;
private SimpleStringProperty createBy;
private SimpleStringProperty createdOn;
public CompanyTableData(CompanyData companyData){
this.itemCompanyId = new SimpleStringProperty(companyData.getItemCompanyId());
this.itemCompanyName = new SimpleStringProperty(companyData.getItemCompanyName());
this.createBy = new SimpleStringProperty(companyData.getCreatedBy());
this.createdOn = new SimpleStringProperty(companyData.getCreatedOn());
}
public String getItemCompanyId() {
return itemCompanyId.get();
}
public SimpleStringProperty itemCompanyIdProperty() {
return itemCompanyId;
}
public void setItemCompanyId(String itemCompanyId) {
this.itemCompanyId.set(itemCompanyId);
}
public String getItemCompanyName() {
return itemCompanyName.get();
}
public SimpleStringProperty itemCompanyNameProperty() {
return itemCompanyName;
}
public void setItemCompanyName(String itemCompanyName) {
this.itemCompanyName.set(itemCompanyName);
}
public String getCreateBy() {
return createBy.get();
}
public SimpleStringProperty createByProperty() {
return createBy;
}
public void setCreateBy(String createBy) {
this.createBy.set(createBy);
}
public String getCreatedOn() {
return createdOn.get();
}
public SimpleStringProperty createdOnProperty() {
return createdOn;
}
public void setCreatedOn(String createdOn) {
this.createdOn.set(createdOn);
}
}
The Cell Factory is set
company.setCellFactory(param -> new CompanyCell());
And the CompanyCell
public class CompanyCell extends ListCell<CompanyTableData> {
#Override
protected void updateItem(CompanyTableData item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null || item.getItemCompanyName() == null) {
setText(null);
} else {
setText(item.getItemCompanyName());
}
}
}
After all this when i try to set the items programmetically as
company.getSelectionModel().select(getSelectedCompanyIndex());
The getSelectedCompanyIndex() function is as follows.
public static CompanyTableData getSelectedCompanyIndex(){
CompanyTableData c = null,i;
Iterator<CompanyTableData> itr = GetCompany.getCompanyTableData().iterator();
while (itr.hasNext()){
i = itr.next();
if (i.getItemCompanyName().equals(Element.getItemTableData().getCompany())){
c = i;
}
}
return c;
}
And the result i am getting is
And
At the end it should select a name or item in the list but it has set some type of object i think.
Now what should i do. Is there any type of string conversion required.
The buttonCell used to display the item when the combobox popup is not shown is not automatically created using the cellFactory. You need to set this property too to use the same cell implementation:
company.setCellFactory(param -> new CompanyCell());
company.setButtonCell(new CompanyCell());
I am using a JFXComboBox to show user group a user can be allocated to. The roles are fetched from the DB. Fetching of the rolelist is done by getRoleNameList(). I populate the JFXComboBox as below:
Populate userGroup JFXComboBox
// Populate userGroup JFXComboBox
ObservableList<Role> roles = rdc.getRoleNameList();
roles.sort(Comparator.comparing(Role::getCode)); // Sort the list
uGroupComboBox.getItems().setAll(roles);
uGroupComboBox.setVisibleRowCount(5);
JFXCombobox FXML:
<JFXComboBox id="userGroup" fx:id="uGroupComboBox" focusColor="#07595a"
layoutX="245.0" layoutY="256.0" prefHeight="30.0" prefWidth="148.0"
promptText="Select User Group" styleClass="jfx-combo-box"
unFocusColor="#48aaad">
The model below is for Roles:
package records.models;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import org.apache.commons.lang3.StringUtils;
public class Role {
private final StringProperty code;
private final StringProperty name;
public Role() {
this.name = new SimpleStringProperty();
this.code = new SimpleStringProperty();
}
public Role(String name) {
String codename = StringUtils.capitalize(name.toLowerCase().trim());
this.code = new SimpleStringProperty(codename);
name = name.toLowerCase().trim();
this.name = new SimpleStringProperty(name);
}
// name
public String getName() {
return name.get();
}
public void setName(String name) {
if (name != null) {
name = name.toLowerCase().trim();
}
this.name.set(name);
}
public StringProperty nameProperty() {
return name;
}
// #return code
public String getCode() {
return code.get();
}
public void setCode(String code) {
if (code != null) {
code = StringUtils.capitalize(code.toLowerCase().trim());
}
this.code.set(code);
}
public StringProperty codeProperty() {
return code;
}
public Role getRole(){
return this;
}
#Override
public String toString(){
return this.code.get();
}
}
When I fetch from the DB the user group the user is assigned to, the data is not displayed:
String uRole = role.get("role") == null ? null :
role.get("role").toString();
user.setRole(uRole);
// Create the role that is fetched from DB
Role uGroup = new Role(uRole);
// uGroupComboBox.setValue(uGroup);
uGroupComboBox.getSelectionModel().select(uGroup);
This does not work. As you can see, the field is not null. If it were, I would show the prompt text
However, the JFXComboBox list is successfully populated:
If I do System.out.println(uGroupComboBox.getValue()); it shows the correct value eg. if the user group is Nurse, the statement returns Nurse
Why does the JFXComboBox not display the value?
The problem is in your Role object with its StringProperty properties. Let me explain myself.
if you just take :
static void main(String[] args){
StringProperty codeName = new SimpleStringProperty("codename");
StringProperty codeNameSameValue = new SimpleStringProperty("codename");
boolean isthesame = codeName.equals(codeNameSameValue);
System.out.println("isthesame:"+isthesame);
boolean isReallyTheSame = codeName.get().equals(codeNameSameValue.get());
System.out.println("isReallyTheSame:"+isReallyTheSame);
}
You will see on you console output
isthesame:false
isReallyTheSame:true
Ok it is still not the answer to your question but for you Role class default equals method that will be comparing object properties like my first comparison:
comparingStringProperty.equals(comparedStringProperty);
...
To solve the problem you need to override the equals method in the Role object. For example with something like that:
i
mport java.util.Objects;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import org.apache.commons.lang3.StringUtils;
public class Role {
private final StringProperty code;
private final StringProperty name;
/*Constructors, setters and getter are deleted for clarity */
....
#Override
public String toString() {
return this.code.get();
}
#Override
public boolean equals(Object o) {
// If the object is compared with itself then return true
if (o == this) {
return true;
}
/* Check if o is an instance of Role or not
"null instanceof [type]" also returns false */
if (!(o instanceof Role)) {
return false;
}
// typecast o to Role so that we can compare data members
Role r = (Role) o;
return (r.name.get() == this.name.get() || r.name.get() != null && r.name.get().equals(this.name.get()));
}
#Override
public int hashCode() {
return Objects.hash(name.get(), code.get());
}
}
This equals method will be use during the call :
uGroupComboBox.getSelectionModel().select(uGroup);
And voila!
I need to order the list of items based on a field say starredAt
I am loading the data in the recyclerview from Realm DB using RealmRecyclerView by thorbenprimke
The field changes it value on user's action i.e when user presses star button the item should be moved to top.
For this I am just updating the starredAt field of the object.
The items are already sorted by starredAt so realm loads the updated list but it randomly adds one more item to the recyclerview.
CheatSheet.java
public class CheatSheet extends RealmObject {
#PrimaryKey
private String id;
private RealmList<Item> items;
private String title;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public RealmList<Item> getItems() {
return items;
}
public void setItems(RealmList<Item> items) {
this.items = items;
}
}
Item.java
public class Item extends RealmObject {
#PrimaryKey
private String id;
private String description;
private Date starredAt;
public Item() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Date getStarredAt() {
return starredAt;
}
public void setStarredAt(Date starredAt) {
this.starredAt = starredAt;
}
}
CheatSheetActivity.java
public class MainActivity extends AppCompatActivity {
RealmRecyclerView revItems;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setData();
}
private void setData() {
rvItems = (RealmRecyclerView) findViewById(R.id.rev_items);
RealmResults<Item> items = Realm.getDefaultInstance().where(CheatSheet.class)
.equalTo("id", "some-id").findFirst().getItems()
.where()
.findAllSorted("starredAt", Sort.DESCENDING);
ItemRealmListAdapter itemRealmListAdapter =
new ItemRealmListAdapter(this, items,
true, true);
rvItems.setAdapter(itemRealmListAdapter);
}
ItemRealmListAdapter.java
public class ItemRealmListAdapter extends RealmBasedRecyclerViewAdapter<Item,
ItemRealmListAdapter.ItemViewHolder> {
RealmResults<Item> mItems;
public ItemRealmListAdapter(Context context, RealmResults<Item> realmResults,
boolean automaticUpdate, boolean animateResults) {
super(context, realmResults, automaticUpdate, animateResults);
this.mItems = realmResults;
}
#Override
public ItemViewHolder onCreateRealmViewHolder(ViewGroup viewGroup, int i) {
return new ItemViewHolder(LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_layout_cs_text, viewGroup, false));
}
public Item getItem(int position) {
return mItems.get(position);
}
#Override
public void onBindRealmViewHolder(ItemViewHolder itemViewHolder, int position) {
itemViewHolder.txtBody.setText(getItem(position).getDescription());
if (getItem(position).getStarredAt() != null) {
itemViewHolder.imvStar.setImageResource(R.drawable.ic_star_yellow);
}
itemViewHolder.imvStar.setOnClickListener(v -> handleStarClick(v,position));
}
private void handleStarClick(View v, int position) {
if (getItem(position).getStarredAt() != null) {
((ImageView) v).setImageResource(R.drawable.ic_star);
CheatSheetStorage.unStarItem("some-id", getItem(position));
} else {
((ImageView) v).setImageResource(R.drawable.ic_star_yellow);
CheatSheetStorage.starItem("some-id", getItem(position));
}
}
public static class ItemViewHolder extends RealmViewHolder {
#Bind(R.id.txt_cheat_sheet)
TextView txtBody;
#Bind(R.id.img_star)
ImageView imvStar;
public ItemViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
CheatSheetStorage.java
public class CheatSheetStorage {
public static void unStarItem(String cheatSheetId, Item item) {
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
CheatSheet cheatSheet = getCheatSheetById(cheatSheetId);
Item itemDB = cheatSheet.getItems().where().equalTo("id", item.getId()).findFirst();
itemDB.setStarredAt(null);
realm.commitTransaction();
}
public static void starItem(String cheatSheetId, Item item) {
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
CheatSheet cheatSheet = getCheatSheetById(cheatSheetId);
Item itemDB = cheatSheet.getItems().where().equalTo("id", item.getId()).findFirst();
itemDB.setStarredAt(new Date());
realm.commitTransaction();
}
}
Please refer following screenshots for clearer idea :
Screenshot before starring
Screenshot after starring the sixth item
#Rohan-Peshkar - You will have to provide a animateExtraColumnName value to the adapter. For the animations, the adapter keeps track of the items and since that item's id doesn't change, the list isn't updated. With an additional column (in your case that should be the starredAt column - as long as it is stored as an Integer), the diffing algorithm will detect a change and the order is updated.
For reference: https://github.com/thorbenprimke/realm-recyclerview/blob/2835a543dce20993d8f98a4f773fa0e67132ce52/library/src/main/java/io/realm/RealmBasedRecyclerViewAdapter.java#L177
You can also check out the MainActivity in the example folder. The example changes a row's text from "ABC" to "Updated ABC" and the list recognizes the change because both the primary key and the quote field are used to basically create a composite key for diffing purposes.
How do I use the JdbcTemplate.query()/queryForList() to run a query using namedParameter and store the result set into a List of 'User's?
User Class:
public class User {
String name = null;
String id = null;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getId() {
return name;
}
public void setId(String id) {
this.id = id;
}
}
Query:
SELECT name, id FROM USERS where email=:email
I'm looking for something like:
ArrayList<User> userList = jdbcTemplate.query(sql_query,
...some_mapper..., etc);
Seems like the answer to the question is not available at one place, on the Internet. Here's what I found out:
For adding the resultset into a List<>, we can use the NamedParameterJdbcTemplate.query() function:
NamedParameterJdbcTemplate jdbcTemplate;
ArrayList<User> usersSearchResult = (ArrayList<User>) jdbcTemplate.query(
USER_LIST_TP_query,
namedParameters,
new RowMapperResultSetExtractor<User>(new UserRowMapper(), 20));
We also have to define a custom RowMapperResultSetExtractor so that JDBC can understand how to convert each row in the result set to the type User.
private class UserRowMapper implements RowMapper<User> {
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getString("ID"));
user.setName(rs.getString("NAME"));
return user;
}
}