Customize jfoenix slider indicator - javafx

For the jfoenix slider, the indicator always changed with the slider and show the value accordingly. How can we customize the value in indicate, for example when slide the value from 0-4 we want to show "Small" on the indicate.

I've digged out the answer by myself, just post it here in case someone else need that appreciate it if you can let me know some better ones. This one is for formatting the value in the indicator to one decimal point and of course you can add some logic to show some string based on the value.
yourSlider.setValueFactory(new Callback<JFXSlider, StringBinding>() {
#Override
public StringBinding call(JFXSlider arg0) {
return Bindings.createStringBinding(new java.util.concurrent.Callable<String>(){
#Override
public String call() throws Exception {
DecimalFormat df = new DecimalFormat("#.0");
return df.format(yourSlider.getValue());
}
}, yourSlider.valueProperty());
}
});

Related

Persistent header fragment (disable animation) in Android TV (leanback)

Anyone knows how to achieve the question in the title? The objective is to avoid the animation that results in the Headers bar disappearing as the Leanback app zooms in on the Row Item once a Header has been clicked.
setHeadersState of BrowseSupportFragment doesn't help. Perhaps something to do with hijacking startHeadersTransitionInternal during OnHeaderClickedListener? If so, any idea how to correctly implement it?
So the problem with this one is that the transition is handled by the method startHeadersTransitionInternal which is package private. Because of this, you can't override it in most situations. However, since it's only package private and not private private, there's a little hack around this that you can do.
First, make a package in your app with the same package name as BrowseSupportFragment. Then make a class in that package that extends BrowseSupportFragment and override the offending method with no implementation. That'd look something like this:
package android.support.v17.leanback.app; // Different for AndroidX
public class HackyBrowseSupportFragment extends BrowseSupportFragment {
#Override
void startHeadersTransitionInternal(boolean withHeaders) {
// Do nothing. This avoids the transition.
}
}
Then, instead of extending BrowseSupportFragment, you'd extend HackyBrowseSupportFragment.
One thing to note that I found with this is that the back button will no longer refocus the headers from one of the rows, so you'll have to do that manually. Other than that, seems to work just fine.
Following #MichaelCeley 's response and based on the original startHeadersTransitionInternal method from BrowseSupportFragment, this implementation that keeps the backstack and listeners works, too.
#Override
void startHeadersTransitionInternal(final boolean withHeaders) {
if (getFragmentManager().isDestroyed()) {
return;
}
if (!isHeadersDataReady()) {
return;
}
new Runnable() {
#Override
public void run() {
if (mBrowseTransitionListener != null) {
mBrowseTransitionListener.onHeadersTransitionStart(withHeaders);
}
if (mHeadersBackStackEnabled) {
if (!withHeaders) {
getFragmentManager().beginTransaction()
.addToBackStack(mWithHeadersBackStackName).commit();
} else {
int index = mBackStackChangedListener.mIndexOfHeadersBackStack;
if (index >= 0) {
FragmentManager.BackStackEntry entry = getFragmentManager().getBackStackEntryAt(index);
getFragmentManager().popBackStackImmediate(entry.getId(),
FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
}
}
}.run();
}

Javafx : ComboBoxTableCell - how to select a value in one click?

I have a TableView with a ComboBoxTableCell, when using the default implementation the user have to click three times to select a value from of the ComboBox's list.
I want when the user clicks on the cell to show the combo box list. I based my solution on this one:
JavaFX editable ComboBox in a table view
The cell does get into edit mode (startEdit() is called) but it takes another click to show the list of values, what am I missing?
table.addEventHandler(MouseEvent.MOUSE_CLICKED, (e) ->
{
if (table.getEditingCell() == null)
{
TablePosition focusedCellPos = table.getFocusModel().getFocusedCell();
table.edit(focusedCellPos.getRow(), focusedCellPos.getTableColumn());
}
});
Thanks.
Interesting problem - bubbling up again after quite a while :)
Looks like the approach of the OP is indeed working (as of fx11, some bugs around its editing seem to be fixed) - with a little help from the combo cell:
start editing in a single click handler on the tableView (from OP)
extend ComboBoxTableCell and override its startEdit to open the dropDown
Code snippet:
// set editable to see the combo
table.setEditable(true);
// keep approach by OP
table.addEventHandler(MouseEvent.MOUSE_CLICKED, (e) -> {
TablePosition<Person, ?> focusedCellPos = table.getFocusModel()
.getFocusedCell();
if (table.getEditingCell() == null) {
table.edit(focusedCellPos.getRow(),
focusedCellPos.getTableColumn());
}
});
// use modified standard combo cell shows its popup on startEdit
firstName.setCellFactory(cb -> new ComboBoxTableCell<>(firstNames) {
#Override
public void startEdit() {
super.startEdit();
if (isEditing() && getGraphic() instanceof ComboBox) {
// needs focus for proper working of esc/enter
getGraphic().requestFocus();
((ComboBox<?>) getGraphic()).show();
}
}
});
Maybe not the cleanest solution to this problem, but I found a workaround to make the ComboBoxTableCells drop down its menu in just 1 click:
column.setCellFactory(new Callback<TableColumn<Person, String>, TableCell<Person, String>>() {
#Override
public TableCell<Person, String> call(TableColumn<Person, String> column) {
ComboBoxTableCell cbtCell = new ComboBoxTableCell<>(cbValues);
cbtCell.setOnMouseEntered(new EventHandler<Event>() {
#Override
public void handle(Event event) {
// Without a Person object, a combobox shouldn't open in that row
if (((Person)((TableRow)cbtCell.getParent()).getItem()) != null) {
Robot r = new Robot();
r.mouseClick(MouseButton.PRIMARY);
r.mouseClick(MouseButton.PRIMARY);
}
}
});
return cbtCell;
}
});
PS: I know that this topic is a bit old, but I also stumbled upon this problem recently and could not find any working solution to it online. As I sad, it's not the cleanest workaround, but at least it does its job. ;)

Restrict user to give input up to 3 characters in javafx

I am new to javaFx so please ignore my silly question here I want to validate (restrict) user to give 3 characters input (if user put more input, it should not allowed or after 3 characters, no latter should be visible) I found many solutions for validation but it does not restricting up to 3 characters as well as it little confusing to understand Here is my code.
public class editController {
#FXML
private TextField countrycode;
public void add(ActionEvent event) {
String ADD=countrycode.getText();
try {
if(ADD.isEmpty()){
Alert alert=new Alert(Alert.AlertType.ERROR);
alert.setHeaderText(null);
alert.setContentText("Please Fill All DATA");
alert.showAndWait();
return;
}
FXMLLoader loader =new FXMLLoader();
loader.load(getClass().getResource("/region/newCountry.fxml").openStream());
Validate using textProperty Listener like:
countrycode.textProperty().addListener(new ChangeListener<String>() {
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if (newValue.length() > 3) {
countrycode.setText(oldValue);
}
}
});
Or get a String length and check that like
int length = ADD.length();
if (length > 3) {
System.out.println("Please Enter Lessthen 3 character");
}
you have to #Override the replaceText(int,int,String) method of your textField this way:
#Override
public void replaceText(int start, int end, String text) {
if ("".equals(text)) { // handles delete
super.replaceText(start, end, text);
return;
}
if (getText().length() < 3) { // handles add and checks length.
super.replaceText(start, end, text);
}
}
This won't let you to insert more than 3 characters, but you can define any regexp and use instead of the second if condition.
The solution of #KeyurBhanderi is good enough until you are typing char by char. But when you try to paste from the clipboard a long text you will not see any changes in the text field. Surprise!
As alternative to avoid confusing set at least truncated string:
field.setText(newValue.substring(0, maxLimit));
In this case the end user will see a part of text and will quickly understand what is going on. Even better to show a popup window with a warning.

JavaFX: Run ChangeListener

Ho can I run the change method inside ChangeListener in the initialization.
Since it only run when changes happen. For example I have a TextField that I want to set it's value to 0 when it's empty, and I do it like this:
textField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if (!newValue.matches("\\d*")) {
textField.setText(newValue.replaceAll("[^\\d]", ""));
}
if (newValue.isEmpty())
textField.setText("0");
}
});
But at the start of the application the changed method is not called. So how to work around this issue?
There's no way to access a listener added to a property. Obviously you could just keep a reference to it:
ChangeListener<String> textFieldListener = (observable, oldValue, newValue) -> {
if (!newValue.matches("\\d*")) {
textField.setText(newValue.replaceAll("[^\\d]", ""));
}
if (newValue.isEmpty())
textField.setText("0");
};
textField.textProperty().addListener(textFieldListener);
textFieldListener.changed(null, null, textField.getText());
or, perhaps more naturally, just move the actual functionality to a different method:
textField.textProperty().addListener((observable, oldValue, newValue) -> vetoTextFieldChanges());
vetoTextFieldChanges();
// ...
private void vetoTextFieldChanges() {
String newText = textField.getText();
if (!newText.matches("\\d*")) {
textField.setText(newText.replaceAll("[^\\d]", ""));
}
if (newText.isEmpty())
textField.setText("0");
}
Note that the whole approach of watching for changes and then modifying them if they are inconsistent with your business logic is not very satisfactory. For example, if you have other listeners registered with the property, they may see the intermediate (invalid) values of the text. The supported way to do this is to use a TextFormatter. The TextFormatter both allows you to veto changes that are requested to the text, before the textProperty() is updated, and to convert the text to an appropriate value. So in your case you could do:
UnaryOperator<TextFormatter.Change> filter = change -> {
// remove any non-digit characters from inserted text:
if (! change.getText().matches("\\d*")) {
change.setText(change.getText().replaceAll("[^\\d]", ""));
}
// if new text is empty, replace all text with "0":
if (change.getControlNewText().isEmpty()) {
change.setRange(0, change.getControlText().length());
change.setText("0");
}
return change ;
};
TextFormatter<Integer> formatter = new TextFormatter<Integer>(new IntegerStringConverter(), 0, filter);
textField.setTextFormatter(formatter);
Now you can use the formatter to get the (numeric) values directly via the formatter's value property, and bind them to your model, etc:
// assume model is a data model and valueProperty() returns a Property<Integer>:
formatter.valueProperty().bindBidirectional(model.valueProperty());
Note that a binding like this will both update the formatter (and consequently the text field) when the model changes, and will also initialize it depending on your model value (thus providing a solution for your original question).

JavaFX TabPane remove/removeAll does not work

Good day, I have a problem when trying to remove tab's from a TabPane in JavaFX.
First thing I do I pass an ArrayList of String's with selected tab names from the list to the method which suppose to delete the corresponding by name tabs from the TabPane:
removeBranch.setOnAction(e->{
ArrayList<String>indexes=new ArrayList(list.getSelectionModel().getSelectedItems());
entryShifts.removeBranch(indexes);
list.getItems().removeAll(indexes);
});
Afterwards I try to delete them like this in the mentioned method:
public void removeBranch(ArrayList<String> branch){
pane.getTabs().removeAll(branch);
}
However it does not work, even if I do something like(Please notice I pass only the first name from the list):
public void removeBranch(ArrayList<String> branch){
pane.getTabs().remove(branch.get(0));
}
But if I do something like this, it works (here I only remove the first name from the ArrayList as well):
public void removeBranch(ArrayList<String> branch){
pane.getTabs().forEach(i -> {
if (i.getText().equals(branch.get(0))) {
Platform.runLater(() -> {
pane.getTabs().remove(i);
});
}
});
}
Why is this happening? Am I doing something wrong?
P.S the way I fixed it by looping through the ArrayList and passing each time a different name to the method:
indexes.forEach(i->{entryShifts.removeBranch(i);});
public void removeBranch(String branch){
pane.getTabs().forEach(i -> {
if (i.getText().equals(branch)) {
Platform.runLater(() -> {
pane.getTabs().remove(i);
});
}
});
}
Your problem is that pane.getTabs() returns a list of Tab, and you are trying to find Strings in it. You need a way to convert the strings (Tab names?) to the actual Tab nodes in the TabPane.
One such way is to have a Map<String, Tab> map which you initialize with the tab names and tabs, and then do something like:
List<Tab> tabs = branch.stream().map(map::get).collect(Collectors.toList());
pane.getTabs().removeAll(tabs);

Resources