Edit next row on tab - user-interface

When i put all code in a SSCCE, it works as expected i.e first and third cells are editable. When tab on last column, takes to next row.
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.application.Platform;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* #author Yunus
*/
public class CollectionForm extends Application{
private TableView table = new TableView();
private ObservableList<Collection> collectionList = FXCollections.<Collection>observableArrayList();
ListProperty<Collection> collectionListProperty = new SimpleListProperty<>();
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
// single cell selection mode
table.getSelectionModel().setCellSelectionEnabled(true);
//Create a custom cell factory so that cells can support editing.
Callback<TableColumn, TableCell> editableFactory = new Callback<TableColumn, TableCell>() {
#Override
public TableCell call(TableColumn p) {
return new EditableTableCell();
}
};
//A custom cell factory that creates cells that only accept numerical input.
Callback<TableColumn, TableCell> numericFactory = new Callback<TableColumn, TableCell>() {
#Override
public TableCell call(TableColumn p) {
return new NumericEditableTableCell();
}
};
Button b = createSaveCollectionBtn();
//Create columns
TableColumn colMNO = createMNOColumn(editableFactory);
TableColumn colName = createNameColumn(editableFactory);
TableColumn colQty = createQuantityColumn(numericFactory);
table.getColumns().addAll(colMNO, colName, colQty);
//Make the table editable
table.setEditable(true);
collectionListProperty.set(collectionList);
table.itemsProperty().bindBidirectional(collectionListProperty);
collectionList.add(new Collection());
collectionList.add(new Collection());
Scene scene = new Scene(new Group());
stage.setTitle("Table View Sample");
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.getChildren().addAll(b, table);
vbox.setPadding(new Insets(10, 0, 0, 10));
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
private void handleCollection(ActionEvent event){
for (Collection collection : collectionList) {
System.out.println("MNO: "+collection.getMno()+" Quantity: "+collection.getQuantity());
}
}
private Button createSaveCollectionBtn(){
Button btn = new Button("Save Collection");
btn.setId("btnSaveCollection");
btn.setOnAction(this::handleCollection);
return btn;
}
private TableColumn createQuantityColumn(Callback<TableColumn, TableCell> editableFactory) {
TableColumn colQty = new TableColumn("Quantity");
colQty.setMinWidth(25);
colQty.setId("colQty");
colQty.setCellValueFactory(new PropertyValueFactory("quantity"));
colQty.setCellFactory(editableFactory);
colQty.setOnEditCommit(new EventHandler<CellEditEvent<Collection, Long>>() {
#Override
public void handle(CellEditEvent<Collection, Long> t) {
((Collection) t.getTableView().getItems().get(t.getTablePosition().getRow())).setQuantity(t.getNewValue());
}
});
return colQty;
}
private TableColumn createMNOColumn(Callback<TableColumn, TableCell> editableFactory) {
TableColumn colMno = new TableColumn("M/NO");
colMno.setMinWidth(25);
colMno.setId("colMNO");
colMno.setCellValueFactory(new PropertyValueFactory("mno"));
colMno.setCellFactory(editableFactory);
colMno.setOnEditCommit(new EventHandler<CellEditEvent<Collection, String>>() {
#Override
public void handle(CellEditEvent<Collection, String> t) {
((Collection) t.getTableView().getItems().get(t.getTablePosition().getRow())).setMno(t.getNewValue());
}
});
return colMno;
}
private TableColumn createNameColumn(Callback<TableColumn, TableCell> editableFactory) {
TableColumn colName = new TableColumn("Name");
colName.setEditable(false);
colName.setMinWidth(100);
colName.setId("colName");
colName.setCellValueFactory(new PropertyValueFactory<Collection, String>("name"));
colName.setCellFactory(editableFactory);
//Modifying the firstName property
colName.setOnEditCommit(new EventHandler<CellEditEvent<Collection, String>>() {
#Override
public void handle(CellEditEvent<Collection, String> t) {
((Collection) t.getTableView().getItems().get(t.getTablePosition().getRow())).setName(t.getNewValue());
}
});
return colName;
}
/**
*
* #author Graham Smith
*/
public class EditableTableCell<S extends Object, T extends String> extends AbstractEditableTableCell<S, T> {
public EditableTableCell() {
}
#Override
protected String getString() {
return getItem() == null ? "" : getItem().toString();
}
#Override
protected void commitHelper( boolean losingFocus ) {
commitEdit(((T) textField.getText()));
}
}
/**
*
* #author Graham Smith
*/
public class NumericEditableTableCell<S extends Object, T extends Number> extends AbstractEditableTableCell<S, T> {
private final NumberFormat format;
private boolean emptyZero;
private boolean completeParse;
/**
* Creates a new {#code NumericEditableTableCell} which treats empty strings as zero,
* will parse integers only and will fail if is can't parse the whole string.
*/
public NumericEditableTableCell() {
this( NumberFormat.getInstance(), true, true, true );
}
/**
* The integerOnly and completeParse settings have a complex relationship and care needs
* to be take to get the correct result.
* <ul>
* <li>If you want to accept only integers and you want to parse the whole string then
* set both integerOnly and completeParse to true. Strings such as 1.5 will be rejected
* as invalid. A string such as 1000 will be accepted as the number 1000.</li>
* <li>If you only want integers but don't care about parsing the whole string set
* integerOnly to true and completeParse to false. This will parse a string such as
* 1.5 and provide the number 1. The downside of this combination is that it will accept
* the string 1x and return the number 1 also.</li>
* <li>If you want to accept decimals and want to parse the whole string set integerOnly
* to false and completeParse to true. This will accept a string like 1.5 and return
* the number 1.5. A string such as 1.5x will be rejected.</li>
* <li>If you want to accept decimals and don't care about parsing the whole string set
* both integerOnly and completeParse to false. This will accept a string like 1.5x and
* return the number 1.5. A string like x1.5 will be rejected because ti doesn't start
* with a number. The downside of this combination is that a string like 1.5x3 will
* provide the number 1.5.</li>
* </ul>
*
* #param format the {#code NumberFormat} to use to format this cell.
* #param emptyZero if true an empty cell will be treated as zero.
* #param integerOnly if true only the integer part of the string is parsed.
* #param completeParse if true an exception will be thrown if the whole string given can't be parsed.
*/
public NumericEditableTableCell( NumberFormat format, boolean emptyZero, boolean integerOnly, boolean completeParse ) {
this.format = format;
this.emptyZero = emptyZero;
this.completeParse = completeParse;
format.setParseIntegerOnly(integerOnly);
}
#Override
protected String getString() {
return getItem() == null ? "" : format.format(getItem());
}
/**
* Parses the value of the text field and if matches the set format
* commits the edit otherwise it returns the cell to it's previous value.
*/
#Override
protected void commitHelper( boolean losingFocus ) {
if( textField == null ) {
return;
}
try {
String input = textField.getText();
if (input == null || input.length() == 0) {
if(emptyZero) {
setText( format.format(0) );
commitEdit( (T)new Integer( 0 ));
}
return;
}
int startIndex = 0;
ParsePosition position = new ParsePosition(startIndex);
Number parsedNumber = format.parse(input, position);
if (completeParse && position.getIndex() != input.length()) {
throw new ParseException("Failed to parse complete string: " + input, position.getIndex());
}
if (position.getIndex() == startIndex ) {
throw new ParseException("Failed to parse a number from the string: " + input, position.getIndex());
}
commitEdit( (T)parsedNumber );
} catch (ParseException ex) {
//Most of the time we don't mind if there is a parse exception as it
//indicates duff user data but in the case where we are losing focus
//it means the user has clicked away with bad data in the cell. In that
//situation we want to just cancel the editing and show them the old
//value.
if( losingFocus ) {
cancelEdit();
}
}
}
}
/**
* Provides the basis for an editable table cell using a text field. Sub-classes can provide formatters for display and a
* commitHelper to control when editing is committed.
*
* #author Graham Smith
*/
public abstract class AbstractEditableTableCell<S, T> extends TableCell<S, T> {
protected TextField textField;
public AbstractEditableTableCell() {
}
/**
* Any action attempting to commit an edit should call this method rather than commit the edit directly itself. This
* method will perform any validation and conversion required on the value. For text values that normally means this
* method just commits the edit but for numeric values, for example, it may first parse the given input. <p> The only
* situation that needs to be treated specially is when the field is losing focus. If you user hits enter to commit the
* cell with bad data we can happily cancel the commit and force them to enter a real value. If they click away from the
* cell though we want to give them their old value back.
*
* #param losingFocus true if the reason for the call was because the field is losing focus.
*/
protected abstract void commitHelper(boolean losingFocus);
/**
* Provides the string representation of the value of this cell when the cell is not being edited.
*/
protected abstract String getString();
#Override
public void startEdit() {
super.startEdit();
if (textField == null) {
createTextField();
}
setGraphic(textField);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
Platform.runLater(new Runnable() {
#Override
public void run() {
textField.selectAll();
textField.requestFocus();
}
});
}
#Override
public void cancelEdit() {
super.cancelEdit();
setText(getString());
setContentDisplay(ContentDisplay.TEXT_ONLY);
//Once the edit has been cancelled we no longer need the text field
//so we mark it for cleanup here. Note though that you have to handle
//this situation in the focus listener which gets fired at the end
//of the editing.
textField = null;
}
#Override
public void updateItem(T 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.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
if (t.getCode() == KeyCode.ENTER) {
commitHelper(false);
} else if (t.getCode() == KeyCode.ESCAPE) {
cancelEdit();
} else if (t.getCode() == KeyCode.TAB) {
commitHelper(false);
TableColumn nextColumn = getNextColumn(!t.isShiftDown());
TablePosition focusedCellPosition = getTableView().getFocusModel().getFocusedCell();
if (nextColumn != null) {
//if( focusedCellPosition.getColumn() ){}focusedCellPosition.getTableColumn()
System.out.println("Column: "+focusedCellPosition.getColumn());
System.out.println("nextColumn.getId();: "+nextColumn.getId());
if( nextColumn.getId().equals("colMNO") ){
collectionList.add(new Collection());
getTableView().edit((getTableRow().getIndex())+1,getTableView().getColumns().get(0) );
getTableView().layout();
} else {
getTableView().edit(getTableRow().getIndex(), nextColumn);
}
}else{
getTableView().edit((getTableRow().getIndex())+1,getTableView().getColumns().get(0) );
}
}
}
});
textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
//This focus listener fires at the end of cell editing when focus is lost
//and when enter is pressed (because that causes the text field to lose focus).
//The problem is that if enter is pressed then cancelEdit is called before this
//listener runs and therefore the text field has been cleaned up. If the
//text field is null we don't commit the edit. This has the useful side effect
//of stopping the double commit.
if (!newValue && textField != null) {
commitHelper(true);
}
}
});
}
/**
*
* #param forward true gets the column to the right, false the column to the left of the current column
* #return
*/
private TableColumn<S, ?> getNextColumn(boolean forward) {
List<TableColumn<S, ?>> columns = new ArrayList<>();
for (TableColumn<S, ?> column : getTableView().getColumns()) {
columns.addAll(getLeaves(column));
}
//There is no other column that supports editing.
if (columns.size() < 2) {
return null;
}
int currentIndex = columns.indexOf(getTableColumn());
int nextIndex = currentIndex;
if (forward) {
nextIndex++;
if (nextIndex > columns.size() - 1) {
nextIndex = 0;
}
} else {
nextIndex--;
if (nextIndex < 0) {
nextIndex = columns.size() - 1;
}
}
return columns.get(nextIndex);
}
private List<TableColumn<S, ?>> getLeaves(TableColumn<S, ?> root) {
List<TableColumn<S, ?>> columns = new ArrayList<>();
if (root.getColumns().isEmpty()) {
//We only want the leaves that are editable.
if (root.isEditable()) {
columns.add(root);
}
return columns;
} else {
for (TableColumn<S, ?> column : root.getColumns()) {
columns.addAll(getLeaves(column));
}
return columns;
}
}
}
public class Collection {
private int id;
private String mno;
private String name;
private float quantity;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getMno() {
return mno;
}
public void setMno(String mno) {
this.mno = mno;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getQuantity() {
return quantity;
}
public void setQuantity(float quantity) {
this.quantity = quantity;
}
}
}
The problem is when i take the same code to a controller and add this table programmatically, does not work as before: it jumps next line and go to third.

Before asking the TableView to edit the cell it's important to make sure that it has focus, that the cell in question is in view, and that the view layout is up to date. This is probably because of the way TableView uses virtual cells.
Add these three lines before any call to TableView#edit:
getTableView().requestFocus();
getTableView().scrollTo(rowToEdit);
getTableView().layout();
// getTableView().edit goes here.
This solved this problem for me.

Related

How to filter hyperlink cells in swt tableviewer

I have a problem i can't solve and I have spent a lot of time. I have a table and I have a column with hyperlinks. I have some filters added to the table and when I activate one of those filters, the hyperlink column doesn't refresh correctly. The code I implemented is showing above. This is an example you can copy&paste, run & reproduce the bug:
The Table class:
package borrar;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.widgets.Hyperlink;
public class Example {
private static TableViewer tViewer = null;
private static Table tblTrades = null;
private static Text txtTicker;
public static TickerFilter2 tickerFilter = new TickerFilter2();
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new FillLayout());
Composite parent = new Composite(shell, SWT.NONE);
parent.setLayout(new FillLayout());
parent.setLayout(new GridLayout(1, true));
Label lblTicker = new Label(parent, SWT.NONE);
lblTicker.setText("Search: ");
txtTicker = new Text(parent, SWT.NONE);
txtTicker.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
txtTicker.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent ke) {
tickerFilter.setSearchText(txtTicker.getText());
tViewer.refresh();
}
});
tViewer = new TableViewer(parent, SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION | SWT.CENTER);
String[] titles = { "Ticker", "Hyperlinks" };
createColumns(titles);
tblTrades = tViewer.getTable();
tViewer.setContentProvider(new ArrayContentProvider());
List<DataTable> dt = Arrays.asList(new DataTable("AAA", "ImagePath"), new DataTable("ABBBBBB", "ImagePath"),
new DataTable("BBBBBBB", "ImagePath"));
tViewer.setInput(dt);
tblTrades.setHeaderVisible(true);
tViewer.addFilter(tickerFilter);
shell.open();
shell.pack();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
private static void createColumns(String[] titles) {
TableViewerColumn col = createTableViewerColumn(titles[0], 70);
col.setLabelProvider(new ColumnLabelProvider() {
#Override
public String getText(Object element) {
DataTable op = (DataTable) element;
if (op.getTicker() != null) {
return op.getTicker();
} else {
return "";
}
}
});
col = createTableViewerColumn(titles[1], 80);
col.setLabelProvider(new ColumnLabelProvider() {
Map<Object, Hyperlink> hyperlinks = new HashMap<Object, Hyperlink>();
#Override
public void update(ViewerCell cell) {
TableItem item = (TableItem) cell.getItem();
final Hyperlink hyperlink;
if (hyperlinks.containsKey(cell.getElement()) && !hyperlinks.get(cell.getElement()).isDisposed()) {
hyperlink = hyperlinks.get(cell.getElement());
} else {
hyperlink = new Hyperlink((Composite) (cell.getViewerRow().getControl()), SWT.NONE);
if (cell.getElement() instanceof DataTable) {
DataTable trade = (DataTable) cell.getElement();
if (trade.getPath() != null && !trade.getPath().equals("")) {
hyperlink.setText(trade.getPath() + "-" + trade.getTicker());
hyperlink.setHref(trade.getPath());
}
}
hyperlink.addHyperlinkListener(new HyperlinkAdapter() {
public void linkActivated(HyperlinkEvent e) {
org.eclipse.swt.program.Program.launch(hyperlink.getHref().toString());
}
});
hyperlinks.put(cell.getElement(), hyperlink);
}
TableEditor editor = new TableEditor(item.getParent());
editor.grabHorizontal = true;
editor.grabVertical = true;
editor.setEditor(hyperlink, item, cell.getColumnIndex());
editor.layout();
}
});
}
private static TableViewerColumn createTableViewerColumn(String title, int bound) {
final TableViewerColumn viewerColumn = new TableViewerColumn(tViewer, SWT.CENTER);
final TableColumn column = viewerColumn.getColumn();
column.setText(title);
column.setWidth(bound);
column.setResizable(true);
column.setMoveable(true);
return viewerColumn;
}
public void refreshTable() {
tViewer.refresh();
}
}
My filter class:
package borrar;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
public class TickerFilter2 extends ViewerFilter {
private String searchString;
public void setSearchText(String s) {
// ensure that the value can be used for matching
this.searchString = ".*" + s + ".*";
}
#Override
public boolean select(Viewer viewer, Object parentElement, Object element) {
if (searchString == null || searchString.length() == 0) {
return true;
}
DataTable trade = (DataTable) element;
if (trade.getTicker().matches(searchString)) {
return true;
}
return false;
}
}
The data table object:
package borrar;
public class DataTable {
String ticker;
String path;
public DataTable(String ticker, String path) {
super();
this.ticker = ticker;
this.path = path;
}
public String getTicker() {
return ticker;
}
public void setTicker(String ticker) {
this.ticker = ticker;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
If you put in the search field a "z" or "l" for example, you can see that the hyperlink column does not filter.
The filter class filters data correctly but the hyperlinks column doesn't filter fine.
Here you have an image with the bad result:
The TableViewer doesn't really know about the TableEditor code so it doesn't deal with them when filtering.
You could perhaps use the EditingSupport code. The following code defines a cell editor which does an action as soon as you click on the column cell.
For the column definition replace your code with something like:
col.setLabelProvider(new ColumnLabelProvider() {
#Override
public String getText(final Object element) {
// Whatever you want for the cell text here
final DataTable op = (DataTable) element;
return op.getPath();
}
});
col.setEditingSupport(new HyperlinkEditingSupport(tViewer));
The editing support class:
public class HyperlinkEditingSupport extends EditingSupport
{
private final HyperlinkCellEditor _editor;
public HyperlinkEditingSupport(final TableViewer viewer)
{
super(viewer);
_editor = new HyperlinkCellEditor(viewer.getTable());
}
#Override
protected CellEditor getCellEditor(final Object element)
{
return _editor;
}
#Override
protected boolean canEdit(final Object element)
{
return true;
}
#Override
protected Object getValue(final Object element)
{
// Return the value you want launched
return ((DataTable)element).getPath();
}
#Override
protected void setValue(final Object element, final Object value)
{
// no action
}
}
And the CellEditor. This is a bit odd because we are just going to launch as soon as the editor is invoked so we don't really need a separate editor:
public class HyperlinkCellEditor extends CellEditor
{
public HyperlinkCellEditor(final Composite parent)
{
super(parent);
}
#Override
protected Control createControl(final Composite parent)
{
return new Composite(parent, SWT.None);
}
#Override
protected Object doGetValue()
{
return new Object[0];
}
#Override
protected void doSetFocus()
{
// no action
}
#Override
protected void doSetValue(final Object value)
{
final String path = (String)value;
// TODO Open the link
}
}
I have modified #greg-449 solution to show a link appearance in the cell only modifying the LabelProvider:
col.setLabelProvider(new StyledCellLabelProvider() {
#Override
public void update(ViewerCell cell) {
DataTable op = (DataTable) cell.getItem().getData();
StyledString ss = new StyledString();
StyleRange sr = new StyleRange(0, 80, Display.getCurrent().getSystemColor(SWT.COLOR_BLUE), null);
sr.underline = true;
ss.append(op.getPath(), StyledString.DECORATIONS_STYLER);
cell.setText("Open Image");
StyleRange[] range = { sr };
cell.setStyleRanges(range);
}
});
col.setEditingSupport(new HyperlinkEditingSupport(tViewer));

what i should know to do this in android

i wanna ask you exactly about the action that when i click on the button, the result will be inserted directly and automatically into their "input id" ... how can i do it in the android??
Well, if you are not familiar with Android, then I will advise you to take some tutorials about Android basics and Java.
Apart from that, Android does provide geocoding functionality, you should have latitude and longitude to run the following code:
Geocoder geocoder;
List<Address> addresses;
geocoder = new Geocoder(this, Locale.getDefault());
addresses = geocoder.getFromLocation(latitude, longitude, 1); // Here 1 represent max location result to returned, by documents it recommended 1 to 5
String address = addresses.get(0).getAddressLine(0);
String city = addresses.get(0).getLocality();
String state = addresses.get(0).getAdminArea();
String country = addresses.get(0).getCountryName();
String postalCode = addresses.get(0).getPostalCode();
String knownName = addresses.get(0).getFeatureName();
If you don't have the latitude and longitude of the user and wanted to access it also from an android device, you can use the following process:
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Activity
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;
public class MainActivity extends Activity {
TextView textview;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.geo_locations);
// check if GPS enabled
GPSTracker gpsTracker = new GPSTracker(this);
if (gpsTracker.getIsGPSTrackingEnabled())
{
String stringLatitude = String.valueOf(gpsTracker.latitude);
textview = (TextView)findViewById(R.id.fieldLatitude);
textview.setText(stringLatitude);
String stringLongitude = String.valueOf(gpsTracker.longitude);
textview = (TextView)findViewById(R.id.fieldLongitude);
textview.setText(stringLongitude);
String country = gpsTracker.getCountryName(this);
textview = (TextView)findViewById(R.id.fieldCountry);
textview.setText(country);
String city = gpsTracker.getLocality(this);
textview = (TextView)findViewById(R.id.fieldCity);
textview.setText(city);
String postalCode = gpsTracker.getPostalCode(this);
textview = (TextView)findViewById(R.id.fieldPostalCode);
textview.setText(postalCode);
String addressLine = gpsTracker.getAddressLine(this);
textview = (TextView)findViewById(R.id.fieldAddressLine);
textview.setText(addressLine);
}
else
{
// can't get location
// GPS or Network is not enabled
// Ask user to enable GPS/network in settings
gpsTracker.showSettingsAlert();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.varna_lab_geo_locations, menu);
return true;
}
}
GPS Tracker
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import android.app.AlertDialog;
import android.app.Service;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.Settings;
import android.util.Log;
/**
* Create this Class from tutorial :
* http://www.androidhive.info/2012/07/android-gps-location-manager-tutorial
*
* For Geocoder read this : http://stackoverflow.com/questions/472313/android-reverse-geocoding-getfromlocation
*
*/
public class GPSTracker extends Service implements LocationListener {
// Get Class Name
private static String TAG = GPSTracker.class.getName();
private final Context mContext;
// flag for GPS Status
boolean isGPSEnabled = false;
// flag for network status
boolean isNetworkEnabled = false;
// flag for GPS Tracking is enabled
boolean isGPSTrackingEnabled = false;
Location location;
double latitude;
double longitude;
// How many Geocoder should return our GPSTracker
int geocoderMaxResults = 1;
// The minimum distance to change updates in meters
private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters
// The minimum time between updates in milliseconds
private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute
// Declaring a Location Manager
protected LocationManager locationManager;
// Store LocationManager.GPS_PROVIDER or LocationManager.NETWORK_PROVIDER information
private String provider_info;
public GPSTracker(Context context) {
this.mContext = context;
getLocation();
}
/**
* Try to get my current location by GPS or Network Provider
*/
public void getLocation() {
try {
locationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE);
//getting GPS status
isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
//getting network status
isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
// Try to get location if you GPS Service is enabled
if (isGPSEnabled) {
this.isGPSTrackingEnabled = true;
Log.d(TAG, "Application use GPS Service");
/*
* This provider determines location using
* satellites. Depending on conditions, this provider may take a while to return
* a location fix.
*/
provider_info = LocationManager.GPS_PROVIDER;
} else if (isNetworkEnabled) { // Try to get location if you Network Service is enabled
this.isGPSTrackingEnabled = true;
Log.d(TAG, "Application use Network State to get GPS coordinates");
/*
* This provider determines location based on
* availability of cell tower and WiFi access points. Results are retrieved
* by means of a network lookup.
*/
provider_info = LocationManager.NETWORK_PROVIDER;
}
// Application can use GPS or Network Provider
if (!provider_info.isEmpty()) {
locationManager.requestLocationUpdates(
provider_info,
MIN_TIME_BW_UPDATES,
MIN_DISTANCE_CHANGE_FOR_UPDATES,
this
);
if (locationManager != null) {
location = locationManager.getLastKnownLocation(provider_info);
updateGPSCoordinates();
}
}
}
catch (Exception e)
{
//e.printStackTrace();
Log.e(TAG, "Impossible to connect to LocationManager", e);
}
}
/**
* Update GPSTracker latitude and longitude
*/
public void updateGPSCoordinates() {
if (location != null) {
latitude = location.getLatitude();
longitude = location.getLongitude();
}
}
/**
* GPSTracker latitude getter and setter
* #return latitude
*/
public double getLatitude() {
if (location != null) {
latitude = location.getLatitude();
}
return latitude;
}
/**
* GPSTracker longitude getter and setter
* #return
*/
public double getLongitude() {
if (location != null) {
longitude = location.getLongitude();
}
return longitude;
}
/**
* GPSTracker isGPSTrackingEnabled getter.
* Check GPS/wifi is enabled
*/
public boolean getIsGPSTrackingEnabled() {
return this.isGPSTrackingEnabled;
}
/**
* Stop using GPS listener
* Calling this method will stop using GPS in your app
*/
public void stopUsingGPS() {
if (locationManager != null) {
locationManager.removeUpdates(GPSTracker.this);
}
}
/**
* Function to show settings alert dialog
*/
public void showSettingsAlert() {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
//Setting Dialog Title
alertDialog.setTitle(R.string.GPSAlertDialogTitle);
//Setting Dialog Message
alertDialog.setMessage(R.string.GPSAlertDialogMessage);
//On Pressing Setting button
alertDialog.setPositiveButton(R.string.action_settings, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which)
{
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
mContext.startActivity(intent);
}
});
//On pressing cancel button
alertDialog.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which)
{
dialog.cancel();
}
});
alertDialog.show();
}
/**
* Get list of address by latitude and longitude
* #return null or List<Address>
*/
public List<Address> getGeocoderAddress(Context context) {
if (location != null) {
Geocoder geocoder = new Geocoder(context, Locale.ENGLISH);
try {
/**
* Geocoder.getFromLocation - Returns an array of Addresses
* that are known to describe the area immediately surrounding the given latitude and longitude.
*/
List<Address> addresses = geocoder.getFromLocation(latitude, longitude, this.geocoderMaxResults);
return addresses;
} catch (IOException e) {
//e.printStackTrace();
Log.e(TAG, "Impossible to connect to Geocoder", e);
}
}
return null;
}
/**
* Try to get AddressLine
* #return null or addressLine
*/
public String getAddressLine(Context context) {
List<Address> addresses = getGeocoderAddress(context);
if (addresses != null && addresses.size() > 0) {
Address address = addresses.get(0);
String addressLine = address.getAddressLine(0);
return addressLine;
} else {
return null;
}
}
/**
* Try to get Locality
* #return null or locality
*/
public String getLocality(Context context) {
List<Address> addresses = getGeocoderAddress(context);
if (addresses != null && addresses.size() > 0) {
Address address = addresses.get(0);
String locality = address.getLocality();
return locality;
}
else {
return null;
}
}
/**
* Try to get Postal Code
* #return null or postalCode
*/
public String getPostalCode(Context context) {
List<Address> addresses = getGeocoderAddress(context);
if (addresses != null && addresses.size() > 0) {
Address address = addresses.get(0);
String postalCode = address.getPostalCode();
return postalCode;
} else {
return null;
}
}
/**
* Try to get CountryName
* #return null or postalCode
*/
public String getCountryName(Context context) {
List<Address> addresses = getGeocoderAddress(context);
if (addresses != null && addresses.size() > 0) {
Address address = addresses.get(0);
String countryName = address.getCountryName();
return countryName;
} else {
return null;
}
}
#Override
public void onLocationChanged(Location location) {
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
Some tutorials:
https://www.androidhive.info/2012/07/android-gps-location-manager-tutorial/
https://www.tutorialspoint.com/android/android_location_based_services.htm
https://www.rishabhsoft.com/blog/android-geocoding-and-reverse-geocoding
https://developers.google.com/maps/documentation/geocoding/start
https://www.tutorialspoint.com/android/android_location_based_services.htm

Drag and drop with JavaFX TreeView

I implemented Drag and Drop feature as recommended in other threads in this forum. I have a cellFactory on a TreeView and set the events on the cells.
tvProject.setCellFactory(new Callback<TreeView<PlanningItem>, TreeCell<PlanningItem>>() {
#Override
public PlanningCheckBoxTreeCell call(TreeView<PlanningItem> siTreeView) {
final PlanningCheckBoxTreeCell source = new PlanningCheckBoxTreeCell();
final PlanningCheckBoxTreeCell target = new PlanningCheckBoxTreeCell();
source.setOnDragDetected(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
/* drag was detected, start drag-and-drop gesture */
System.out.println("onDragDetected");
/* allow any transfer mode */
Dragboard db = source.startDragAndDrop(TransferMode.ANY);
/* put a string on dragboard */
ClipboardContent content = new ClipboardContent();
content.putString(source.getText());
db.setContent(content);
event.consume();
}
});
target.setOnDragOver(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
/* data is dragged over the target */
System.out.println("onDragOver");
...
event.consume();
}
});
The setOnDragDetected is executed/hit as wanted but all other events are NOT (I did not post them all here: setOnDragOver,setOnDragEntered,setOnDragExited,setOnDragDropped,setOnDragDone).
The PlanningCheckBoxTreeCell is a custom implementation of a treecell as follows:
public class PlanningCheckBoxTreeCell extends CheckBoxTreeCell<PlanningItem> {
public PlanningCheckBoxTreeCell() {
}
#Override
public void updateItem(PlanningItem item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
setText(null);
}
}
}
UPDATE PlanningItem:
package at.v2c2.testplaygroundui;
import java.io.Serializable;
public class PlanningItem implements Serializable {
private static final long serialVersionUID = 1L;
private Double scene = null;
private Integer path = null;
private String move = null;
/**
* #return the scene
*/
public Double getScene() {
return scene;
}
/**
* #param scene the scene to set
*/
private void setScene(Double scene) {
this.scene = scene;
}
/**
* #return the path
*/
public Integer getPath() {
return path;
}
/**
* #param path the path to set
*/
private void setPath(Integer path) {
this.path = path;
}
/**
* #return the move
*/
public String getMove() {
return move;
}
/**
* #param move the move to set
*/
private void setMove(String move) {
this.move = move;
}
public PlanningItem(Object item) {
super();
if (item instanceof Double) {
setScene((Double) item);
} else if (item instanceof Integer) {
setPath((Integer) item);
} else if (item instanceof String) {
setMove((String) item);
}
}
}
UPDATE The MCVE:
package at.v2c2.testplaygroundui;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DataFormat;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
public final class TestplaygroundUi extends Application {
private final DataFormat objectDataFormat = new DataFormat("application/x-java-serialized-object");
/**
* Constructor.
*/
public TestplaygroundUi() {
// empty.
}
/**
* #param args Program arguments.
*/
public static void main(final String[] args) {
launch(args);
}
#Override
public void start(final Stage primaryStage) throws Exception {
TreeItem<PlanningItem> treeItemRoot = new TreeItem<>(new PlanningItem(1.0));
TreeItem<PlanningItem> nodeItemA = new TreeItem<>(new PlanningItem(2));
TreeItem<PlanningItem> nodeItemB = new TreeItem<>(new PlanningItem(2));
treeItemRoot.getChildren().addAll(nodeItemA, nodeItemB);
TreeItem<PlanningItem> nodeItemA1 = new TreeItem<>(new PlanningItem("A1"));
TreeItem<PlanningItem> nodeItemB1 = new TreeItem<>(new PlanningItem("B1"));
nodeItemA.getChildren().addAll(nodeItemA1);
nodeItemB.getChildren().addAll(nodeItemB1);
TreeView<PlanningItem> treeView = new TreeView<>(treeItemRoot);
treeView.setCellFactory(new Callback<TreeView<PlanningItem>, TreeCell<PlanningItem>>() {
#Override
public TreeCell<PlanningItem> call(TreeView<PlanningItem> siTreeView) {
final TreeCell<PlanningItem> cell = new TreeCell<>();
cell.setOnDragDetected(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
/* drag was detected, start drag-and-drop gesture */
System.out.println("onDragDetected");
/* allow any transfer mode */
Dragboard db = cell.startDragAndDrop(TransferMode.MOVE);
/* put a string on dragboard */
ClipboardContent content = new ClipboardContent();
content.put(objectDataFormat, cell.getItem());
// content.putString("Hello");// cell.getText());
db.setContent(content);
event.consume();
}
});
cell.setOnDragOver(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
/* data is dragged over the target */
System.out.println("onDragOver");
/*
* accept it only if it is not dragged from the same node and if it has a string data
*/
if (event.getGestureSource() != cell && event.getDragboard().hasString()) {
/* allow for both copying and moving, whatever user chooses */
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
}
event.consume();
}
});
cell.setOnDragEntered(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
/* the drag-and-drop gesture entered the target */
System.out.println("onDragEntered");
/* show to the user that it is an actual gesture target */
if (event.getGestureSource() != cell && event.getDragboard().hasString()) {
// target.setFill(Color.GREEN);
}
event.consume();
}
});
cell.setOnDragExited(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
System.out.println("onDragExited");
/* mouse moved away, remove the graphical cues */
// target.setFill(Color.BLACK);
event.consume();
}
});
cell.setOnDragDropped(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
/* data dropped */
System.out.println("onDragDropped");
/* if there is a string data on dragboard, read it and use it */
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasString()) {
cell.setText(db.getString());
success = true;
}
/*
* let the source know whether the string was successfully transferred and used
*/
event.setDropCompleted(success);
event.consume();
}
});
cell.setOnDragDone(new EventHandler<DragEvent>() {
public void handle(DragEvent event) {
/* the drag-and-drop gesture ended */
System.out.println("onDragDone");
/* if the data was successfully moved, clear it */
if (event.getTransferMode() == TransferMode.MOVE) {
cell.setText("");
}
event.consume();
}
});
return cell;
};
});
StackPane root = new StackPane();
root.getChildren().add(treeView);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.setTitle("Test TreeView");
primaryStage.show();
}
}
Thanks in advance

How to display a clob data more than 66k in text field in oracle forms 12g

I don't know how to put clob data which is of more than 66k in Oracle Forms.
The text field will take a long data type and that too not more than 66k. I have a clob data and wanted to display.
The easiest way to display this is too make a forms bean (PJC).
Then you can display it in a JTextPane which is big enough.
You can then make a function that gives you piece of 32000 characters from the clob and give them to the bean.
package be.axi.oracle.forms.jpc;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.text.BadLocationException;
import oracle.forms.handler.IHandler;
import oracle.forms.properties.ID;
import oracle.forms.ui.CustomEvent;
import oracle.forms.ui.VBean;
/**
* A TextArea to get more than 64k texts
* #author Francois Degrelle
* #version 1.1
*/
public class BigTextArea extends VBean implements FocusListener, KeyListener
{
private static final long serialVersionUID = 1L;
public final static ID ADDTEXT = ID.registerProperty("ADD_TEXT");
public final static ID VALUE = ID.registerProperty("VALUE");
public final static ID SHOW = ID.registerProperty("SHOW");
public final static ID CLEAR = ID.registerProperty("CLEAR");
public final static ID GETTEXT = ID.registerProperty("GET_TEXT");
public final static ID GETLENGTH = ID.registerProperty("GET_LENGTH");
public final static ID pLostFocus = ID.registerProperty("BEAN_QUITTED");
private IHandler m_handler;
private int iStart = 0 ;
private int iChunk = 8192 ;
private StringBuffer sb = new StringBuffer();
protected JTextPane jtp = new JTextPane();
public BigTextArea()
{
super();
try
{
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
SwingUtilities.updateComponentTreeUI(this);
}
catch (Exception ex)
{
ex.printStackTrace();
}
JScrollPane ps = new JScrollPane(jtp);
ps.setBorder(null);
add(ps);
ps.setVisible(true);
}
public void init(IHandler handler)
{
m_handler = handler;
super.init(handler);
addFocusListener(this);
jtp.addFocusListener(this);
jtp.addKeyListener(this);
}
public boolean setProperty(ID property, Object value)
{
//
// add text to the TextArea
//
if (property == ADDTEXT)
{
sb.append(value.toString()) ;
printMemory();
return true;
}
//
// display the whole text
//
else if(property == SHOW)
{
jtp.setText(sb.toString());
jtp.setCaretPosition(0);
sb = new StringBuffer();
System.gc();
printMemory();
return true ;
}
//
// clear the TextArea
//
else if(property == CLEAR) {
jtp.setText("");
return true ;
}
else
{
return super.setProperty(property, value);
}
}
/*-----------------------------------*
* Get the result string from Forms *
*-----------------------------------*/
public Object getProperty(ID pId)
{
//
// returns the text length
//
if (pId == GETLENGTH)
{
return "" + jtp.getText().length();
}
//
// returns the chunks
//
else if (pId == GETTEXT) {
String s = "" ;
int iLen = jtp.getText().length() ;
while (iStart < iLen)
{
try{
if(iStart+iChunk <= iLen) s = jtp.getText(iStart,iChunk);
else s = jtp.getText(iStart,iLen-iStart);
iStart += iChunk ;
return s ;
}
catch (BadLocationException ble) { ble.printStackTrace(); return ""; }
}
iStart = 0 ;
return "" ;
}
else
{
return super.getProperty(pId);
}
} // getProperty()
/*--------------------------*
* handle the focus events *
*--------------------------*/
public void focusGained(FocusEvent e)
{
if (e.getComponent() == this)
{
// put the focus on the component
jtp.requestFocus();
}
try
{
m_handler.setProperty(FOCUS_EVENT, e);
}
catch ( Exception ex )
{
;
}
}
public void focusLost(FocusEvent e)
{
CustomEvent ce = new CustomEvent(m_handler, pLostFocus);
dispatchCustomEvent(ce);
}
/*--------------------------*
* Handle the Key listener *
*--------------------------*/
public void keyPressed(KeyEvent e)
{
/*
** Allows TAB key to exit the item
** and continue the standard Forms navigation
*/
if ( (e.getKeyCode() == KeyEvent.VK_TAB) )
{
try
{
m_handler.setProperty(KEY_EVENT, e);
}
catch ( Exception ex )
{
}
}
}
public void keyTyped(KeyEvent e)
{
}
public void keyReleased(KeyEvent e)
{
}
// utility to output the memory available
private void printMemory() {
System.out.println("Java memory in use = "
+ (Runtime.getRuntime().totalMemory()
- Runtime.getRuntime().freeMemory()));
}
}

Can't stop javafx tables from ignoring my the setter function validation

I'm using javafx to do some table stuff. I want to validate my textfields in the myTextRow Class. In the "setText2" method I check the input if it is not bigger than 6 symbols, but it has no effects at all.
import java.util.ArrayList;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TextArea;
import javafx.util.Callback;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Supermain extends Application {
#Override
public void start(Stage primaryStage) {
ArrayList myindizes=new ArrayList();
final TableView<myTextRow> table = new TableView<>();
table.setEditable(true);
table.setStyle("-fx-text-wrap: true;");
//Table columns
TableColumn<myTextRow, String> clmID = new TableColumn<>("ID");
clmID.setMinWidth(160);
clmID.setCellValueFactory(new PropertyValueFactory<>("ID"));
TableColumn<myTextRow, String> clmtext = new TableColumn<>("Text");
clmtext.setMinWidth(160);
clmtext.setCellValueFactory(new PropertyValueFactory<>("text"));
clmtext.setCellFactory(new TextFieldCellFactory());
TableColumn<myTextRow, String> clmtext2 = new TableColumn<>("Text2");
clmtext2.setMinWidth(160);
clmtext2.setCellValueFactory(new PropertyValueFactory<>("text2"));
clmtext2.setCellFactory(new TextFieldCellFactory());
//Add data
final ObservableList<myTextRow> data = FXCollections.observableArrayList(
new myTextRow(5, "Lorem","bla"),
new myTextRow(2, "Ipsum","bla")
);
table.getColumns().addAll(clmID, clmtext,clmtext2);
table.setItems(data);
HBox hBox = new HBox();
hBox.setSpacing(5.0);
hBox.setPadding(new Insets(5, 5, 5, 5));
Button btn = new Button();
btn.setText("Get Data");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
for (myTextRow data1 : data) {
System.out.println("data:" + data1.getText2());
}
}
});
hBox.getChildren().add(btn);
BorderPane pane = new BorderPane();
pane.setTop(hBox);
pane.setCenter(table);
primaryStage.setScene(new Scene(pane, 640, 480));
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
public static class TextFieldCellFactory
implements Callback<TableColumn<myTextRow, String>, TableCell<myTextRow, String>> {
#Override
public TableCell<myTextRow, String> call(TableColumn<myTextRow, String> param) {
TextFieldCell textFieldCell = new TextFieldCell();
return textFieldCell;
}
public static class TextFieldCell extends TableCell<myTextRow, String> {
private TextArea textField;
private StringProperty boundToCurrently = null;
public TextFieldCell() {
textField = new TextArea();
textField.setWrapText(true);
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
this.setGraphic(textField);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
// Show the Text Field
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
// myindizes.add(getIndex());
// Retrieve the actual String Property that should be bound to the TextField
// If the TextField is currently bound to a different StringProperty
// Unbind the old property and rebind to the new one
ObservableValue<String> ov = getTableColumn().getCellObservableValue(getIndex());
SimpleStringProperty sp = (SimpleStringProperty) ov;
if (this.boundToCurrently == null) {
this.boundToCurrently = sp;
this.textField.textProperty().bindBidirectional(sp);
} else if (this.boundToCurrently != sp) {
this.textField.textProperty().unbindBidirectional(this.boundToCurrently);
this.boundToCurrently = sp;
this.textField.textProperty().bindBidirectional(this.boundToCurrently);
}
double height = real_lines_height(textField.getText(), this.getWidth(), 30, 22);
textField.setPrefHeight(height);
textField.setMaxHeight(height);
textField.setMaxHeight(Double.MAX_VALUE);
// if height bigger than the biggest height in the row
//-> change all heights of the row(textfields ()typeof textarea) to this height
// else leave the height as it is
//System.out.println("item=" + item + " ObservableValue<String>=" + ov.getValue());
//this.textField.setText(item); // No longer need this!!!
} else {
this.setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
}
public class myTextRow {
private final SimpleIntegerProperty ID;
private final SimpleStringProperty text;
private final SimpleStringProperty text2;
public myTextRow(int ID, String text,String text2) {
this.ID = new SimpleIntegerProperty(ID);
this.text = new SimpleStringProperty(text);
this.text2 = new SimpleStringProperty(text2);
}
public void setID(int id) {
this.ID.set(id);
}
public void setText(String text) {
this.text.set(text);
}
public void setText2(String text) {
if(text2check(text)){
this.text2.set(text);}
else
{System.out.println("wrong value!!!");}
}
public int getID() {
return ID.get();
}
public String getText() {
return text.get();
}
public StringProperty textProperty() {
return text;
}
public String getText2() {
return text2.get();
}
public StringProperty text2Property() {
return text2;
}
public IntegerProperty IDProperty() {
return ID;
}
public boolean text2check(String t)
{
if(t.length()>6)return false;
return true;
}
}
private static double real_lines_height(String s, double width, double heightCorrector, double widthCorrector) {
HBox h = new HBox();
Label l = new Label("Text");
h.getChildren().add(l);
Scene sc = new Scene(h);
l.applyCss();
double line_height = l.prefHeight(-1);
int new_lines = s.replaceAll("[^\r\n|\r|\n]", "").length();
// System.out.println("new lines= "+new_lines);
String[] lines = s.split("\r\n|\r|\n");
// System.out.println("line count func= "+ lines.length);
int count = 0;
//double rest=0;
for (int i = 0; i < lines.length; i++) {
double text_width = get_text_width(lines[i]);
double plus_lines = Math.ceil(text_width / (width - widthCorrector));
if (plus_lines > 1) {
count += plus_lines;
//rest+= (text_width / (width-widthCorrector)) - plus_lines;
} else {
count += 1;
}
}
//count+=(int) Math.ceil(rest);
count += new_lines - lines.length;
return count * line_height + heightCorrector;
}
private static double get_text_width(String s) {
HBox h = new HBox();
Label l = new Label(s);
l.setWrapText(false);
h.getChildren().add(l);
Scene sc = new Scene(h);
l.applyCss();
// System.out.println("dubbyloop.FXMLDocumentController.get_text_width(): "+l.prefWidth(-1));
return l.prefWidth(-1);
}
}
A rule of the JavaFX Properties pattern is that for a property x, invoking xProperty().setValue(value) should always be identical to invoking setX(value). Your validation makes this not true. The binding your cell implementation uses invokes the setValue method on the property, which is why it bypasses your validation check.
(Side note: in all the code I am going to change the names so that they adhere to proper naming conventions.)
The default way to implement a property in this pattern is:
public class MyTextRow {
private final StringProperty text = new SimpleStringProperty();
public StringProperty textProperty() {
return text ;
}
public final void setText(String text) {
textProperty().set(text);
}
public final String getText() {
return textProperty().get();
}
}
By having the set/get methods delegate to the appropriate property methods, you are guaranteed these rules are enforced, even if the textProperty() methods is overridden in a subclass. Making the set and get methods final ensures that the rule is not broken by a subclass overriding those methods.
One approach might be to override the set and setValue methods in the property, as follows:
public class MyTextRow {
private final StringProperty text2 = new StringPropertyBase() {
#Override
public String getName() {
return "text2";
}
#Override
public Object getBean() {
return MyTextRow.this ;
}
#Override
public void setValue(String value) {
if (text2Check(value)) {
super.setValue(value);
}
}
#Override
public void set(String value) {
if (text2Check(value)) {
super.set(value);
}
}
}
public StringProperty text2Property() {
return text2 ;
}
public final void setText2(String text2) {
text2Property().set(text2);
}
public final String getText2() {
return text2Property().get();
}
// ...
}
however, I think this will break the bidirectional binding that you have with the text property in the TextArea (basically, there is no way to communicate back to the text area when a change is vetoed, so the text area will not know to revert to the previous value). One fix would be to implement your cell using listeners on the properties instead of bindings. You could use a TextFormatter on the text area that simply updates the property and vetoes the text change if the change doesn't occur.
Here is a complete SSCCE using this approach:
import java.util.function.Function;
import java.util.function.UnaryOperator;
import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.property.StringPropertyBase;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
import javafx.stage.Stage;
public class VetoStringChange extends Application {
#Override
public void start(Stage primaryStage) {
TableView<Item> table = new TableView<>();
table.setEditable(true);
table.getColumns().add(column("Item", Item::nameProperty));
table.getColumns().add(column("Description", Item::descriptionProperty));
for (int i = 1; i <= 20 ; i++) {
table.getItems().add(new Item("Item "+i, ""));
}
primaryStage.setScene(new Scene(table, 600, 600));
primaryStage.show();
}
public static <S> TableColumn<S,String> column(String title, Function<S,Property<String>> property) {
TableColumn<S,String> col = new TableColumn<>(title);
col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
col.setCellFactory(tc -> new TextAreaCell<S>(property));
col.setPrefWidth(200);
return col ;
}
public static class TextAreaCell<S> extends TableCell<S, String> {
private TextArea textArea ;
public TextAreaCell(Function<S, Property<String>> propertyAccessor) {
textArea = new TextArea();
textArea.setWrapText(true);
textArea.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textArea.setMaxHeight(Double.MAX_VALUE);
UnaryOperator<Change> filter = c -> {
String proposedText = c.getControlNewText() ;
Property<String> prop = propertyAccessor.apply(getTableView().getItems().get(getIndex()));
prop.setValue(proposedText);
if (prop.getValue().equals(proposedText)) {
return c ;
} else {
return null ;
}
};
textArea.setTextFormatter(new TextFormatter<String>(filter));
this.setGraphic(textArea);
}
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty) {
if (! textArea.getText().equals(item)) {
textArea.setText(item);
}
// Show the Text Field
this.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
} else {
this.setContentDisplay(ContentDisplay.TEXT_ONLY);
}
}
}
public static class Item {
private final StringProperty name = new StringPropertyBase() {
#Override
public Object getBean() {
return Item.this;
}
#Override
public String getName() {
return "name" ;
}
#Override
public void set(String value) {
if (checkValue(value)) {
super.set(value);
}
}
#Override
public void setValue(String value) {
if (checkValue(value)) {
super.setValue(value);
}
}
};
private final StringProperty description = new SimpleStringProperty();
public Item(String name, String description) {
setName(name);
setDescription(description);
}
private boolean checkValue(String value) {
return value.length() <= 6 ;
}
public final StringProperty nameProperty() {
return this.name;
}
public final String getName() {
return this.nameProperty().get();
}
public final void setName(final String name) {
this.nameProperty().set(name);
}
public final StringProperty descriptionProperty() {
return this.description;
}
public final String getDescription() {
return this.descriptionProperty().get();
}
public final void setDescription(final String description) {
this.descriptionProperty().set(description);
}
}
public static void main(String[] args) {
launch(args);
}
}
Another approach is to allow a "commit and revert" type strategy on your property:
public class MyTextRow {
private final StringProperty text2 = new SimpleStringProperty();
public MyTextRow() {
text2.addListener((obs, oldText, newText) -> {
if (! checkText2(newText)) {
// sanity check:
if (checkText2(oldText)) {
text2.set(oldText);
}
}
});
}
public StringProperty text2Property() {
return text ;
}
public final void setText2(String text2) {
text2Property().set(text2);
}
public final String getText2() {
return text2Property().get();
}
}
In general I dislike validation by listening for an invalid value and reverting like this, because other listeners to the property will see all the changes, including changes to and from invalid values. However, this might be the best option in this case.
Finally, you could consider vetoing invalid changes as in the first option, and also setting a TextFormatter on the control in the cell that simply doesn't allow text entry that results in an invalid string. This isn't always possible from a usability perspective (e.g. if empty strings are invalid, you almost always want to allow the user to temporarily delete all the text), and it means keeping two validation checks in sync in your code, which is a pain.

Resources