Can I get different mouse-click events from a TreeViewItem depending on the user clicks on the icon or on the text label? The icon is not the collapse image of the tree.
According to Disable TreeItem's default expand/collapse on double click JavaFX 2.2 I have changed the constructor of the TreeMouseEventDispatcher:
class TreeMouseEventDispatcher implements EventDispatcher {
private final EventDispatcher originalDispatcher;
private final TreeCell<?> cell;
public TreeMouseEventDispatcher(EventDispatcher originalDispatcher, TreeCell<?> cell) {
this.originalDispatcher = originalDispatcher;
this.cell = cell;
}
private boolean isMouseEventOnGraphic(Node icon, Point2D coord) {
if (icon == null) {
return false;
}
return icon.localToScene(icon.getBoundsInLocal()).contains(coord);
}
#Override
public Event dispatchEvent(Event event, EventDispatchChain tail) {
if (event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
if (mouseEvent.getButton() == MouseButton.PRIMARY
&& mouseEvent.getClickCount() >= 2) {
if (!mouseEvent.isConsumed()) {
if (isMouseEventOnGraphic(cell.getGraphic(), new Point2D(mouseEvent.getSceneX(), mouseEvent.getSceneY()))) {
// action for double click on graphic
} else {
// action for double click on all other elements of
// the TreeCell (like text, text gap, disclosure node)
}
}
event.consume();
}
}
return originalDispatcher.dispatchEvent(event, tail);
}
}
and then use this TreeMouseEventDispatcher for the TreeCell:
treeView.setCellFactory(new Callback<TreeView<T>, TreeCell<T>>() {
#Override
public TreeCell<T> call(TreeView<T> param) {
return new TreeCell<T>() {
#Override
protected void updateItem(T item, boolean empty) {
if (item != null && !empty) {
EventDispatcher originalDispatcher = getEventDispatcher();
setEventDispatcher(new TreeMouseEventDispatcher(originalDispatcher, this));
}
}
};
}
}
Related
I have an ImageView with an Image inside it and when the image is clicked, a MouseClicked event should be triggered. However, the application is not even printing the test line.
Image cancel = new Image("/cancel.svg.png", 15, 15, false, false);
ImageView view = new ImageView(cancel);
view.setPickOnBounds(true);
view.setOnMouseClicked(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event) {
System.out.println("hi");
/** execute code **/
}
});
Complete version:
I have a ListView called "found_tags_list" and every cell inside that ListView should be a new class called: "ButtonListCell". I did this to add extra content to the cells based on some statements.
found_tags_list.setCellFactory(new Callback<ListView<String>, ListCell>() {
#Override
public ListCell call(ListView<String> param) {
return new ButtonListCell();
}
});
Entire Cell class:
class ButtonListCell extends ListCell<String> {
#Override
public void updateItem(String obj, boolean empty) {
super.updateItem(obj, empty);
if (obj == null || obj.isEmpty() || empty) {
setText(null);
setGraphic(null);
} else {
setText(obj);
if (valid_tags.indexOf(obj) >= 0) {
setTextFill(Color.GREEN);
setGraphic(null);
} else {
Image cancel = new Image("/cancel.png", 15, 15, false, false);
ImageView view = new ImageView(cancel);
view.setPickOnBounds(true);
view.setOnMouseClicked(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event) {
System.out.println("hi");
String item = getItem();
found_tags_list.getItems().remove(item);
for (Tag tag : rfidset) {
if (tag.getId().equals(item)) {
rfidset.remove(tag);
return;
}
}
}
});
setGraphic(view);
setTextFill(Color.RED);
setContentDisplay(ContentDisplay.RIGHT);
}
}
}
I have TableView with column inside it that must only accept numbers.
and I added onMouseClickListener to enter edit mode on the mouse click instead of double click on the cell
I want a way to not allowing the user to enter any character except numbers. My code is:
Callback<TableColumn<DailyDetails, String>, TableCell<DailyDetails, String>> defaultCellFactory
= TextFieldTableCell.<DailyDetails>forTableColumn();
dailyCredit.setCellFactory(column -> {
TableCell<DailyDetails, String> cell = defaultCellFactory.call(column);
cell.setOnMouseClicked(e -> {
if (!cell.isEditing() && !cell.isEmpty()) {
cell.getTableView().edit(cell.getIndex(), column);
}
});
return cell;
});
I implemented Table cell from the scratch:
class NumberCell extends TableCell<DailyDetails, String> {
private TextField textField;
public NumberCell() {
}
#Override
public void startEdit() {
super.startEdit();
if (textField == null) {
createTextField();
}
setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
textField.selectAll();
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText(String.valueOf(getItem()));
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (textField != null) {
textField.setText(getString());
}
setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
setText(getString());
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
private void createTextField() {
textField = new TextField(getString());
//textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.lengthProperty().addListener(new ChangeListener<Number>(){
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
if (newValue.intValue() > oldValue.intValue()) {
char ch = textField.getText().charAt(oldValue.intValue());
// Check if the new character is the number or other's
if (!(ch >= '0' && ch <= '9' )) {
// if it's not number then just setText to previous one
textField.setText(textField.getText().substring(0,textField.getText().length()-1));
}
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
Callback<TableColumn<DailyDetails, String>,
TableCell<DailyDetails, String>> cellFactory
= (TableColumn<DailyDetails, String> p) -> new NumberCell();
dailyDebit.setCellFactory(cellFactory);
the problem is i lost the on mouse listener cell.setOnMouseClicked!!!
how do i get the cell again to assign the listener ???
Just for driving the new api into everybody's brain: a full example with a slightly different TextFormatter (than in the other answer) that is Locale-aware and (dirtily!) hooked into core TextFieldTableCell, can be used in any custom editing TableCell as well:
/**
* Example of how-to use a TextFormatter in a editing TableCell.
*/
public class CellFormatting extends Application {
private Parent getContent() {
ObservableList<IntData> data = FXCollections.observableArrayList(
new IntData(1), new IntData(2), new IntData(3)
);
TableView<IntData> table = new TableView<>(data);
table.setEditable(true);
TableColumn<IntData, Integer> column = new TableColumn<>("Data");
column.setCellValueFactory(new PropertyValueFactory("data"));
// core default: will throw exception on illegal values
// column.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));
NumberFormat format = NumberFormat.getIntegerInstance();
UnaryOperator<TextFormatter.Change> filter = c -> {
if (c.isContentChange()) {
ParsePosition parsePosition = new ParsePosition(0);
// NumberFormat evaluates the beginning of the text
format.parse(c.getControlNewText(), parsePosition);
if (parsePosition.getIndex() == 0 ||
parsePosition.getIndex() < c.getControlNewText().length()) {
// reject parsing the complete text failed
return null;
}
}
return c;
};
column.setCellFactory(c -> new ValidatingTextFieldTableCell<>(
// note: each cell needs its own formatter
// see comment by #SurprisedCoconut
new TextFormatter<Integer>(
// note: should use local-aware converter instead of core!
new IntegerStringConverter(), 0,
filter)));
table.getColumns().add(column);
VBox box = new VBox(table);
return box;
}
/**
* TextFieldTableCell that validates input with a TextFormatter.
* <p>
* Extends TextFieldTableCell, accesses super's private field reflectively.
*
*/
public static class ValidatingTextFieldTableCell<S, T> extends TextFieldTableCell<S, T> {
private TextFormatter<T> formatter;
private TextField textAlias;
public ValidatingTextFieldTableCell() {
this((StringConverter<T>)null);
}
public ValidatingTextFieldTableCell(StringConverter<T> converter) {
super(converter);
}
public ValidatingTextFieldTableCell(TextFormatter<T> formatter) {
super(formatter.getValueConverter());
this.formatter = formatter;
}
/**
* Overridden to install the formatter. <p>
*
* Beware: implementation detail! super creates and configures
* the textField lazy on first access, so have to install after
* calling super.
*/
#Override
public void startEdit() {
super.startEdit();
installFormatter();
}
private void installFormatter() {
if (formatter != null && isEditing() && textAlias == null) {
textAlias = invokeTextField();
textAlias.setTextFormatter(formatter);
}
}
private TextField invokeTextField() {
Class<?> clazz = TextFieldTableCell.class;
try {
Field field = clazz.getDeclaredField("textField");
field.setAccessible(true);
return (TextField) field.get(this);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
public static class IntData {
IntegerProperty data = new SimpleIntegerProperty(this, "data");
public IntData(int value) {
setData(value);
}
public void setData(int value) {
data.set(value);
}
public int getData() {
return data.get();
}
public IntegerProperty dataProperty() {
return data;
}
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent()));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
BTW, the formatter is re-used from another question where the task at hand was to restrict input into a Spinner.
Use a TextFormatter on the TextField like this:
TextFormatter<String> formatter = new TextFormatter<String>( change -> {
change.setText(change.getText().replaceAll("[^0-9.,]", ""));
return change;
});
textField.setTextFormatter(formatter);
Works with Java8u40 upwards. Use e. g. the TableView example from the Oracle site as base.
I have this basic code which adds drag and drop functionality to tree views.
The code works just fine, but when one treeitem is dragged, dropped and added over to another treeitem, the graphic on the treeitem in the original treecell dissapears, while the graphic follows the dragged element and is visible in the new treeitem.
See the image for example, the treeitem "Name" has been dragged over to "Column Name" and lost it's graphic. Alltough the new TreeItem under "Column Name" still has it graphics.
The code for adding drag and drop is this:
private void addDragAndDrop(TreeCell<String> treeCell, MainFXMLController mainFXMLController) {
treeCell.setOnDragDetected(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("setOnDragDetected");
Dragboard db = treeCell.startDragAndDrop(TransferMode.ANY);
ClipboardContent content = new ClipboardContent();
content.putString(event.toString());
db.setContent(content);
DRAGGEDSOURCE = treeCell;
DRAGGEDINDEX = (treeCell.getTreeView().getSelectionModel().getSelectedIndex());
}
});
treeCell.setOnDragOver(new EventHandler<DragEvent>() {
//brukeren har dragget det over et element
public void handle(DragEvent event) {
DRAGGEDTARGET = treeCell.getTreeItem();
if (event.getGestureSource() != treeCell
&& event.getDragboard().hasString()) {
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
}
}
});
treeCell.setOnDragDropped(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
DRAGGEDTARGET.getChildren().add(DRAGGEDSOURCE.getTreeItem());
}
);
}
public void makeTreeViewDragAble(TreeView treeView, MainFXMLController mainFXMLController) {
treeView.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>() {
#Override
public TreeCell<String> call(TreeView<String> stringTreeView) {
TreeCell<String> treeCell = new TreeCell<String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item != null) {
setText(item);
setGraphic(getTreeItem().getGraphic());
} else {
setText(null);
setGraphic(null);
}
}
};
addDragAndDrop(treeCell, mainFXMLController);
treeView.setEditable(true);
return treeCell;
}
});
}
As #James_D and I have concluded on, this is because I am trying to have two instances of the imageView. The solution is to set a new image view on the old item.
I am using a editable ComboBox cell in a table view. Here is my ComboBox cell class
public class ComboBoxCell extends TableCell<ClassesProperty, String> {
private ComboBox<String> comboBox;
public ComboBoxCell() {
}
#Override
public void startEdit() {
super.startEdit();
if (comboBox == null) {
createComboBox();
}
setGraphic(comboBox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
Platform.runLater(new Runnable() {
#Override
public void run() {
comboBox.requestFocus();
comboBox.getEditor().requestFocus();
comboBox.getEditor().selectAll();
}
});
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText(String.valueOf(getItem()));
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (isEditing()) {
if (comboBox != null) {
comboBox.setValue(getString());
}
setGraphic(comboBox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
setText(getString());
setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
private void createComboBox() {
// ClassesController.getLevelChoice() is the observable list of String
comboBox = new ComboBox<>(ClassesController.getLevelChoice());
comboBox.setEditable(true);
comboBox.setMinWidth(this.getWidth() - this.getGraphicTextGap()*2);
comboBox.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitEdit(comboBox.getSelectionModel().getSelectedItem());
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
}
}
});
}
private String getString() {
return getItem() == null ? "" : getItem().toString();
}
}
The problem I need to press three clicks on the table cell to get the text field of the combo box to edit the contents. Is there a way to make it in two clicks? I even used Platforn runlater but when I try to edit a cell at first time it takes three mouse clicks but at the second time only two clicks.
In your overridden cell.startEdit() method, Add a ListView with a textfield, then add this listview to setGraphic. This will show the listview directly once the row is selected and the cell is clicked, the listview will be inside the tablecell, I am yet to find a way to make it as a popup
I have a fragment that has a SearchView. When I click in the search box the keyboard appears. I enter a search term and press "Enter" the keyboard disappears and I get my results page. When I choose an item in my list of results I am taken back to the search fragment and the keyboard reappears. How to I not have the keyboard reappear? Here is part of my code:
public class LocationFragment extends ListFragment implements OnItemLongClickListener, OnClickListener{
// ===========================================================
// Constants
// ===========================================================
private static final int BTN_ID = 0x5f000001;
private static final String DEBUG_TAG = "Tablet/LocationFragment";
// ===========================================================
// Fields
// ===========================================================
private int pos;
private int mPositionToDelete = 0;
private FragmentListener listener;
private Activity mActivity;
// ===========================================================
// Lifecycle
// ===========================================================
#Override
public void onAttach(Activity activity) {
mActivity = activity;
super.onAttach(activity);
try {
listener = (FragmentListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement Fragment");
}
}
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState){
View v = inflater.inflate(R.layout.locations_tile, null);
SearchManager sm = (SearchManager)getActivity().getSystemService(Context.SEARCH_SERVICE);
final SearchView sv = (SearchView)v.findViewById(R.id.search_view);
sv.setSearchableInfo(sm.getSearchableInfo(getActivity().getComponentName()));
sv.setSubmitButtonEnabled(true);
consumeKeyUp(sv);
return v;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final ListView lv = getListView();
lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
lv.setItemChecked(pos, true);
lv.setSelector(R.xml.location_select_gradient);
lv.setCacheColorHint(0x00000000);
lv.setSelector(R.drawable.list_item_selector);
lv.setOnItemLongClickListener(this);
lv.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
if (event.getAction() == KeyEvent.ACTION_DOWN) {
switch(keyCode) {
case KeyEvent.KEYCODE_DPAD_DOWN:
// Log.i(DEBUG_TAG, "onkeyDown");
return !(lv.getSelectedItemPosition() < lv.getCount() - 1);
case KeyEvent.KEYCODE_DPAD_UP:
// Log.i(DEBUG_TAG, "onkey up");
if(lv.getSelectedItemPosition() == 0) {
// Log.i(DEBUG_TAG, " requesting for search focused");
return LocationFragment.this.getView().findViewById(R.id.search_view).requestFocus();
} else return false;
case KeyEvent.KEYCODE_DPAD_RIGHT:
Log.i(DEBUG_TAG, "KEYCODE_DPAD_RIGHT");
return true;
}
return false;
}
return false;
}
});
ImageView closeBtn = (ImageView) getView().findViewById(R.id.locationsCloseButton);
closeBtn.setOnClickListener(this);
closeBtn.setFocusable(true);
closeBtn.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
v.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
Log.i(DEBUG_TAG, "keypad Center");
return false;
}
if (event.getAction() == KeyEvent.ACTION_DOWN) {
return !(KeyEvent.KEYCODE_DPAD_DOWN == keyCode);
}
return false;
}
});
} else {
v.setOnKeyListener(null);
}
}
});
}
You could trying clearing the search view focus in your list click callback, e.g. sv.clearFocus()
I grab the search view to do this in OnCreateOptionsMenu using getActionView() on the MenuItem.
Incidentally I also call the following on my search view in onCreateOptionsMenu
final InputMethodManager imm = (InputMethodManager) getActivity().getSystemServiceContext.INPUT_METHOD_SERVICE);
imm.showSoftInput(searchView, InputMethodManager.SHOW_IMPLICIT);
To be honest though I find the whole are of soft input keyboards a bit of a black art
private SearchView mSearchView;
...
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.search_menu, menu);
// Associate search able configuration with the SearchView
android.app.SearchManager searchManager = (android.app.SearchManager) getSystemService(Context.SEARCH_SERVICE);
mSearchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
mSearchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
mSearchView.setIconifiedByDefault(false);
return super.onCreateOptionsMenu(menu);
}
private result() {
mSearchView.clearFocus();
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(searchView.getWindowToken(), 0);
}
don't use startSearch or onSearchRequested