I'm new to arcore and am using sceneform for a project. Specifically, I'm using the solar system example and trying to implement the info cards it has in Planet.java to my project. I have the info cards set up(at least i think i do) however, they do not appear on tap. Am I missing something or is there something I am doing wrong? Thank you in advance for your help.
private void makeinfoCards(ArFragment arFragment, Anchor anchor, Context context) {
if (infoCard == null) {
AnchorNode anchorNode = new AnchorNode(anchor);
infoCard = new Node();
infoCard.setParent(anchorNode);
infoCard.setEnabled(false);
infoCard.setLocalPosition(new Vector3(0.0f, 2.90f * INFO_CARD_Y_POS_COEFF, 0.0f));
//below would hide/bring up the info card on tap
infoCard.setOnTapListener( //anchorNode.setOnTapListener doesn't make info card appear either
(hitTestResult, motionEvent) -> {
infoCard.setEnabled(!infoCard.isEnabled());
}
);
ViewRenderable.builder()
.setView(arFragment.getContext(), R.layout.card_view) //putting context instead makes no difference
.build()
.thenAccept(
(renderable) -> {
infoCard.setRenderable(renderable);
TextView textView = (TextView) renderable.getView();
textView.setText("random");
})
.exceptionally(
(throwable) -> {
throw new AssertionError("Could not load plane card view.", throwable);
});
}
}
You have set the onTapListener on the infoCard Node which has been disabled by default. You should use the setOnTapListener on the node which contains the 3D model you will be clicking, try passing the specific node that has the 3D model set as the renderable as an argument to the function instead of the Anchor. If you have the Node say modelNode which has the 3D model.
In your ARActivity call
makeInfoCard(arFragment,modelNode,context);
And in the function use
private void makeInfoCard(ArFragment arFragment, Node modelNode, Context context){
//
modelNode.setOnTapListener((hitTestResult, motionEvent) -> {
infoCard.setEnabled(!infoCard.isEnabled());
});
//
}
Implement Node.OnTapListener interface and than override onTap method.
When model is going to be projected keep record of model nodes in a list. Here I set card for each node present on a sceneform.
#Override
public void onTap(HitTestResult hitTestResult, MotionEvent motionEvent) {
modelDataObj = map.get(hitTestResult.getNode()); // get object againt each node
tvModelName.setText(modelDataObj.getItem_name());
Picasso.get().load(modelDataObj.get_thumbnail()).placeholder(R.drawable.imageplaceholder).into(imgLoadAddInfoImage);
tvTitle.setText(modelDataObj.getItem_name());
tvLoadAddInfoDescText.setText(modelDataObj.getDescription())
}
Related
I created a TableTree that contains object of class Component that has a boolean property "selected".
I want to hide the rows from the table where the rows component is not selected.
I tried this:
componentTree.setRowFactory(new Callback<TreeTableView<Component>, TreeTableRow<Component>>() {
#Override
public TreeTableRow<Component> call(TreeTableView<Component> param) {
TreeTableRow<Component> row = new TreeTableRow<Component>() {
#Override
protected void updateItem(Component component, boolean empty) {
if(!empty) {
if (!component.isSelected()) {
setVisible(false);
setManaged(false);
System.out.println("hide");
} else {
setVisible(true);
setManaged(true);
System.out.println("show");
}
}
}
};
return row;
}
});
On system.out I can see a lot of "show" and "hide" messages, but this doesn't affect the table, all rows are shown as before.
Any idea on this topic?
Thanks!
I used eclipse's fx.ui.controls library for the same achieve the same goal before.
<dependency>
<groupId>at.bestsolution.eclipse</groupId>
<artifactId>org.eclipse.fx.ui.controls</artifactId>
<version>2.2.0</version>
</dependency>
The library provides a class: FilterableTreeItem<T> under the tree package. This class was designed to be used in cases like yours. You can provide a Predicate to the root of the tree and the items will get hidden when the value given changes:
// Children
final FilterableTreeItem<Component> childNode1 = new FilterableTreeItem<>(component1);
final FilterableTreeItem<Component> childNode2 = new FilterableTreeItem<>(component2);
final FilterableTreeItem<Component> childNode3 = new FilterableTreeItem<>(component3);
// Root
final FilterableTreeItem<Component> root = new FilterableTreeItem<>(rootComponent);
root.getInternalChildren().setAll(childNode1, childNode2, childNode3);
root.setPredicate((parent, value) -> value.isSelected());
// TreeTableView
final TreeTableView<Component> treeTableView = new TreeTableView<>(root);
Note that you have to use getInternalChildren() to add children and the default getChildren().
FilterableTreeItem<T> also provides a predicateProperty() that you can bind to another property in case you need to update the how items are shown or hidden.
Another advatage of this class is that it shows the whole path up to the root of the items matching that predicate.
I am working on a TreeView which represents a robot controlling program, each TreeCell represents a statement, and a TreeCell can be nested in an other one. Like in programming, statements can be nested in if or for statements.
Here I have created a simple demo, filled with some random blocks.
Demo Screenshot
To customize the rendering of TreeCell, I have create a class extending TreeCell:
public class TreeDataCell extends TreeCell<TreeData> {
public void updateItem(TreeData item, boolean empty) {
super.updateItem(item, empty);
setText(null);
if (item == null || empty) {
setGraphic(null);
} else {
setGraphic(getCellGraphic(item));
}
}
private Group getCellGraphic(TreeData data) {
Group grp = new Group();
VBox vbox = new VBox();
vbox.setMinWidth(100);
vbox.setMaxWidth(200);
vbox.setBorder(new Border(new BorderStroke(
Color.LIGHTGRAY.darker(),
BorderStrokeStyle.SOLID,
new CornerRadii(10.0),
new BorderWidths(2.0))));
vbox.setBackground(new Background(new BackgroundFill(Color.LIGHTGRAY, new CornerRadii(10.0), null)));
vbox.setEffect(new DropShadow(2.0, 3.0, 3.0, Color.DIMGRAY));
Region header = new Region();
header.setPrefHeight(5.0);
Region footer = new Region();
footer.setPrefHeight(5.0);
Label labTitle = new Label();
labTitle.setFont(new Font("San Serif", 20));
labTitle.setText(data.getTitle());
Label labDesc = null;
if (data.getDescription() != null) {
labDesc = new Label();
labDesc.setWrapText(true);
labDesc.setText(data.getDescription());
}
vbox.getChildren().addAll(header, labTitle);
if (labDesc != null) {
vbox.getChildren().add(labDesc);
}
vbox.getChildren().add(footer);
grp.getChildren().add(vbox);
return grp;
}
}
The TreeData is a simple class containing 2 Strings:
public class TreeData {
private String title;
private String desc;
/* getters + setters */
}
As you can see, the indentation between two levels are too small, and we can barely see statement nesting.
I am hard coding all the styles in Java, since I haven't learnt FXML+CSS yet.
I'd like to know if it is possible to set the size of indentation in Java? I cannot find any API for this purpose. In addition, is it possible to draw lines between parent node and its children like JTree in Swing ?
Thank you.
Regarding having lines like in JTree, there is no built in way to do that as of JavaFX 11. There is a feature request (JDK-8090579) but there doesn't seem to be any plans to implement it. You may be able to implement it yourself but I'm not sure how.
As to modifying the indent of the TreeCells, the easiest way is by using CSS.
As documented in the JavaFX CSS Reference Guide, TreeCell has a CSS property named -fx-indent whose value is a <size>. You can set this property by using a stylesheet or inline it via the style property. An example using inline styles:
public class TreeDataCell extends TreeCell<TreeData> {
public TreeDataCell() {
setStyle("-fx-indent: <size>;");
}
}
However, since you are currently not using CSS or FXML, there is another option that is purely code: Modifying the indent property of TreeCellSkin. This class became public API in JavaFX 9. There may be equivalent internal API in JavaFX 8 but I'm not sure.
By default, the Skin of a TreeCell will be an instance of TreeCellSkin. This means you can get this skin and set the indent value as needed. You have to be careful, though, as the skin is lazily created; it won't necessarily be available until the TreeView is actually part of a showing window.
If you only want to set the property once, one way is to intercept the skin inside createDefaultSkin():
public class TreeDataCell extends TreeCell<TreeData> {
#Override
protected Skin<?> createDefaultSkin() {
TreeCellSkin<?> skin = (TreeCellSkin<?>) super.createDefaultSkin();
skin.setIndent(/* your value */);
return skin;
}
}
You could also extend TreeCellSkin and customize it. Just remember to override createDefaultSkin() and return you custom skin implementation.
I'm relatively new to java and android programming, and i wanted to get coordinates based on a point that i tapped on HERE maps. I really hoped you guys could help me out with this.
Edit :
This is the code that i tried to implement, however, it returned me with the error : Error:(354, 53) error: cannot find symbol variable PointF :
private void addDestination() {
image.setImageResource(R.drawable.marker);
GeoCoordinate endpoint = map.pixelToGeo(PointF);
MapMarker destination = new MapMarker(endpoint,image);
if (destination != null)
{
map.removeMapObject(destination);
}
else
{
map.addMapObject(destination);
}
}
PointF is a datatype, so calling "map.pixelToGeo(PointF)" doesn't make sense, you need to call it with concrete data.
In a short:
You need to listen for click events (or longpress, or whatever event you wanna handle), and you get PointF data that's reflecting the screen coordinates. Then you can convert the screen coordinates via pixelToGeo into geocoordinates that you can use to add mapmarker or whatever you wanna do with it on the map.
Some code to help you getting started:
Listening to click events on the map (to retrieve the PointF screencoordinates) are done via registering the gestureListener to your mapview. Means, after successfull mapengine init, you do something like that:
yourMapViewInstance.getMapGesture().addOnGestureListener(yourGestureHandlerImpementation, 10, true);
and in your gestureHandler implementation, you can override several events (click, longpress, etc.), so for example you can do for longpress the following:
private MapGesture.OnGestureListener yourGestureHandlerImpementation = new MapGesture.OnGestureListener.OnGestureListenerAdapter()
{
#Override
public boolean onLongPressEvent(PointF p) {
GeoCoordinate c = map.pixelToGeo(p);
// c is your geoccordinate on the map, where you clicked on the screen
// [...]
}
}
I need an icon for my MenuItem's.
This is like a "worker class" to get the ImageView of the icon :
public class IconFactory {
private static ImageView HLP_BOOK_JFX;
public enum ICONS {
BASCET_REMOVE, BASCET_PUT, SAVE, OPEN, ARROW_RIGHT, ARROW_LEFT, ARROW_UP, ARROW_DOWN, CLOCK, ANALOG_SIGNAL, DIGITAL_SIGNAL, REFRESH, GREEN_PLUS, NETWORK, OK, CANCEL, RIGHT_NAV2, LEFT_NAV2, PLAY, PAUSE, LIST_ADD, PAGE_FIND, SET_PARAM, DOWNLOAD, UPLOAD, LOG_FILE, WARNING, INFO, LOG_DIAG, DATA_TRANS, TREE, FILTER, SEARCH, PARAM, ERASE, RESETDEF, RESETDEF2, DEBUG_BUG, INTERNATIONAL, CLOSE, HLP_BOOK
}
public static ImageView getImage(ICONS en) {
switch (en) {
case HLP_BOOK:
if (HLP_BOOK_JFX == null)
HLP_BOOK_JFX = new ImageView(new Image(IconFactory.class.getResourceAsStream("help_book.png")));
return HLP_BOOK_JFX;
}
return null;
}
When I use myMenuItem.setGraphic(IconFactory.getImage(ICONS.HLP_BOOK)) for a single menu item it works perfectly.
But then, when I want to generate two menus in a loop and set the same graphic, one MenuItem has no icon displayed. (the first one in loop in the code below).
My code:
while (keys.hasMoreElements()) {
// that will do 2 loops, do not care about how
MenuItem subMenuHelp = new MenuItem("MenuItem");
subMenuHelp.setGraphic(IconFactory.getImage(ICONS.HLP_BOOK));
subMenuHelp.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// do not care
openHelpFile(link);
}
});
System.out.println(((ImageView) subMenuHelp.getGraphic()).toString());
myMenu.getItems().add(subMenuHelp);
}
As you can see, I added a System.out.println to see if a graphic was set for the current item.
Result in console : both lines (MenuItem) with the same ImageView:
ImageView#79814766[styleClass=image-view]
ImageView#79814766[styleClass=image-view]
I did exactly the same in Swing (but with Icons and .setIcons() function) and it worked very well. I've also looked for a "repaint" function to force displaying but no way.
Hope you can help me!
This is because the same Node cannot be attached to the scene-graph multiple times and - as you even state - you are adding the same ImageView object.
From the documentation of Node:
If a program adds a child node to a Parent (including Group, Region,
etc) and that node is already a child of a different Parent or the
root of a Scene, the node is automatically (and silently) removed from
its former parent.
The solution is to modify getImage method of IconFactory to return a new ImageView instance on each call or to return Image instances rather than ImageView instances (the second one fits better to the name "IconFactory" I think).
You could store the Image instance instead of storing the ImageView to avoid re-loading the Image itself. You could check this question as reference: Reusing same ImageView multiple times in the same scene on JavaFX
A possible update on IconFactory:
public class IconFactory {
private static HashMap<ICON, Image> images = new HashMap<ICON, Image>();
public enum ICON {
BASCET_REMOVE, BASCET_PUT, SAVE, OPEN, ARROW_RIGHT, ARROW_LEFT, ARROW_UP, ARROW_DOWN, CLOCK, ANALOG_SIGNAL, DIGITAL_SIGNAL, REFRESH, GREEN_PLUS, NETWORK, OK, CANCEL, RIGHT_NAV2, LEFT_NAV2, PLAY, PAUSE, LIST_ADD, PAGE_FIND, SET_PARAM, DOWNLOAD, UPLOAD, LOG_FILE, WARNING, INFO, LOG_DIAG, DATA_TRANS, TREE, FILTER, SEARCH, PARAM, ERASE, RESETDEF, RESETDEF2, DEBUG_BUG, INTERNATIONAL, CLOSE, HLP_BOOK
}
public static Image getImage(ICON en) {
if (!images.containsKey(en)) {
switch (en) {
case HLP_BOOK:
images.put(en, new Image(IconFactory.class.getResourceAsStream("help_book.png"))); break;
default:
return null;
}
}
return images.get(en);
}
}
Usage after the update:
subMenuHelp.setGraphic(new ImageView(IconFactory.getImage(ICONS.HLP_BOOK)));
In the code below, two circles are drawn, and added to one grouping node.
The following behavior variants observed:
1) Able to drag by any point of circle, including exterior and interior; if drag by intercircle point, drag does not occur
2) Able to drag only by exterior
3) Unable to drag
4) Able to drag by any point in extended bounds
Any behavior is inited by subinitialize() params.
I wonder, is it possible to finetune active "pickeble" points of a node? For example, can I leave subnodes not pickable, but make the entire group is draggable by circles exteriors only, as it was in case (2)?
The need is required because Piccolo does not allow to determine, in which group object the click was made. Particularly, I can't determine the group node, to which listener were attached, so if I have single listener and attach it to multiple nodes, I won't be able to distinguish, which one was called.
public class Try_Picking_01 {
private static final Logger log = LoggerFactory.getLogger(Try_Picking_01.class);
public static void main(String[] args) {
new PFrame() {
final PNode group = new PNode();
#Override
public void initialize() {
group.addInputEventListener(new PBasicInputEventHandler() {
#Override
public void mouseDragged(PInputEvent event) {
PDimension dim = event.getDeltaRelativeTo(group.getParent());
group.offset(dim.getWidth(), dim.getHeight());
}
});
getCanvas().getLayer().addChild(group);
getCanvas().setPanEventHandler(null);
// able to drag by any point of circle, including exterior and interior
// if drag by intercircle point, drag does not occur
// subinitialize(false, true, false);
// able to drag only by exterior
//subinitialize(true, true, false);
// unable to drag
// subinitialize(true, false, false);
// able to drag by any point in extended bounds
subinitialize(true, false, true);
}
private void subinitialize(boolean emptyFill, boolean pickable, boolean expandBounds) {
PPath path1 = PPath.createEllipse(100, 100, 100, 100);
path1.setStrokePaint(Color.RED);
path1.setPaint( emptyFill ? null : Color.YELLOW ); // line #1
log.info("path1.bounds = {}", path1.getBounds());
path1.setPickable(pickable); // line #2
PPath path2 = PPath.createEllipse(200, 200, 100, 100);
path2.setPaint( emptyFill ? null : Color.YELLOW ); // line #3
log.info("path2.bounds = {}", path2.getBounds());
path2.setPickable(pickable); // line #4
group.addChild(path1);
group.addChild(path2);
log.info("group.bounds = {}", group.getBounds());
if( expandBounds ) {
group.setBounds(group.getFullBounds()); // line #5
log.info("group.bounds = {}", group.getBounds());
}
}
};
}
}
Suzan,
Looking at how Piccolo manages input events the most sensible thing would be to create a specific event handler for each of your node groups. The piccolo only provides you with a generic input handler at the PNode level. This renders all PNode events effectively the same. If you want to define different behavior per node (or group) you'll need to derive a class (for instance from PNode or PPath) and add the logic to detect which node group was clicked and drag according to the settings passed in subInitialize.
The great thing about Java is that you can easily extend libraries like Piccolo to suit your own purpose.