Wrong results when trying to get sizes of a pane with css-formattings - javafx-2

I came across a strange behaviour of JavaFX when i tried to obtain the bordersizes (bounds) of a pane that has some css-effects//formattings applied to it. In my application i have to lookup the exact sizes of different objects in order to connect them with lines (imagine some sort of UML-diagramm editor, the start and endpoints of the lines are the border coordinates of the objects).
Now to my problem: whenever i try to get the bordersizes of an object in the same method where this object is put on the scene graph, the result does not include any css attributes like padding, bordersize, strokes and so on. The exact result gets returned if the object already exists on the scene graph before i lookup the size. It seems to be that JavaFX has to wait for one rendering pass (16,7ms) to actually update the real bounds and sizes on an object. Is there any way to get the size of an object (especially those which extend Pane) in the same method as it is created? I don't really like the workaround with waiting for 16,7ms, because it creates some unwanted behaviour in my application.
The following code shows the problem. The size when creating the pane containing the rectangle does not equal the size when pressing the "show size" button.
public class SzenarioView extends GridPane
{
private Group paintingLayer;
public SzenarioView()
{
super();
paintingLayer = new Group();
paintingLayer.getStylesheets().add(TestStarter.class.getResource("ReprBox.css").toString());
Rectangle r1 = new Rectangle(0, 0, 1000, 1000);
r1.setFill(Color.AZURE);
paintingLayer.getChildren().add(r1);
Button b1 = new Button("Show Size");
b1.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
for(Node n : paintingLayer.getChildren())
{
System.out.println("Border...");
System.out.println(getNodeBorderCoords(n, BorderTypes.RIGHT)[0]);
System.out.println(getNodeBorderCoords(n, BorderTypes.RIGHT)[1]);
System.out.println("End Border");
}
}
});
Button b2 = new Button("Add CCSBTN");
b2.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
BorderPane bp = new BorderPane();
bp.getStylesheets().add(TestStarter.class.getResource("ReprBox.css").toString());
Rectangle rcss = new Rectangle(50, 50);
bp.setTop(rcss);
bp.getStyleClass().add("my-box");
setObjectOnScreen(bp, 100, 100);
System.out.println(getNodeBorderCoords(bp, BorderTypes.RIGHT)[0]);
System.out.println(getNodeBorderCoords(bp, BorderTypes.RIGHT)[1]);
}
});
this.add(b1, 0, 0);
this.add(b2, 1, 0);
this.add(paintingLayer, 1, 1);
this.setMaxHeight(500);
this.setMaxWidth(700);
this.setHgap(10);
this.setVgap(10);
this.setPadding(new Insets(10, 10, 10, 10));
}
public void setObjectOnScreen(Node obj, double toX, double toY)
{
obj.setLayoutX(toX);
obj.setLayoutY(toY);
paintingLayer.getChildren().add(obj);
}
public double[] getNodeBorderCoords(Node n, BorderTypes type)
{
double x = 0;
double y = 0;
double bx = n.getBoundsInLocal().getWidth();
double by = n.getBoundsInLocal().getHeight();
switch (type)
{
case LEFT:
x = n.getLayoutX();
y = n.getLayoutY() + by / 2;
break;
case RIGHT:
x = n.getLayoutX() + bx ;
y = n.getLayoutY() + by / 2;
break;
case TOP:
x = n.getLayoutX() + bx / 2;
y = n.getLayoutY();
break;
case BOTTOM:
x = n.getLayoutX() + bx / 2;
y = n.getLayoutY() + by;
break;
}
double[] ret =
{ x, y, };
return ret;
}
}
The CSS-File
#CHARSET "ISO-8859-1";
.my-box {
-fx-border-color: rgb(255, 0, 0);
-fx-border-radius: 2;
-fx-padding: 1 1 1 1;
-fx-border-width: 5 5 5 5;
}
By the way, it doesn't matter if use getBoundsInLocal() or getBoundsInParent() .
UPDATE
Here are two workarounds that can be used:
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
Platform.runLater(new Runnable() {
#Override
public void run() {
System.out.println(getNodeBorderCoords(bp, BorderTypes.RIGHT)[0]);
System.out.println(getNodeBorderCoords(bp, BorderTypes.RIGHT)[1]);
}
});
}
});
t.start();
But delaying the call might cause some strange behaviour like i stated in my post. But i found another "solution" recently.
bp.snapshot(new SnapshotParameters(), new WritableImage(5, 5));
System.out.println(getNodeBorderCoords(bp, BorderTypes.RIGHT)[0]);
System.out.println(getNodeBorderCoords(bp, BorderTypes.RIGHT)[1]);
The snapshot() method applies all css effects (and all other layout work) on the node. After that, the returned values of the borders are correct.

There are 2 approaches:
You can use binding instead of static size call. It gives benefits of additional support for update after resize but adds a bit of listeners handling burden on FX enging.
Wrapping size reading logic into Platform.runLater() will put it later into event queue and should address premature size access issue.

Related

use Teapot instead of CubeObject in object recognition

I'm trying to use Teapot instead of the cube object in the object recognition sample app, but the Teapot is placed too far away from my target.
I will really appreciate any help.
Here is my ObjectTargetRenderer:
public class ObjectTargetRenderer implements GLSurfaceView.Renderer, SampleAppRendererControl
{
private static final String LOGTAG = "ObjectTargetRenderer";
private SampleApplicationSession vuforiaAppSession;
private ObjectTargets mActivity;
private SampleAppRenderer mSampleAppRenderer;
private Vector<Texture> mTextures;
private int shaderProgramID;
private int vertexHandle;
private int textureCoordHandle;
private int texSampler2DHandle;
private int mvpMatrixHandle;
private int opacityHandle;
private int colorHandle;
private CubeObject mCubeObject;
private Teapot mTeapot;
private Renderer mRenderer;
private boolean mIsActive = false;
public ObjectTargetRenderer(ObjectTargets activity,
SampleApplicationSession session)
{
mActivity = activity;
vuforiaAppSession = session;
// SampleAppRenderer used to encapsulate the use of RenderingPrimitives setting
// the device mode AR/VR and stereo mode
mSampleAppRenderer = new SampleAppRenderer(this, mActivity, Device.MODE.MODE_AR, false, 10f, 5000f);
}
// Called to draw the current frame.
#Override
public void onDrawFrame(GL10 gl)
{
if (!mIsActive)
return;
// Call our function to render content from SampleAppRenderer class
mSampleAppRenderer.render();
}
// Called when the surface is created or recreated.
#Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
Log.d(LOGTAG, "GLRenderer.onSurfaceCreated");
// Call Vuforia function to (re)initialize rendering after first use
// or after OpenGL ES context was lost (e.g. after onPause/onResume):
vuforiaAppSession.onSurfaceCreated();
mSampleAppRenderer.onSurfaceCreated();
}
// Called when the surface changed size.
#Override
public void onSurfaceChanged(GL10 gl, int width, int height)
{
Log.d(LOGTAG, "GLRenderer.onSurfaceChanged");
// Call Vuforia function to handle render surface size changes:
vuforiaAppSession.onSurfaceChanged(width, height);
// RenderingPrimitives to be updated when some rendering change is done
mSampleAppRenderer.onConfigurationChanged(mIsActive);
// Init rendering
initRendering();
}
public void setActive(boolean active)
{
mIsActive = active;
if(mIsActive)
mSampleAppRenderer.configureVideoBackground();
}
// Function for initializing the renderer.
private void initRendering()
{
// mCubeObject = new CubeObject();
mTeapot = new Teapot();
mRenderer = Renderer.getInstance();
// Now generate the OpenGL texture objects and add settings
for (Texture t : mTextures)
{
GLES20.glGenTextures(1, t.mTextureID, 0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, t.mTextureID[0]);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA,
t.mWidth, t.mHeight, 0, GLES20.GL_RGBA,
GLES20.GL_UNSIGNED_BYTE, t.mData);
}
SampleUtils.checkGLError("ObjectTarget GLInitRendering");
GLES20.glClearColor(0.0f, 0.0f, 0.0f, Vuforia.requiresAlpha() ? 0.0f
: 1.0f);
shaderProgramID = SampleUtils.createProgramFromShaderSrc(
CubeShaders.CUBE_MESH_VERTEX_SHADER,
CubeShaders.CUBE_MESH_FRAGMENT_SHADER);
vertexHandle = GLES20.glGetAttribLocation(shaderProgramID,
"vertexPosition");
textureCoordHandle = GLES20.glGetAttribLocation(shaderProgramID,
"vertexTexCoord");
texSampler2DHandle = GLES20.glGetUniformLocation(shaderProgramID,
"texSampler2D");
mvpMatrixHandle = GLES20.glGetUniformLocation(shaderProgramID,
"modelViewProjectionMatrix");
opacityHandle = GLES20.glGetUniformLocation(shaderProgramID,
"opacity");
colorHandle = GLES20.glGetUniformLocation(shaderProgramID, "color");
// Hide the Loading Dialog
mActivity.loadingDialogHandler
.sendEmptyMessage(LoadingDialogHandler.HIDE_LOADING_DIALOG);
}
// The render function called from SampleAppRendering by using RenderingPrimitives views.
// The state is owned by SampleAppRenderer which is controlling it's lifecycle.
// State should not be cached outside this method.
public void renderFrame(State state, float[] projectionMatrix)
{
// Renders video background replacing Renderer.DrawVideoBackground()
mSampleAppRenderer.renderVideoBackground();
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
// did we find any trackables this frame?
for (int tIdx = 0; tIdx < state.getNumTrackableResults(); tIdx++)
{
TrackableResult result = state.getTrackableResult(tIdx);
Trackable trackable = result.getTrackable();
printUserData(trackable);
if (!result.isOfType(ObjectTargetResult.getClassType()))
continue;
ObjectTarget objectTarget = (ObjectTarget) trackable;
Matrix44F modelViewMatrix_Vuforia = Tool
.convertPose2GLMatrix(result.getPose());
float[] modelViewMatrix = modelViewMatrix_Vuforia.getData();
// deal with the modelview and projection matrices
float[] modelViewProjection = new float[16];
float[] objectSize = objectTarget.getSize().getData();
Matrix.translateM(modelViewMatrix, 0, objectSize[0]/2, objectSize[1]/2,
objectSize[2]/2);
Matrix.scaleM(modelViewMatrix, 0, objectSize[0]/2,
objectSize[1]/2, objectSize[2]/2);
Matrix.multiplyMM(modelViewProjection, 0, projectionMatrix, 0, modelViewMatrix, 0);
// activatrigidBodyTarget.xmle the shader program and bind the vertex/normal/tex coords
GLES20.glUseProgram(shaderProgramID);
GLES20.glVertexAttribPointer(vertexHandle, 3, GLES20.GL_FLOAT,
false, 0, mTeapot.getVertices());
GLES20.glUniform1f(opacityHandle, 0.3f);
GLES20.glUniform3f(colorHandle, 0.0f, 0.0f, 0.0f);
GLES20.glVertexAttribPointer(textureCoordHandle, 2,
GLES20.GL_FLOAT, false, 0, mTeapot.getTexCoords());
GLES20.glEnableVertexAttribArray(vertexHandle);
GLES20.glEnableVertexAttribArray(textureCoordHandle);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,
mTextures.get(0).mTextureID[0]);
GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false,
modelViewProjection, 0);
GLES20.glUniform1i(texSampler2DHandle, 0);
// pass the model view matrix to the shader
GLES20.glUniformMatrix4fv(mvpMatrixHandle, 1, false,
modelViewProjection, 0);
// finally render
GLES20.glDrawElements(GLES20.GL_TRIANGLES,
mTeapot.getNumObjectIndex(), GLES20.GL_UNSIGNED_SHORT,
mTeapot.getIndices());
// disable the enabled arrays
GLES20.glDisableVertexAttribArray(vertexHandle);
GLES20.glDisableVertexAttribArray(textureCoordHandle);
SampleUtils.checkGLError("Render Frame");
}
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glDisable(GLES20.GL_BLEND);
mRenderer.end();
}
private void printUserData(Trackable trackable)
{
String userData = (String) trackable.getUserData();
Log.d(LOGTAG, "UserData:Retreived User Data \"" + userData + "\"");
}
public void setTextures(Vector<Texture> textures)
{
mTextures = textures;
}
}
If the object is far, it could be that the Z translation or scaling is wrong. Check if your objectSize[2] is correct. Anyway, you can control the distance with Matrix.translateM (the last argument is Z)

How do I create an editable Label in javafx 2.2

I am looking to create an editable label at an arbitrary position on the pane on which I am writing. I am under the impression that TextField or TextArea objects are what I could use to implement that capability. There is obviously more to it as I don't know how to position the object when I create it. I have found an example on the "Chaotic Java" website but I need to do a bit more work to understand what's going on there. http://chaoticjava.com/posts/another-javafx-example-the-editable-label/
I am looking for more input from this group.
(There are no errors because I have not written any code.)
I was kind of curious about how to achieve this, so I gave it a try. This is what I came up with.
The approach used is pretty the same as that suggested by James in his comment:
I would start with a Pane, . . ., TextFields to represent text while being edited. Register mouse listeners with the Pane and Text objects, and use the layoutX and layoutY properties to position things . . . just to use text fields, and to use CSS to make them look like labels when not focused and text fields when focused.
The only significantly tricky part was working out how to correctly size the text fields as the Text inside the text field is not exposed via public API to allow you to listen to it's layout bounds. You could perhaps use a css lookup function to get at the enclosed Text, but I chose to use a private sun FontMetrics API (which may be deprecated in the future), to get the size of the text. In the future with Java 9, you should be able to perform the task without using the private API.
The solution doesn't try to do anything tricky like deal with multi-format or multi-line text, it is just for short, single line comments of a few words that can be placed over a scene.
TextCreator.java
// ## CAUTION: beware the com.sun imports...
import com.sun.javafx.tk.FontMetrics;
import com.sun.javafx.tk.Toolkit;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
* Displays a map of the lonely mountain upon which draggable, editable labels can be overlaid.
*/
public class TextCreator extends Application {
private static final String MAP_IMAGE_LOC =
"http://images.wikia.com/lotr/images/archive/f/f6/20130209175313!F27c_thorins_map_from_the_hobbit.jpg";
public static void main(String[] args) throws Exception {
launch(args);
}
#Override
public void start(final Stage stage) throws Exception {
Pane pane = new Pane();
pane.setOnMouseClicked(event -> {
if (event.getTarget() == pane) {
pane.getChildren().add(
new EditableDraggableText(event.getX(), event.getY())
);
}
});
EditableDraggableText cssStyled =
new EditableDraggableText(439, 253, "Style them with CSS");
cssStyled.getStyleClass().add("highlighted");
pane.getChildren().addAll(
new EditableDraggableText(330, 101, "Click to add a label"),
new EditableDraggableText(318, 225, "You can edit your labels"),
cssStyled,
new EditableDraggableText(336, 307, "And drag them"),
new EditableDraggableText(309, 346, "Around The Lonely Mountain")
);
StackPane layout = new StackPane(
new ImageView(
new Image(
MAP_IMAGE_LOC
)
),
pane
);
Scene scene = new Scene(layout);
scene.getStylesheets().add(getClass().getResource(
"editable-text.css"
).toExternalForm());
stage.setScene(scene);
stage.setResizable(false);
stage.show();
}
/**
* A text field which has no special decorations like background, border or focus ring.
* i.e. the EditableText just looks like a vanilla Text node or a Label node.
*/
class EditableText extends TextField {
// The right margin allows a little bit of space
// to the right of the text for the editor caret.
private final double RIGHT_MARGIN = 5;
EditableText(double x, double y) {
relocate(x, y);
getStyleClass().add("editable-text");
//** CAUTION: this uses a non-public API (FontMetrics) to calculate the field size
// the non-public API may be removed in a future JavaFX version.
// see: https://bugs.openjdk.java.net/browse/JDK-8090775
// Need font/text measurement API
FontMetrics metrics = Toolkit.getToolkit().getFontLoader().getFontMetrics(getFont());
setPrefWidth(RIGHT_MARGIN);
textProperty().addListener((observable, oldTextString, newTextString) ->
setPrefWidth(metrics.computeStringWidth(newTextString) + RIGHT_MARGIN)
);
Platform.runLater(this::requestFocus);
}
}
/**
* An EditableText (a text field which looks like a label), which can be dragged around
* the screen to reposition it.
*/
class EditableDraggableText extends StackPane {
private final double PADDING = 5;
private EditableText text = new EditableText(PADDING, PADDING);
EditableDraggableText(double x, double y) {
relocate(x - PADDING, y - PADDING);
getChildren().add(text);
getStyleClass().add("editable-draggable-text");
// if the text is empty when we lose focus,
// the node has no purpose anymore
// just remove it from the scene.
text.focusedProperty().addListener((observable, hadFocus, hasFocus) -> {
if (!hasFocus && getParent() != null && getParent() instanceof Pane &&
(text.getText() == null || text.getText().trim().isEmpty())) {
((Pane) getParent()).getChildren().remove(this);
}
});
enableDrag();
}
public EditableDraggableText(int x, int y, String text) {
this(x, y);
this.text.setText(text);
}
// make a node movable by dragging it around with the mouse.
private void enableDrag() {
final Delta dragDelta = new Delta();
setOnMousePressed(mouseEvent -> {
this.toFront();
// record a delta distance for the drag and drop operation.
dragDelta.x = mouseEvent.getX();
dragDelta.y = mouseEvent.getY();
getScene().setCursor(Cursor.MOVE);
});
setOnMouseReleased(mouseEvent -> getScene().setCursor(Cursor.HAND));
setOnMouseDragged(mouseEvent -> {
double newX = getLayoutX() + mouseEvent.getX() - dragDelta.x;
if (newX > 0 && newX < getScene().getWidth()) {
setLayoutX(newX);
}
double newY = getLayoutY() + mouseEvent.getY() - dragDelta.y;
if (newY > 0 && newY < getScene().getHeight()) {
setLayoutY(newY);
}
});
setOnMouseEntered(mouseEvent -> {
if (!mouseEvent.isPrimaryButtonDown()) {
getScene().setCursor(Cursor.HAND);
}
});
setOnMouseExited(mouseEvent -> {
if (!mouseEvent.isPrimaryButtonDown()) {
getScene().setCursor(Cursor.DEFAULT);
}
});
}
// records relative x and y co-ordinates.
private class Delta {
double x, y;
}
}
}
editable-text.css
.editable-text {
-fx-background-color: transparent;
-fx-background-insets: 0;
-fx-background-radius: 0;
-fx-padding: 0;
}
.editable-draggable-text:hover .editable-text {
-fx-background-color: yellow;
}
.editable-draggable-text {
-fx-padding: 5;
-fx-background-color: rgba(152, 251, 152, 0.2); // translucent palegreen
}
.editable-draggable-text:hover {
-fx-background-color: orange;
}
.highlighted {
-fx-background-color: rgba(255, 182, 93, 0.3); // translucent mistyrose
-fx-border-style: dashed;
-fx-border-color: firebrick;
}
If you have time, you could clean the sample implementation up and donate it to the ControlsFX project.
You can use a function of label: setGraphic().
Here is my code:
public void editableLabelTest(Stage stage){
Scene scene = new Scene(new VBox(new EditableLabel("I am a label"),
new EditableLabel("I am a label too")));
stage.setScene(scene);
stage.show();
}
class EditableLabel extends Label{
TextField tf = new TextField();
/***
* backup is used to cancel when press ESC...
*/
String backup = "";
public EditableLabel(){
this("");
}
public EditableLabel(String str){
super(str);
this.setOnMouseClicked(e -> {
if(e.getClickCount() == 2){
tf.setText(backup = this.getText());
this.setGraphic(tf);
this.setText("");
tf.requestFocus();
}
});
tf.focusedProperty().addListener((prop, o, n) -> {
if(!n){
toLabel();
}
});
tf.setOnKeyReleased(e -> {
if(e.getCode().equals(KeyCode.ENTER)){
toLabel();
}else if(e.getCode().equals(KeyCode.ESCAPE)){
tf.setText(backup);
toLabel();
}
});
}
void toLabel(){
this.setGraphic(null);
this.setText(tf.getText());
}
}

try/catch does nothing in Java ME

So I have the following code:
public class Minesweeper extends MIDlet implements CommandListener {
public static String error = "";
public void startApp() throws MIDletStateChangeException {
Display display = Display.getDisplay(this);
canvas = new MCanvas();
canvas.addCommand(exitCommand);
canvas.addCommand(okCommand);
canvas.addCommand(newCommand);
canvas.setCommandListener(this);
try{
display.setCurrent(canvas);
} catch (Exception e) {
error = e.toString();
}
}
}
When I leave display.setCurrent(canvas); outside of the try block, the app fails with a NullPointerException. When I comment out that line, the app works (although obviously no canvas is added). So the error is caused by that line, or something that that line causes.
So I suround that line with try/catch. Although the error is caused by that line, the error still happens when the line is surrounded by try/catch. How can I catch the error? (I've tried this using Throwable as well, and it is still not caught.
MCanvas:
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
public class MCanvas extends Canvas {
protected void paint(Graphics g){
//Minesweeper.p("repaint");
Space[] data = Minesweeper.topaint;
for(int x=0; x<data.length; x++){
data[x].print();
int r = data[x].row * 10;
int c = data[x].col * 10;
int v = data[x].value;
String s = "";
//System.out.println("r:"+Integer.toString(r)+" c:"+Integer.toString(c)+" s:"+Integer.toString(v));
g.setColor(250, 0, 0);
//Minesweeper.p("if");
if(data[x].open){
switch(v){
case 0:
g.setColor(50, 50, 50);
break;
case 1:
g.setColor(100, 50, 50);
s = "1";
break;
case 2:
g.setColor(150, 50, 50);
s = "2";
break;
case 3:
g.setColor(200, 50, 50);
s = "3";
break;
case 4:
g.setColor(250, 50, 50);
s = "4";
break;
case 5:
g.setColor(250, 100, 100);
s = "5";
break;
case 6:
g.setColor(250, 125, 125);
s = "6";
break;
case 7:
g.setColor(250, 150, 150);
s = "7";
break;
case 8:
g.setColor(250, 175, 175);
s = "8";
break;
case 9:
g.setColor(250, 200, 200);
break;
default:
g.setColor(250, 100, 100);
}
} else {
g.setColor(0,0,0);
}
g.fillRect(c, r, 10, 10);
g.setColor(250, 250, 250);
Font font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);
g.setFont(font);
g.drawString(s, c+5, r+8, Graphics.HCENTER | Graphics.BASELINE);
if(data[x].hover){
g.setColor(250, 250, 250);
g.drawLine(c, r, c, r+9);
g.drawLine(c, r, c+9, r);
g.drawLine(c+9, r, c+9, r+9);
g.drawLine(c, r+9, c+9, r+9);
}
//Minesweeper.p("here?");
}
//Minesweeper.p("here");
//Minesweeper.p(Minesweeper.error);
if(Minesweeper.error != null){
g.drawString(Minesweeper.error, 10, 10, Graphics.HCENTER | Graphics.BASELINE);
}
Minesweeper.p("msg:"+Minesweeper.message);
g.setColor(0, 0, 0);
Font font = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_LARGE);
g.setFont(font);
g.drawString(Minesweeper.message, this.getWidth()/2, this.getHeight()-10, Graphics.HCENTER | Graphics.BASELINE);
Font fontsm = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_SMALL);
g.setFont(fontsm);
}
protected void keyPressed(int keyCode) {
int gameaction = getGameAction(keyCode);
int c = Minesweeper.selected.col;
int r = Minesweeper.selected.row;
switch (gameaction) {
case UP:
Minesweeper.p("UP");
if(r>0){
Minesweeper.selected.leavehere();
Minesweeper.getSpace(Minesweeper.selected.row - 1, Minesweeper.selected.col).gohere();
}
break;
case DOWN:
Minesweeper.p("DOWN");
if(r<Minesweeper.height-1){
Minesweeper.selected.leavehere();
Minesweeper.getSpace(Minesweeper.selected.row + 1, Minesweeper.selected.col).gohere();
}
break;
case LEFT:
Minesweeper.p("LEFT");
if(c>0){
Minesweeper.selected.leavehere();
Minesweeper.getSpace(Minesweeper.selected.row, Minesweeper.selected.col - 1).gohere();
}
break;
case RIGHT:
Minesweeper.p("RIGHT");
if(c<Minesweeper.length-1){
Minesweeper.selected.leavehere();
Minesweeper.getSpace(Minesweeper.selected.row, Minesweeper.selected.col + 1).gohere();
}
break;
}
repaint();
}
}
As explained in Display.setCurrent API javadocs,
...The setCurrent() method returns immediately, without waiting for the change to take place...
Because of above, exceptions that may occur in calls triggered by setCurrent may (and most likely will) slip through your try-catch.
To be able to catch and report such exceptions, one should study what calls are triggered by setCurrent (in your case, these are explained in API javadocs for Canvas, Event Delivery section), cover these by try-catch blocks where appropriate and design the appropriate way to report exceptions if these occur.
In your case, try-catch could likely surround code in MCanvas.paint (this is where NPE likely occurs) and exceptions could be reported for example by showing appropriate screen with error message (eg Alert) by invoking setCurrent for that screen from catch block.
If I were you I would continue to insert try/catch blocks in the MCanvas class.
It is difficult for anyone to figure out where your NullPointerException occurs without seeing more of the code.
The only question I can come up with, from the code you pasted so far, is:
Does your Minesweeper class contain a static array of object Space called topaint? Did you declare it but maybe forgot to fill it with data?
Space[] topaint = new Space[20]; // Declared, but nothing in it yet.
Trying to access Minesweeper.topaint[0] will give a NullPointerException, unless you also do
topaint[0] = new Space();

get layout height and width at run time android

How can I get width and height of a linear layout which is defined in xml as fill_parent both in height and width? I have tried onmeasure method but I dont know why it is not giving exact value. I need these values in an Activity before oncreate method finishes.
Suppose I have to get a LinearLayout width defined in XML. I have to get reference of it by XML. Define LinearLayout l as instance.
l = (LinearLayout)findviewbyid(R.id.l1);
ViewTreeObserver observer = l.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
init();
l.getViewTreeObserver().removeGlobalOnLayoutListener(
this);
}
});
protected void init() {
int a= l.getHeight();
int b = l.getWidth();
Toast.makeText(getActivity,""+a+" "+b,3000).show();
}
callfragment();
}
The width and height values are set after the layout has been created, when elements have been placed they then get measured. On the first call to onSizeChanged the parms will be 0 so if you use that check for it.
Little more detail here
https://groups.google.com/forum/?fromgroups=#!topic/android-developers/nNEp6xBnPiw
and here http://developer.android.com/reference/android/view/View.html#Layout
Here is how to use onLayout:
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int width = someView.getWidth();
int height = someView.getHeight();
}
To get it work, you need to check whether the desired height value is bigger than 0 - and first then remove the onGlobalLayout listener and do whatever you want with the height. The listener calls its method continuously and by the first call it is not guaranteed that the view is measured properly.
final LinearLayout parent = (LinearLayout) findViewById(R.id.parentView);
parent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int availableHeight = parent.getMeasuredHeight();
if(availableHeight>0) {
parent.getViewTreeObserver().removeGlobalOnLayoutListener(this);
//save height here and do whatever you want with it
}
}
});
You could add on layout change listener to your layout and get the newest height and width or even the one before last change.
Added in API level 11
Add a listener that will be called when the bounds of the view change
due to layout processing.
LinearLayout myLinearLayout = (LinearLayout) findViewById(R.id.my_linear_layout);
myLinearLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
// Preventing extra work because method will be called many times.
if(height == (bottom - top))
return;
height = (bottom - top);
// do something here...
}
});
A generic approach using Kotlin based on MGDroid's answer for API 16+.
/**
* Align height of a container from wrap-content to actual height at runtime.
* */
private fun <T: ViewGroup> alignContainerHeight(container: T) {
container.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
// Obtain runtime height
val availableHeight = container.measuredHeight
if (availableHeight > 0) {
container.viewTreeObserver.removeOnGlobalLayoutListener(this)
setContainerHeight(container, availableHeight)
}
}
})
}
/**
* Note: Assumes that the parent is a LinearLayout.
* */
private fun <T : ViewGroup> setContainerHeight(container: T, availableHeight: Int) {
val availableWidth = container.measuredWidth
val params = LinearLayout.LayoutParams(availableWidth, availableHeight)
// Note: getLayoutParams() returns null if no parent exists
if (container.layoutParams != null) {
container.layoutParams = params
}
}

update pathgraphic only

I use the following code to draw line as following
private void Form1_Paint(object sender, PaintEventArgs e)
{
base.OnPaint(e);
path = new GraphicsPath(new Point[]{ new Point(10, 10),
new Point(100, 100) } ,
new byte[] {(byte)PathPointType.Start,
(byte)PathPointType.Line });
e.Graphics.DrawPath(Pens.Red, path);
}
I need when click a button to change the path and redraw it only so I use
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < path.PointCount; i++)
{
path.PathPoints[i].X += 100;
path.PathPoints[i].Y += 100;
}
Invalidate();
//path.
}
the problem no effect happened, and I don't want to refresh the container I draw on it
First, it will not give you any effect since you always create a new path in OnPaint. As a result you have the same picture. In order to change it you need at least move your path creation to the constructor or another initialization method
Second, PathPoint is an array of PointF, PointF is a structure, therefore it is immutable and you will not get a new point in array by doing so - PathPoints[i].X += 10

Resources