I have an app that makes significant use of checkboxes in JavaFX TreeView and TableView. There is custom code as much of it was done before the enhancements that came later in JavaFX 2.2.
I now find that checkboxes that used to work do not work (as if they are disabled) although some work intermittently.
I have checked through the Oracle compatibility documentation and I can find nothing relevant.
I have a small sample app that works perfectly in Java 7.4 but shows the same faulty behaviour as the main app in Java 8.
Could anyone you please suggest where I might start looking from your own experience - e.g. cell factory, callback etc - or indicate if anything fundamental changed with this kind of construct at Java 8? I have posted the sample app code below (four separate classes).
Thank you in advance.
package samplefx2_original_v7;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
import javafx.util.Callback;
public class SampleFX2_Original_V7 extends Application {
private TableView table = new TableView();
private boolean everything = false;
//Sample data for the table.
private final ObservableList<Person> data =
FXCollections.observableArrayList(
new ControlPerson(false, "Select Columns", false, false),
new Person(true, "Jacob Smith", true, false),
new Person(true, "Isabella Johnson", true, true),
new Person(true, "Ethan Williams", false, false),
new Person(true, "Emma Jones", false, true),
new Person(false, "Michael Brown", true, true));
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
stage.setWidth(850);
stage.setHeight(500);
final Label label = new Label("Address Book");
label.setFont(new Font("Arial", 20));
//A custom cell factory that creates checkboxes for a boolean property.
Callback<TableColumn, TableCell> colCheckFactory = new Callback<TableColumn, TableCell>() {
#Override
public TableCell call(TableColumn p) {
return new CheckBoxCell();
}
};
//The various columns
TableColumn nameCol = new TableColumn("Name");
nameCol.setMinWidth(100);
nameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("name"));
TableColumn contactCol = createContactColumn(colCheckFactory);
TableColumn emailCol = createEmailColumn(colCheckFactory);
TableColumn phoneCol = createPhoneColumn(colCheckFactory);
//Add the columns and data to the table.
table.setItems(data);
table.getColumns().addAll(nameCol, contactCol, emailCol, phoneCol);
//Make the table editable
table.setEditable(true);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.getChildren().addAll(label, table);
vbox.setPadding(new Insets(10, 0, 0, 10));
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
private TableColumn createEmailColumn(Callback<TableColumn, TableCell> cellFactory) {
TableColumn emailCol = new TableColumn("Email");
emailCol.setMinWidth(75);
emailCol.setCellValueFactory(new PropertyValueFactory("email"));
emailCol.setCellFactory(cellFactory);
emailCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, Boolean>>() {
#Override
public void handle(CellEditEvent<Person, Boolean> event) {
if (event.getRowValue() instanceof ControlPerson) {
for (Person p : data) {
p.setEmail(event.getNewValue());
}
} else {
//Need to handle the indivdual cells as well as the special control cells.
Person p = event.getRowValue();
p.setEmail( event.getNewValue() );
}
}
});
return emailCol;
}
private TableColumn createPhoneColumn(Callback<TableColumn, TableCell> cellFactory) {
TableColumn phoneCol = new TableColumn("Phone");
phoneCol.setMinWidth(75);
phoneCol.setCellValueFactory( new PropertyValueFactory("phone"));
phoneCol.setCellFactory(cellFactory);
phoneCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, Boolean>>() {
#Override
public void handle(CellEditEvent<Person, Boolean> event) {
if (event.getRowValue() instanceof ControlPerson) {
for (Person p : data) {
p.setPhone(event.getNewValue());
}
} else {
Person p = event.getRowValue();
p.setPhone( event.getNewValue() );
}
}
});
return phoneCol;
}
/**
* This is the main control column in your application (containing the green and red circles).
*
* #param cellFactory
* #return
*/
private TableColumn createContactColumn( Callback<TableColumn, TableCell> cellFactory ) {
TableColumn contactCol = new TableColumn("Contact");
contactCol.setMinWidth(75);
contactCol.setCellValueFactory( new PropertyValueFactory( "contact"));
contactCol.setCellFactory(cellFactory);
contactCol.setOnEditCommit(new EventHandler<CellEditEvent<Person, Boolean>>() {
#Override
public void handle(CellEditEvent<Person, Boolean> event) {
//This handler is different to the other two as it controls the checking/unchecking of both
//the whole table and individual rows.
if (event.getRowValue() instanceof ControlPerson) {
for (Person p : data) {
p.setContact(event.getNewValue());
p.setEmail(event.getNewValue());
p.setPhone(event.getNewValue());
}
//This is just an example of how you would control a special "everything" flag.
//You could call any method or take any action here to deal with the special
//case where everything is selected.
everything = event.getNewValue();
} else {
//Set the state of any boolean properties to modify the whole row.
Person p = event.getRowValue();
p.setContact( event.getNewValue() );
p.setEmail(event.getNewValue());
p.setPhone(event.getNewValue());
}
}
});
return contactCol;
}
}
package samplefx2_original_v7;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
public class Person {
private final SimpleBooleanProperty contact;
private final SimpleStringProperty name;
private final SimpleBooleanProperty email;
private final SimpleBooleanProperty phone;
public Person(boolean contact, String name, boolean email, boolean phone) {
this.contact = new SimpleBooleanProperty( contact );
this.name = new SimpleStringProperty(name);
this.email = new SimpleBooleanProperty( email );
this.phone = new SimpleBooleanProperty( phone );
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
public SimpleStringProperty nameProperty() {
return name;
}
public boolean isContact() {
return contact.get();
}
public void setContact( boolean contact ) {
this.contact.set( contact );
}
public SimpleBooleanProperty contactProperty() {
return contact;
}
public boolean isEmail() {
return email.get();
}
public void setEmail( boolean email ) {
this.email.set( email );
}
public SimpleBooleanProperty emailProperty() {
return email;
}
public boolean isPhone() {
return contact.get();
}
public void setPhone( boolean phone ) {
this.phone.set( phone );
}
public SimpleBooleanProperty phoneProperty() {
return phone;
}
}
package samplefx2_original_v7;
/**
* This subclass of Person is used only to identify the row(s) that is used to control
* the checking and unchecking of columns.
*/
public class ControlPerson extends Person {
public ControlPerson(boolean active, String name, boolean email, boolean phone) {
super(active, name, email, phone);
}
}
package samplefx2_original_v7;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
public class CheckBoxCell extends TableCell<Person, Boolean> {
private CheckBox checkBox;
public CheckBoxCell() {
if (checkBox == null) {
checkBox = new CheckBox();
checkBox.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
CheckBox cb = (CheckBox) event.getSource();
getTableView().edit(getTableRow().getIndex(), getTableColumn());
commitEdit(cb.isSelected());
}
});
}
setGraphic(checkBox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
//Center align the checkboxes.
setAlignment(Pos.CENTER);
}
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (item == null) {
//If we don't have an item don't draw the checkbox.
setGraphic( null );
// checkBox.setDisable(true);
// checkBox.setSelected(false);
} else {
checkBox.setDisable(false);
checkBox.setSelected(item);
}
}
}
A couple of things are going on here. A simple thing to note is that you don't set the graphic when the cell changes from empty to non-empty. You need:
#Override
public void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (item == null) {
//If we don't have an item don't draw the checkbox.
setGraphic( null );
// checkBox.setDisable(true);
// checkBox.setSelected(false);
} else {
checkBox.setDisable(false);
checkBox.setSelected(item);
setGraphic(checkBox);
}
}
But probably more of an issue is that your table cell implementation really relies on undocumented behavior, in that it forces the table into editing mode (getTableView().edit(...)) and (as far as I can tell) seems to rely on some side effects of this in order to get the functionality you want. Because the behavior is undocumented, there's no guarantee it will remain the same in new releases, so I'm not too surprised it breaks under Java 8.
A better way to implement the cells is just to use listeners (or bindings) to exchange data between the check box's selectedProperty and the property in the Person class. Because the display is always the same (i.e. a check box) no matter whether the cell is being edited or not, there's no really need to work with the editing API from the cell class. This is the approach the standard CheckBoxTableCell introduced in JavaFX 2.2 works.
Assuming the functionality you want is that the "contact" column check boxes should select both the "email" and "phone" checkboxes, I would approach this slightly differently. There's really no logical need to have a contact property in the model class (Person), because that information is already incorporated in the other two boolean properties. (Another way to think of this is that the "control" checkboxes are just UI components and not really part of the data, so they have no place in the model.) Your "contact" column doesn't really have a property associated with it; just make that a TableColumn<Person, Person> and have its cells update the other properties appropriately.
Also, your ControlPerson is a bit of a hack. Again this row in the table is really just for user interface purposes and is not part of the data. So it really shouldn't appear as part of the table's items. I would put the "select all" check boxes in the table header, not in the data part of the table. This will change their appearance some (though you can always fix that with CSS if you need) but the benefit is that they remain visible when you scroll, which is probably desirable.
Here's a complete example which works this way, and runs under both Java 7 (JavaFX 2.2) and under Java 8. (I tested under 1.7.0_65, 1.8.0_25, and 1.8.0_40.)
import java.util.Arrays;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class SampleFX2 extends Application {
#Override
public void start(Stage primaryStage) {
ObservableList<Person> data = FXCollections.observableArrayList(
new Callback<Person, Observable[]>() {
#Override
public Observable[] call(Person person) {
return new Observable[] {person.emailProperty(), person.phoneProperty()};
}
});
data.addAll(Arrays.asList(
new Person("Jacob Smith", true, false),
new Person("Isabella Johnson", true, true),
new Person("Ethan Williams", false, false),
new Person("Emma Jones", false, true),
new Person("Michael Brown", true, true)));
final TableView<Person> table = new TableView<>();
table.setItems(data);
TableColumn<Person, String> nameCol = new TableColumn<>("Name");
nameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("name"));
final CheckBox allContactCheckBox = new CheckBox() ;
final CheckBox allEmailCheckBox = new CheckBox();
final CheckBox allPhoneCheckBox = new CheckBox();
updateHeaderCheckBoxes(table, allContactCheckBox, allEmailCheckBox, allPhoneCheckBox);
data.addListener(new ListChangeListener<Person>() {
#Override
public void onChanged(Change<? extends Person> change) {
updateHeaderCheckBoxes(table, allContactCheckBox, allEmailCheckBox, allPhoneCheckBox);
}
});
allPhoneCheckBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean wasSelected, Boolean isSelected) {
for (Person person : table.getItems()) {
person.setPhone(isSelected);
}
}
});
allEmailCheckBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean wasSelected, Boolean isSelected) {
for (Person person : table.getItems()) {
person.setEmail(isSelected);
}
}
});
allContactCheckBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean wasSelected, Boolean isSelected) {
for (Person person : table.getItems()) {
person.setPhone(isSelected);
person.setEmail(isSelected);
}
}
});
TableColumn<Person, Person> contactCol = new TableColumn<>();
contactCol.setPrefWidth(75);
contactCol.setGraphic(createTableHeader("Contact", allContactCheckBox));
contactCol.setCellValueFactory(new Callback<CellDataFeatures<Person, Person>, ObservableValue<Person>>() {
#Override
public ObservableValue<Person> call(
CellDataFeatures<Person, Person> person) {
return new ReadOnlyObjectWrapper<>(person.getValue());
}
});
contactCol.setCellFactory(new Callback<TableColumn<Person, Person>, TableCell<Person, Person>>() {
#Override
public TableCell<Person, Person> call(
TableColumn<Person, Person> param) {
return new ContactCell();
}
});
TableColumn<Person, Boolean> emailCol = new TableColumn<>();
emailCol.setPrefWidth(75);
emailCol.setGraphic(createTableHeader("Email", allEmailCheckBox));
emailCol.setCellValueFactory(new PropertyValueFactory<Person, Boolean>("email"));
emailCol.setCellFactory(new Callback<TableColumn<Person, Boolean>, TableCell<Person, Boolean>>() {
#Override
public TableCell<Person, Boolean> call(
TableColumn<Person, Boolean> column) {
return new CheckBoxCell<Person>(new Callback<Person, BooleanProperty>() {
#Override
public BooleanProperty call(Person person) {
return person.emailProperty();
}
});
}
});
TableColumn<Person, Boolean> phoneCol = new TableColumn<>();
phoneCol.setPrefWidth(75);
phoneCol.setGraphic(createTableHeader("Phone", allPhoneCheckBox));
phoneCol.setCellValueFactory(new PropertyValueFactory<Person, Boolean>("phone"));
phoneCol.setCellFactory(new Callback<TableColumn<Person, Boolean>, TableCell<Person, Boolean>>() {
#Override
public TableCell<Person, Boolean> call(
TableColumn<Person, Boolean> column) {
return new CheckBoxCell<Person>(new Callback<Person, BooleanProperty>() {
#Override
public BooleanProperty call(Person person) {
return person.phoneProperty();
}
});
}
});
table.getColumns().add(nameCol);
table.getColumns().add(contactCol);
table.getColumns().add(emailCol);
table.getColumns().add(phoneCol);
Button showButton = new Button("Debug");
showButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent evt) {
for (Person p : table.getItems()) {
System.out.println(p.getName() + " " + p.isEmail() +" "+ p.isPhone());
}
System.out.println();
}
});
HBox controls = new HBox(5);
controls.setAlignment(Pos.CENTER);
controls.setPadding(new Insets(10));
controls.getChildren().add(showButton);
BorderPane root = new BorderPane();
root.setCenter(table);
root.setBottom(controls);
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private Node createTableHeader(String text, CheckBox checkBox) {
VBox header = new VBox(2);
header.setPadding(new Insets(2));
header.getChildren().addAll(new Label(text), checkBox);
header.setAlignment(Pos.CENTER);
return header ;
}
private static void updateHeaderCheckBoxes(final TableView<Person> table,
final CheckBox allContactCheckBox, final CheckBox allEmailCheckBox,
final CheckBox allPhoneCheckBox) {
boolean allPhoneSelected = true ;
boolean noPhoneSelected = true ;
boolean allEmailSelected = true ;
boolean noEmailSelected = true ;
for (Person person : table.getItems()) {
if (person.isEmail()) {
noEmailSelected = false ;
} else {
allEmailSelected = false ;
}
if (person.isPhone()) {
noPhoneSelected = false ;
} else {
allPhoneSelected = false ;
}
}
setCheckBoxState(allPhoneSelected, noPhoneSelected, allPhoneCheckBox);
setCheckBoxState(allEmailSelected, noEmailSelected, allEmailCheckBox);
setCheckBoxState(allPhoneSelected && allEmailSelected, noPhoneSelected && noEmailSelected, allContactCheckBox);
}
private static void setCheckBoxState(boolean on, boolean off, CheckBox checkBox) {
if (on) {
checkBox.setIndeterminate(false);
checkBox.setSelected(true);
} else if (off) {
checkBox.setIndeterminate(false);
checkBox.setSelected(false);
} else {
checkBox.setIndeterminate(true);
}
}
public static class ContactCell extends TableCell<Person, Person> {
private final CheckBox checkBox ;
public ContactCell() {
checkBox = new CheckBox();
final ChangeListener<Boolean> propertyListener = new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean oldValue, Boolean newValue) {
updateCheckBox();
}
};
checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean oldValue, Boolean newValue) {
Person person = getItem();
person.setEmail(newValue);
person.setPhone(newValue);
}
});
itemProperty().addListener(new ChangeListener<Person>() {
#Override
public void changed(
ObservableValue<? extends Person> observable,
Person oldValue, Person newValue) {
if (oldValue != null) {
oldValue.emailProperty().removeListener(propertyListener);
oldValue.phoneProperty().removeListener(propertyListener);
}
if (newValue != null) {
newValue.emailProperty().addListener(propertyListener);
newValue.phoneProperty().addListener(propertyListener);
updateCheckBox();
}
}
});
emptyProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean wasEmpty, Boolean isEmpty) {
if (isEmpty) {
setGraphic(null);
} else {
setGraphic(checkBox);
}
}
});
setAlignment(Pos.CENTER);
}
private void updateCheckBox() {
Person person = getItem();
if (person != null) {
setCheckBoxState(person.isEmail() && person.isPhone(), ! person.isEmail() && ! person.isPhone(), checkBox);
if (person.isEmail() && person.isPhone()) {
checkBox.setSelected(true);
} else if (! person.isEmail() && ! person.isPhone()) {
checkBox.setSelected(false);
} else {
checkBox.setIndeterminate(true);
}
checkBox.setIndeterminate(person.isEmail() != person.isPhone());
}
}
}
public static class CheckBoxCell<T> extends TableCell<T, Boolean> {
private final CheckBox checkBox ;
public CheckBoxCell(final Callback<T, BooleanProperty> propertyMapper) {
checkBox = new CheckBox();
checkBox.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(
ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
#SuppressWarnings("unchecked") // for some reason getTableRow() returns a raw type...
TableRow<T> row = getTableRow();
if (row != null) {
T item = row.getItem();
if (item != null) {
BooleanProperty property = propertyMapper.call(item);
property.set(newValue);
}
}
}
});
itemProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(
ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
if (newValue != null) {
checkBox.setSelected(newValue);
}
}
});
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setAlignment(Pos.CENTER);
emptyProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(
ObservableValue<? extends Boolean> observable,
Boolean oldValue, Boolean newValue) {
if (newValue) {
setGraphic(null);
} else {
setGraphic(checkBox);
}
}
});
}
}
public static class Person {
private final StringProperty name;
private final BooleanProperty email;
private final BooleanProperty phone;
public Person(String name, boolean email, boolean phone) {
this.name = new SimpleStringProperty(name);
this.email = new SimpleBooleanProperty( email );
this.phone = new SimpleBooleanProperty( phone );
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
public StringProperty nameProperty() {
return name;
}
public boolean isEmail() {
return email.get();
}
public void setEmail( boolean email ) {
this.email.set( email );
}
public BooleanProperty emailProperty() {
return email;
}
public boolean isPhone() {
return phone.get();
}
public void setPhone( boolean phone ) {
this.phone.set( phone );
}
public BooleanProperty phoneProperty() {
return phone;
}
}
public static void main(String[] args) {
launch(args);
}
}
Related
I'm new to android programming and I have a problem in displaying data from firestore. I want to display the data in fragments in the navigation drawer. I found tutorials that display it on activities but nothing in fragments. Please help me with this.
public class MainActivity extends AppCompatActivity {
Toolbar toolbar;
DrawerLayout drawerLayout;
ActionBarDrawerToggle actionBarDrawerToggle;
NavigationView navigationView;
FragmentTransaction fragmentTransaction;
private boolean shouldLoadProductFragOnBackPress = false;
public static int navItemIndex = 0;
public FirebaseAuth mAuth;
FirebaseFirestore db = FirebaseFirestore.getInstance();
private void Load_Product_fragment() {
navItemIndex = 0;
fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.maincontainer,new ProductFragment());
fragmentTransaction.commit();
getSupportActionBar().setTitle(R.string.Productfragment_Title);
drawerLayout.closeDrawers();
}
private void Load_Service_fragment(){
navItemIndex = 1;
fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.maincontainer,new ServiceFragment());
fragmentTransaction.commit();
getSupportActionBar().setTitle(R.string.Servicefragmnet_Title);
drawerLayout.closeDrawers();
shouldLoadProductFragOnBackPress = true;
}
private void Load_Account_fragment(){
navItemIndex = 2;
fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.maincontainer,new AccountFragment());
fragmentTransaction.commit();
getSupportActionBar().setTitle(R.string.Accountfragment_Title);
drawerLayout.closeDrawers();
shouldLoadProductFragOnBackPress = true;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar=(Toolbar)findViewById(R.id.Toolbar_Layout);
setSupportActionBar(toolbar);
drawerLayout=(DrawerLayout) findViewById(R.id.Drawer_Layout);
actionBarDrawerToggle = new ActionBarDrawerToggle(this,drawerLayout,toolbar,R.string.drawer_open,R.string.drawer_close);
drawerLayout.addDrawerListener(actionBarDrawerToggle);
drawerLayout.openDrawer(Gravity.LEFT);
Load_Product_fragment();
navigationView= findViewById(R.id.Navigation_View);
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId())
{
case R.id.Productsfragment_ND:
Load_Product_fragment();
item.setChecked(true);
break;
case R.id.Servicefragment_ND:
Load_Service_fragment();
item.setChecked(true);
break;
case R.id.Accountfragemnt_ND:
Load_Account_fragment();
item.setChecked(true);
break;
}
return false;
}
});
}
#Override
public void onBackPressed() {
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.closeDrawers();
return;
}
// This code loads home fragment when back key is pressed
// when user is in other fragment than home
if (shouldLoadProductFragOnBackPress) {
// checking if user is on other navigation menu
// rather than home
if (navItemIndex !=0) {
navItemIndex = 0;
shouldLoadProductFragOnBackPress = false;
Load_Product_fragment();
return;
}
}
super.onBackPressed();
}
#Override
protected void onPostCreate(#Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
actionBarDrawerToggle.syncState();
}
public void del(View view) {db.collection("products").document("test")
.delete()
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
}
});
}
}
This is my main-activity.It contains the requirements for a navigation drawer for 3 fragments.I have a Firestore database linked to the app. I need to display the contents of the database in these fragments.
Next i will show one fragment and the recyclerview adapter and view holder i have tried using.
I am not sure if it is the correct way to do it.Please help me with this
public class ProductFragment extends Fragment {
private static final String TAG = ProductFragment.class.getSimpleName();
private RecyclerView recipeRecyclerview;
private LinearLayoutManager linearLayoutManager;
private Product_adapter mAdapter;
private DatabaseReference mDatabaseRef;
private DatabaseReference childRef;
public ProductFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate (R.layout.fragment_product, container, false);
getActivity().setTitle(getString(R.string.Productfrag_title));
linearLayoutManager = new LinearLayoutManager(getActivity());
recipeRecyclerview = view.findViewById(R.id.List_recycleview);
recipeRecyclerview.setHasFixedSize(true);
mDatabaseRef = FirebaseDatabase.getInstance().getReference();
childRef = mDatabaseRef.child("recipes");
mAdapter = new Product_adapter(Product_response.class, R.layout.list_layout, View_holder.class, childRef, getContext());
recipeRecyclerview.setLayoutManager(linearLayoutManager);
recipeRecyclerview.setAdapter(mAdapter);
return view;
}
}
This is my products-fragment. I have tried to add the recycler view.
Adapter
public class Product_adapter extends RecyclerView.Adapter {
FirebaseFirestore db = FirebaseFirestore.getInstance();
private Context context;
Query query = db.collection("products");
FirestoreRecyclerOptions<Product_response> response = new FirestoreRecyclerOptions.Builder<Product_response>()
.setQuery(query, Product_response.class)
.build();
FirestoreRecyclerAdapter adapter = new FirestoreRecyclerAdapter<Product_response, View_holder>(response) {
#Override
protected void onBindViewHolder(View_holder holder, int position, Product_response model) {
}
#Override
public View_holder onCreateViewHolder(ViewGroup group, int i) {
// Create a new instance of the ViewHolder, in this case we are using a custom
// layout called R.layout.message for each item
View view = LayoutInflater.from(group.getContext())
.inflate(R.layout.list_layout, group, false);
return new View_holder(view);
}
};
public Product_adapter(Class<Product_response> product_responseClass, int list_layout, Class<View_holder> view_holderClass, DatabaseReference childRef, Context context) {
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
}
#Override
public int getItemCount() {
return 0;
}
}
View holder
public class View_holder extends RecyclerView.ViewHolder{
private static final String TAG = View_holder.class.getSimpleName();
public TextView main_text, subtext ;
public ImageView image;
public View_holder(View itemView) {
super(itemView);
main_text = (TextView)itemView.findViewById(R.id.List_maintext);
subtext = (TextView)itemView.findViewById(R.id.List_subtext);
image = (ImageView)itemView.findViewById(R.id.List_imageview);
}
}
Next i will show the response page where i used the getter and setter for the firebase
import com.google.firebase.firestore.IgnoreExtraProperties;
#IgnoreExtraProperties
public class Product_response {
private String Product;
private String Cost;
public Product_response() {
}
public Product_response(String Product, String Cost){
this.Product= Product;
this.Cost= Cost;
}
public String getProduct() {
return Product;
}
public void setProduct(String Product) {
this.Product = Product;
}
public String getCost(){
return Cost;
}
public void setCost(String Cost)
{
this.Cost = Cost;
}
}
This is how my database looks like. I need to display this products in my fragment
This is the github link of the app. Please help me complete this
I try to make my comboBox multiple selection
and this my code:
this multiSelectComboBox:
import java.util.List;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.WindowEvent;
import com.extjs.gxt.ui.client.event.WindowListener;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.widget.CheckBoxListView;
import com.extjs.gxt.ui.client.widget.Dialog;
import com.extjs.gxt.ui.client.widget.form.TriggerField;
import com.extjs.gxt.ui.client.widget.layout.FillLayout;
import com.google.gwt.user.client.Element;
public class MultiSelectComboBox<D extends ModelData> extends TriggerField<String> {
private Dialog checkBoxListHolder;
private CheckBoxListView<D> listView;
private ListStore<D> store;
private String delimiter = ",";
private boolean readOnly;
public MultiSelectComboBox() {
store = new ListStore<D>();
listView = new CheckBoxListView<D>();
}
#Override
protected void onTriggerClick(ComponentEvent ce) {
super.onTriggerClick(ce);
checkBoxListHolder.setSize(getWidth(), 200);
listView.setWidth(getWidth());
checkBoxListHolder.setPosition(getAbsoluteLeft(),
getAbsoluteTop() + getHeight());
if(checkBoxListHolder.isVisible()) {
checkBoxListHolder.hide();
}
else {
checkBoxListHolder.show();
}
}
#Override
protected void onRender(Element target, int index) {
super.onRender(target, index);
checkBoxListHolder = new Dialog();
checkBoxListHolder.setClosable(false);
checkBoxListHolder.setHeaderVisible(false);
checkBoxListHolder.setFooter(false);
checkBoxListHolder.setFrame(false);
checkBoxListHolder.setResizable(false);
checkBoxListHolder.setAutoHide(false);
checkBoxListHolder.getButtonBar().setVisible(false);
checkBoxListHolder.setLayout(new FillLayout());
checkBoxListHolder.add(listView);
listView.setStore(store);
checkBoxListHolder.addWindowListener(new WindowListener(){
#Override
public void windowHide(WindowEvent we) {
setValue(parseCheckedValues(listView));
}
});
}
private String parseCheckedValues(CheckBoxListView<D> checkBoxView) {
StringBuffer buf = new StringBuffer();
if(checkBoxView != null) {
List<D> selected = checkBoxView.getChecked();
int index = 1, len = selected.size();
for(D c : selected) {
buf.append(c.get(listView.getDisplayProperty()));
if(index < len) {
buf.append(delimiter);
}
index++;
}
}
return buf.toString();
}
public CheckBoxListView<D> getListView() {
return listView;
}
public void setListView(CheckBoxListView<D> listView) {
this.listView = listView;
}
public ListStore<D> getStore() {
return store;
}
public void setStore(ListStore<D> store) {
this.store = store;
}
public String getDelimiter() {
return delimiter;
}
public void setDelimiter(String delimiter) {
this.delimiter = delimiter;
}
public boolean isReadOnly() {
return readOnly;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
}
then use this class in my project:
// ...
ListStore<UserDTO> employeeList = getUsers();
MultiSelectComboBox<ModelData> multiUsers = new MultiSelectComboBox<ModelData>();
multiUsers.getListView().setDisplayProperty("label");
//multiUsers.setStore(employeeList);
multiUsers.getStore().add(getModelData());
multiUsers.setFieldLabel("Employee");
//multiUsers.setEditable(true);
//multiUsers.setMaxHeight(200);
simpleForm.add(multiUsers, formData);
// ...
private List<ModelData> getModelData() {
List<ModelData> list = new ArrayList<ModelData>();
List<NctrUserDTO> employees= employeeList.getModels();
for (int i=0; i<employeeList.getCount(); i++) {
ModelData data = new BaseModel();
data.set("label", employeeList.getAt(i).getName());
list.add(data);
}
return list;
}
but the comboBox appear empty or have not any values when i pass values from employeeList but when i tried with static string values this works fine, i dont know why this problem occur, any suggestion please ??
It sounds as if your looking for something like the option multiple select boxes, if so you can look up the source code on Github for pointers - or rather than re-inventing the wheel, just use that GwtBootstrap3 component
I want to create a simple table with multiple selection using checkboxes on the first column.
I've created a custom "TableColumn" to handle this feature in every table I need. I've implemented all the features except one: when I select or deselect a single row the corresponding checkbox on the first colum must be selected or unselected. I need help to develop this last function.
public class ColumnSelectRow extends TableColumn{
private CheckBox headerCheckBox = new CheckBox();
public ColumnSelectRow(String title) {
super(title);
setSortable(false);
setPrefWidth(100);
setMaxWidth(100);
setMinWidth(100);
setResizable(false);
setGraphic(headerCheckBox);
setCellValueFactory( new Callback<TableColumn.CellDataFeatures<Object, Boolean>, ObservableValue<Boolean>>() {
#Override
public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<Object, Boolean> p) {
return new SimpleBooleanProperty(p.getValue() != null);
}
});
setCellFactory(new Callback<javafx.scene.control.TableColumn<Object, Boolean>, TableCell<Object, Boolean>>() {
#Override
public TableCell<Object, Boolean> call(javafx.scene.control.TableColumn<Object, Boolean> p) {
return new SelectButtonCell();
}
});
headerCheckBox.setOnAction(t->{
if(disableEvents) return;
TableView table = ColumnSelectRow.this.getTableView();
if (table == null) return;
if(headerCheckBox.isSelected()) table.getSelectionModel().selectAll();
else table.getSelectionModel().clearSelection();
});
}
public CheckBox getHeaderCheckBox() {
return headerCheckBox;
}
private boolean disableEvents = false;
private class SelectButtonCell extends TableCell {
private final CheckBox checkBox = new CheckBox();
public SelectButtonCell(){
setAlignment(Pos.CENTER);
checkBox.setOnAction(t -> {
int row = getTableRow().getIndex();
TableView table = SelectButtonCell.this.getTableView();
if (table == null) return;
if(checkBox.isSelected()) table.getSelectionModel().select(row);
else table.getSelectionModel().clearSelection(row);
table.getFocusModel().focus(row);
boolean empty = table.getSelectionModel().isEmpty();
boolean full = table.getSelectionModel().getSelectedCells().size()==table.getItems().size();
disableEvents = true;
headerCheckBox.setIndeterminate(false);
if(empty) headerCheckBox.setSelected(false);
else if(full) headerCheckBox.setSelected(true);
else headerCheckBox.setIndeterminate(true);
disableEvents = false;
});
}
public CheckBox getCheckBox() {
return checkBox;
}
#Override
protected void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if(!empty){
setGraphic(checkBox);
}
int row = getTableRow().getIndex();
TableView table = SelectButtonCell.this.getTableView();
checkBox.setSelected(table.getSelectionModel().isSelected(row));
}
public boolean isCheckBoxSelected(){
return checkBox.isSelected();
}
}
thanks!!
Edit: a custom table with the first column selectable
import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
public class MyTable extends Application {
private ColumnSelectRow columSelect;
#Override
public void start(Stage primaryStage) throws Exception {
columSelect = new ColumnSelectRow();
TableView T = new TableView<>();
T.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
ObservableList<myObject> al = FXCollections.observableArrayList();
al.add(new myObject(1));
al.add(new myObject(2));
al.add(new myObject(3));
T.setItems(al);
T.getColumns().add(columSelect);
TableColumn<myObject, Number> number = new TableColumn("Number");
number.setCellValueFactory(cellData->cellData.getValue().propetry);
T.getColumns().add(number);
Scene scene = new Scene(T);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private class myObject{
public SimpleIntegerProperty propetry = new SimpleIntegerProperty();
public myObject(int i) {
propetry.set(i);
}
}
}
I have 1 issue in my project.
I'd like to have a tableColum built with comboBox, and for this i use:
tableColumn.setCellFactory(ComboBoxTableCell.forTableColumn(cbValues));
it works but like DOC says:
By default, the ComboBoxTableCell is rendered as a Label when not being edited, and as a ComboBox when in editing mode.
but i want to see the comboBox every time, so i build this code
tableColumn.setCellFactory(new Callback<TableColumn<Ambientale, Integer>, TableCell<Ambientale, Integer>>() {
#Override
public TableCell<Ambientale, Integer> call(TableColumn<Ambientale, Integer> param) {
return new ComboBoxCell(cbValues);
}
});
and my ComboBoxCell is:
class ComboBoxCell extends TableCell<Ambientale, Integer> implements Callback<Object, Object>{
private ComboBox combo;
public ComboBoxCell() {
combo = new ComboBox();
}
public ComboBoxCell(ObservableList items) {
combo = new ComboBox();
combo.setItems(items);
combo.setOnAction(new EventHandler() {
#Override
public void handle(Event event) {
System.out.println(" it's works");
commitEdit((Integer)combo.getSelectionModel().getSelectedItem());
}
});
}
#Override
public void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
combo.getSelectionModel().select(item);
setGraphic(combo);
setContentDisplay(ContentDisplay.CENTER);
}
}
#Override
public Object call(Object param) {
return new ComboBoxCell();
}
}
the issue is here!
class ComboBoxCell extends TableCell<Ambientale, Integer> implements Callback<Object, Object>{
private ComboBox combo;
public ComboBoxCell() {
combo = new ComboBox();
}
public ComboBoxCell(ObservableList items) {
combo = new ComboBox();
combo.setItems(items);
combo.setOnAction(new EventHandler() {
#Override
public void handle(Event event) {
System.out.println(" it's works");
commitEdit((Integer)combo.getSelectionModel().getSelectedItem());
}
});
}
#Override
public void updateItem(Integer item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
combo.getSelectionModel().select(item);
setGraphic(combo);
setContentDisplay(ContentDisplay.CENTER);
}
}
#Override
public Object call(Object param) {
return new ComboBoxCell();
}
}
commitEdit(...) doesn't call:
tableColumn.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<Ambientale, Integer>>() {
#Override
public void handle(TableColumn.CellEditEvent<Ambientale, Integer> t) { //aggiornare database cella singola
System.out.println("not called");
//other stuffs
}
});
Can you help me please.
It works, i don't know why but it works.
i've added this on my ComboBoxCell
combo.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
getTableView().edit(getIndex(), getTableColumn());
} else {
commitEdit((Integer)combo.getSelectionModel().getSelectedItem());
}
}
});
I'm still new to JavaFX and need to create a combobox with objects (SimlpePerson) and not strings. I want to edit the shown value in the box itself. Works good for strings but I have problems with SimpleObjects. I made a StringConverter and it also works, I can edit the object shown in the comboBox. But the list itself is not rerendered after that. If I click on the ComboBox I see the original values. How can I change that?
Any suggestion is very welcome!=)
BR and Thank you!
Stefan
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TableView;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class ComboBoxDemo extends Application{
public class SimplePerson {
private StringProperty name;
private String somethingElse;
public SimplePerson(String name) {
setName(name);
}
public final void setName(String value) { nameProperty().set(value); }
public String getName() { return nameProperty().get(); }
public StringProperty nameProperty() {
if (name == null) name = new SimpleStringProperty(this, "name");
return name;
}
}
final ObservableList<SimplePerson> persons = FXCollections.observableArrayList(
new SimplePerson("Jacob"),
new SimplePerson("Isabella"),
new SimplePerson("Ethan"),
new SimplePerson("Emma"),
new SimplePerson("Michael")
);
#Override
public void start(Stage stage) throws Exception {
// TODO Auto-generated method stub
final ComboBox cb = new ComboBox();
cb.setItems(persons);
cb.setEditable(true);
cb.setConverter(new StringConverter<SimplePerson>() {
#Override
public String toString(SimplePerson p)
{
if(p != null)
return p.getName();
return "";
}
#Override
public SimplePerson fromString(String name)
{
if(cb.getValue() != null)
{
((SimplePerson)cb.getValue()).setName(name);
cb.show();
return (SimplePerson)cb.getValue();
}
return null;
}
});
stage.setScene(new Scene(cb));
stage.show();
}
public static void main(String[] args) { launch(args); }
}
Check out this solution. There is a handler which is triggered when you've finished editing. There you may implement all the code which changes the model's state.
To update the combobox list the following approach work may:
cb.getEditor().setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
SimplePerson person = cb.getValue();
if (null != person) {
int idx = persons.indexOf(person);
person.setName(cb.getEditor().getText());
persons.set(idx, person);
cb.setValue(person);
}
}
});