Stop multiple ListCell Context Menus from Showing - java

I made a Custom ListView Cell so i can add a Context Menu but it keeps opening multiple context menus when you click more than once and the old ones just raise Exceptions when used.
Here is my SongCell class
public SongCell(ListView<Song> list, Playlist playlist) {
setAlignment(Pos.CENTER_LEFT);
ContextMenu listContextMenu = new ContextMenu();
MenuItem removeItem = new MenuItem("Remove");
MenuItem editID3Item = new MenuItem("Edit ID3");
MenuItem playNextItem = new MenuItem("Play Next");
removeItem.setOnAction((ActionEvent event) -> {
list.getItems().remove(getIndex());
});
editID3Item.setOnAction((ActionEvent event) -> {
Optional<Pair<String, String>> show = new FXID3Edit(getItem()).show();
});
playNextItem.setOnAction((ActionEvent event) -> {
Song song = getItem();
list.getItems().remove(song);
playlist.addSongRequest(new SongRequest(song, SongRequest.type.NEXT));
});
listContextMenu.getItems().add(removeItem);
listContextMenu.getItems().add(playNextItem);
listContextMenu.getItems().add(editID3Item);
setOnMousePressed(event -> {
if (event.getButton().equals(MouseButton.SECONDARY)) {
if (getItem() != null) {
if (getItem().equals(playlist.getSong())) {
playNextItem.setDisable(true);
removeItem.setDisable(true);
} else {
playNextItem.setDisable(false);
removeItem.setDisable(false);
}
listContextMenu.show(list, event.getScreenX(), event.getScreenY());
}
}
if (event.getButton().equals(MouseButton.PRIMARY) && event.getClickCount() == 2) {
playlist.setIndex(this.getIndex());
playlist.play();
}
event.consume();
});
}
#Override
protected void updateItem(Song item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item != null) {
this.setText(item.toString());
} else {
this.setText("");
}
}
I also greatly appreciate help on my Coding Style

Instead of registering a mouse listener to show the context menu, use the built-in setContextMenu(...) property. I.e.:
public SongCell(ListView<Song> list, Playlist playlist) {
// ...
setOnMousePressed(event -> {
if (event.getButton().equals(MouseButton.PRIMARY) && event.getClickCount() == 2) {
playlist.setIndex(this.getIndex());
playlist.play();
}
event.consume();
});
}
#Override
public void updateItem(Song item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item != null) {
this.setText(item.toString());
this.setContextMenu(listContextMenu);
} else {
this.setText("");
this.setContextMenu(null);
}
}

Related

How to switch back to previous tab on back pressed in an tabbed activity

I have a tabbed activity 1 to 5. If I move from 3 to 5 then on back pressed I want to switch back from 5 to 3 i.e. on back pressed I want to move back to the previous tab. Please help
Use the below code to achieve it.(Code is tested also.)
1.Initialise a List of integers and a boolean value;
private boolean isBackPressed = false;
private boolean isBackPressedOnce = false;
private List<Integer> tabsInBack = new ArrayList<>();
2.Now your onTabSelectedListener has to be like
tabs.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
if(!isBackPressed){
tabsInBack.add(tab.getPosition());}
else {
isBackPressed = false;}
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
3(i).Now your onBackPressed() function has to be,(One tab back then close)
#Override
public void onBackPressed() {
isBackPressed = true;
if (tabsInBack != null && tabsInBack.size() > 0) {
if (tabs.getSelectedTabPosition() == tabsInBack.get(tabsInBack.size() - 1)) {
tabsInBack.remove(tabsInBack.size() - 1);
}
if (tabsInBack != null && tabsInBack.size() > 0) {
tabs.getTabAt(tabsInBack.get(tabsInBack.size() - 1)).select();
tabsInBack.remove(tabsInBack.size() - 1);
} else {
super.onBackPressed();
}
} else {
super.onBackPressed();
}
}
3(ii).Now your onBackPressed() function has to be,(Back to all selected tabs and then close)
#Override
public void onBackPressed() {
isBackPressed = true;
if (tabsInBack != null && tabsInBack.size() > 0) {
if (tabs.getSelectedTabPosition() == tabsInBack.get(tabsInBack.size() - 1)) {
tabsInBack.remove(tabsInBack.size() - 1);
}
if (tabsInBack != null && tabsInBack.size() > 0) {
tabs.getTabAt(tabsInBack.get(tabsInBack.size() - 1)).select();
tabsInBack.remove(tabsInBack.size() - 1);
if(isBackPressedOnce )
{
super.onBackPressed();
}
else
{
isBackPressedOnce = true;
}
} else {
super.onBackPressed();
}
} else {
super.onBackPressed();
}
}
Let me know whether it helped or not.
This works for me :)
It works exactly like whatsapp
#Override
public void onBackPressed() {
//If first tab is open, then quit
if (viewPager.getCurrentItem() == 0) {
super.onBackPressed();
}else {
//If any other tab is open, then switch to first tab
viewPager.setCurrentItem(0);
}
}

How to set Root checkbox selected when every child is selected(GWT)

I am building GWT app where I have Tree and TreeItems with CheckBoxes. I have one root CheckBox called allCheckBox and his child elements rootCheckBox(this checkBoxes also have theirs children but that is not matter for this). I want that, when user opens dialog with checkBoxes, this checkBox is selected if every childCheckBox is selected. I have done that when tihs root checkBox is selected that child checkBoxes also are selected.
This is my piece of code:
enter cod GWT_DOMAIN_SERVICE.findAll(new AsyncCallback<List<GwtDomain>>() {
#Override
public void onFailure(Throwable caught) {
exitMessage = MSGS.dialogAddPermissionErrorDomains(caught.getLocalizedMessage());
exitStatus = false;
hide();
}
#Override
public void onSuccess(List<GwtDomain> result) {
for (final GwtDomain gwtDomain : result) {
GWT_DOMAIN_SERVICE.findActionsByDomainName(gwtDomain.name(), new AsyncCallback<List<GwtAction>>() {
#Override
public void onFailure(Throwable caught) {
exitMessage = MSGS.dialogAddPermissionErrorActions(caught.getLocalizedMessage());
exitStatus = false;
hide();
}
#Override
public void onSuccess(List<GwtAction> result) {
checkedItems = new GwtCheckedItems();
checkedItems.setName(gwtDomain);
rootCheckBox = new CheckBox();
rootCheckBox.setBoxLabel(gwtDomain.toString());
listCheckBoxes.add(rootCheckBox);
rootTreeItem = new TreeItem(rootCheckBox);
childCheckBoxMapList = new HashMap<GwtAction, CheckBox>();
checkedItems.setMap(childCheckBoxMapList);
for (GwtAccessPermission gwtAccessPermission : checkedPermissionsList) {
if (gwtAccessPermission.getPermissionDomain().toString().equals(checkedItems.getName().toString())) {
if (gwtAccessPermission.getPermissionAction().toString().equals(GwtAction.ALL.toString())) {
rootCheckBox.setValue(true);
}
}
}
if (listOfNewClass.size() == checkedPermissionsList.size()) {
allCheckBox.setValue(true);
}
for (final GwtAction gwtAction : result) {
final CheckBox childTreeItemCheckox = new CheckBox();
treeItem = new TreeItem(childTreeItemCheckox);
childTreeItemCheckox.setBoxLabel(gwtAction.toString());
rootTreeItem.addItem(treeItem);
childListOfNewClass.add(gwtAction);
allTreeItem.addItem(rootTreeItem);
childCheckBoxMapList.put(gwtAction, childTreeItemCheckox);
for (GwtAccessPermission gwtAccessPermission : checkedPermissionsList) {
if (gwtAccessPermission.getPermissionDomain().toString().equals(gwtDomain.toString())) {
if (gwtAccessPermission.getPermissionAction().toString().equals(gwtAction.toString())) {
childTreeItemCheckox.setValue(true);
}
}
}
}
listOfNewClass.put(checkedItems, rootCheckBox);
}
});
}
allCheckBox.addListener(Events.OnClick, new Listener<BaseEvent>() {
#Override
public void handleEvent(BaseEvent be) {
if (allCheckBox.getValue()) {
for (CheckBox checkBox : listCheckBoxes) {
if (!checkBox.getValue()) {
checkBox.setValue(true);
}
}
} else {
for (CheckBox checkBox : listCheckBoxes) {
checkBox.setValue(false);
}
}
}
});
How to set that when all rootCheckBoxes are checked then allCheckBox become also checked?
EDIT: This checkedPermissionsList is List of rootCheckBox which are checked.
Well the listener would have to iterate the child boxes and see if they are all selected, and set the parent box accordingly
Listener<BaseEvent> listener = new Listener<>() {
public void handleEvent(BaseEvent be) {
boolean allSet = listCheckBoxes.stream().allMatch(CheckBox::getValue);
allCheckBox.setValue(allSet); // this will also unselect if appropriate
}
}
It's the same listener for all boxes, so add it to each
listCheckBoxes.forEach(box -> box.addListener(Event.OnClick, listener));
In the pre-Java 8 version:
Listener<BaseEvent> listener = new Listener<>() {
public void handleEvent(BaseEvent be) {
boolean allSet = true;
for (CheckBox child : listCheckBoxes) {
if (!child.getValue()) {
allSet = false; // found a non-checked box
break;
}
}
allCheckBox.setValue(allSet); // this will also unselect if appropriate
}
// and set the listener to the children with
for (CheckBox box : listCheckBoxes) {
box.addListener(Event.Clicked, listener);
}

JavaFX Binding String to TreeCell Factory Properly

I'm getting an issue, where when I expand and unexpand a tree cell. The text I binded to doesn't ever go away.. What could be the cause of this?
This is what I get
Here is my code
treeView.setCellFactory(t -> {
return new TreeCell<Entry>() {
#Override
protected void updateItem(Entry entry, boolean empty) {
super.updateItem(entry, empty);
if (entry != null) {
if (empty) {
setText(null);
setGraphic(null);
} else {
if (entry.getImage() != null) {
Image image = FXUtils.toFXImage(entry.getImage());
setGraphic(new ImageView(image));
} else {
setGraphic(null);
}
textProperty().bind(entry.nameProperty());
}
}
}
};
});
If a cell is empty, the item is also null. However all your logic is only executed, if (entry != null), i.e. never, if the cell is empty or the item is null. Furthermore this should fail, even if you fix this error, since you never unbind the textProperty, but it could be set for empty cells. Change your updateItem method to something like this:
#Override
protected void updateItem(Entry entry, boolean empty) {
super.updateItem(entry, empty);
textProperty().unbind();
if (empty || entry == null) {
setText(null);
setGraphic(null);
} else {
if (entry.getImage() != null) {
Image image = FXUtils.toFXImage(entry.getImage());
setGraphic(new ImageView(image));
} else {
setGraphic(null);
}
textProperty().bind(entry.nameProperty());
}
}

Two different row factories for TableView

In my program have an TableView and I want to register two different row factories to format my rows based on the content.
Here are my factories:
private void highlightReportRowsIfImportant() {
tv_berichte.setRowFactory(new Callback<TableView<DatabaseReport>, TableRow<DatabaseReport>>() {
#Override
public TableRow<DatabaseReport> call(TableView<DatabaseReport> tableView) {
final TableRow<DatabaseReport> row = new TableRow<DatabaseReport>() {
#Override
protected void updateItem(DatabaseReport report, boolean empty) {
super.updateItem(report, empty);
if (report != null) {
if (report.getReport_art().contains("!!!")) {
setStyle("-fx-background-color: #FF0000;");
} else {
setStyle("");
}
} else {
setStyle("");
}
}
};
reports.addListener(new ListChangeListener<DatabaseReport>() {
#Override
public void onChanged(ListChangeListener.Change<? extends DatabaseReport> change) {
if (row.getItem() != null) {
if (row.getItem().getReport_art().contains("!!!")) {
row.setStyle("-fx-background-color: #FF0000;");
} else {
row.setStyle("");
}
} else {
row.setStyle("");
}
}
});
return row;
}
});
}
And here is the other one:
private void registerDragAndDropReportListener(TableView view) {
view.setRowFactory(tv -> {
TableRow<DatabaseReport> row = new TableRow<>();
row.setOnDragDetected(event -> {
if (!row.isEmpty()) {
Dragboard db = row.startDragAndDrop(TransferMode.COPY);
db.setDragView(row.snapshot(null, null));
ClipboardContent cc = new ClipboardContent();
cc.putString(row.getItem().getReport_content());
db.setContent(cc);
event.consume();
}
});
row.setOnDragOver(event -> {
Dragboard db = event.getDragboard();
if (db.hasContent(DataFormat.PLAIN_TEXT)) {
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
event.consume();
}
});
row.setOnDragDropped(event -> {
Dragboard db = event.getDragboard();
if (db.hasContent(DataFormat.PLAIN_TEXT)) {
reportFlowController.addNewReport(username, getSqlTimeStamp().toString(), this.txt_adressnummer.getText(), reports, db.getString());
event.setDropCompleted(true);
event.consume();
}
});
return row;
});
}
Now if I register the factories like this:
registerDragAndDropReportListener(tv_berichte);
highlightReportRowsIfImportant();
only the last one is used.
Could you please explain me, how I can register both of them?
You can't register multiple row factories, and it makes no sense to try to do so. The TableView uses its row factory to create a TableRow object whenever it needs one (typically when it is first laid out on the screen, and perhaps later if it grows vertically and needs to display more rows); remember the row factory is just a function that provides TableRow objects. If two or more row factories were registered, the TableView would have no mechanism for choosing which one to use.
What you can do is define a row factory which uses an existing row factory to generate its row. This effectively replaces the existing row factory with one that adds additional functionality to it:
private void registerDragAndDropReportListener(TableView<DatabaseReport> view) {
Callback<TableView<DatabaseReport>, TableRow<DatabaseReport>> existingRowFactory
= view.getRowFactory();
view.setRowFactory(tv -> {
TableRow<DatabaseReport> row ;
if (existingRowFactory == null) {
row = new TableRow<>();
} else {
row = existingRowFactory.call(view);
}
row.setOnDragDetected(event -> {
if (!row.isEmpty()) {
Dragboard db = row.startDragAndDrop(TransferMode.COPY);
db.setDragView(row.snapshot(null, null));
ClipboardContent cc = new ClipboardContent();
cc.putString(row.getItem().getReport_content());
db.setContent(cc);
event.consume();
}
});
row.setOnDragOver(event -> {
Dragboard db = event.getDragboard();
if (db.hasContent(DataFormat.PLAIN_TEXT)) {
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
event.consume();
}
});
row.setOnDragDropped(event -> {
Dragboard db = event.getDragboard();
if (db.hasContent(DataFormat.PLAIN_TEXT)) {
reportFlowController.addNewReport(username, getSqlTimeStamp().toString(), this.txt_adressnummer.getText(), reports, db.getString());
event.setDropCompleted(true);
event.consume();
}
});
return row;
});
}
With this, the registerDragAndDropReportListener method replaces the existing row factory with one that adds additional functionality to the existing row factory, which I think is what you want. So in this case you would first call your original highlightRowsIfImportant method, and then call the new registerDragAndDropReportListener method shown.
You could make similar changes to the other method, so that you could call the two methods in any order you want. Note that in this case, it would be important to ensure you only call each method once.
Here is my solution, using both listeners in one row factory with the same row object. After you postet your awnser James_D, it was clear for me. I cant register 2 of them, which should be handelt ... first think, then write code ...
private void highlightReportRowsIfImportant(TextField txt_adressnummer) {
tv_berichte.setRowFactory(new Callback<TableView<DatabaseReport>, TableRow<DatabaseReport>>() {
#Override
public TableRow<DatabaseReport> call(TableView<DatabaseReport> tableView) {
final TableRow<DatabaseReport> row = new TableRow<DatabaseReport>() {
#Override
protected void updateItem(DatabaseReport report, boolean empty) {
super.updateItem(report, empty);
if (report != null) {
if (report.getReport_art().contains("!!!")) {
setStyle("-fx-background-color: #FF0000;");
} else {
setStyle("");
}
} else {
setStyle("");
}
}
};
reports.addListener(new ListChangeListener<DatabaseReport>() {
#Override
public void onChanged(ListChangeListener.Change<? extends DatabaseReport> change) {
if (row.getItem() != null) {
if (row.getItem().getReport_art().contains("!!!")) {
row.setStyle("-fx-background-color: #FF0000;");
} else {
row.setStyle("");
}
} else {
row.setStyle("");
}
}
});
row.setOnDragDetected(event -> {
if (!row.isEmpty()) {
Dragboard db = row.startDragAndDrop(TransferMode.COPY);
db.setDragView(row.snapshot(null, null));
ClipboardContent cc = new ClipboardContent();
cc.putString(row.getItem().getReport_content());
db.setContent(cc);
event.consume();
}
});
row.setOnDragOver(event -> {
Dragboard db = event.getDragboard();
if (db.hasContent(DataFormat.PLAIN_TEXT)) {
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
event.consume();
}
});
row.setOnDragDropped(event -> {
Dragboard db = event.getDragboard();
if (db.hasContent(DataFormat.PLAIN_TEXT)) {
reportFlowController.addNewReport(username, getSqlTimeStamp().toString(), txt_adressnummer.getText(), reports, db.getString());
event.setDropCompleted(true);
event.consume();
}
});
return row;
}
});
}

Disable Checkbox click in multiselectListPreference in android

I am using mutliselectlistpreference that extends DialogPreference in my application. And i have not use any adapter for building the UI. Please find the below image.
The issue here is I am able to persist the CheckBox checked for Monday and Tuesday but i am not able to make the items read only wherein user will not able to unchecked the items. I want to make both items grey out. could you please help me out on this ?
#Override
protected void onPrepareDialogBuilder(Builder builder) {
super.onPrepareDialogBuilder(builder);
if (entries == null || entryValues == null) {
throw new IllegalStateException(
"MultiSelectListPreference requires an entries array and an entryValues array.");
}
checked = new boolean[entryValues.length];
List<CharSequence> entryValuesList = Arrays.asList(entryValues);
List<CharSequence> entriesList = Arrays.asList(entries);
for (int i = 0; i < entryValues.length; ++i) {
if("Monday".equals(entriesList.get(i).toString())){
checked[i]=true;
}
}
if (values != null) {
for (String value : values) {
int index = entryValuesList.indexOf(value);
if (index != -1) {
checked[index] = true;
}
}
}
builder.setMultiChoiceItems(entries, checked,
new OnMultiChoiceClickListener() {
public void onClick(DialogInterface dialog, int which,
boolean isChecked) {
checked[which] = isChecked;
}
});
}
#Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult && entryValues != null) {
for (int i = 0; i < entryValues.length; ++i) {
if (checked[i]) {
newValues.add(entryValues[i].toString());
}
}
if (callChangeListener(newValues)) {
setValues(newValues);
}
}
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
CharSequence[] array = a.getTextArray(index);
Set<String> set = new HashSet<String>();
for (CharSequence item : array) {
set.add(item.toString());
}
return set;
}
#Override
protected void onSetInitialValue(boolean restorePersistedValue,
Object defaultValue) {
#SuppressWarnings("unchecked")
Set<String> defaultValues = (Set<String>) defaultValue;
setValues((restorePersistedValue ? getPersistedStringSet(values)
: defaultValues));
}
private Set<String> getPersistedStringSet(Set<String> defaultReturnValue) {
String key = getKey();
//String value = getSharedPreferences().getString("4", "Generic");
return getSharedPreferences().getStringSet(key, defaultReturnValue);
}
private boolean persistStringSet(Set<String> values) {
if (shouldPersist()) {
// Shouldn't store null
if (values == getPersistedStringSet(null)) {
return true;
}
}
SharedPreferences.Editor editor = getEditor();
editor.putStringSet(getKey(), values);
editor.apply();
return true;
}
#Override
protected Parcelable onSaveInstanceState() {
if (isPersistent()) {
return super.onSaveInstanceState();
} else {
throw new IllegalStateException("Must always be persistent");
}
}
You can use them;
checkBox.setEnabled(true); // enable checkbox
checkBox.setEnabled(false); // disable checkbox
Also you can disable them after check the checkBox with this code:
checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener(){
#Override
public void onCheckedChanged(CompoundButton arg0, boolean isChecked) {
if (isChecked){
checkBox.setEnabled(false); // disable checkbox
}
}
});

Categories