Problem
Using RelativeLayout.LayoutParams and marginTop before setLayoutParams( params ).
Works on all devices for half a second but some cases it bumps back to top.
The view stays centered in the holding view on my Moto X 2014 running Lollipop but not on my Nexus 4 running Lollipop.
Opening activity
Opens activity
The margin is fine and the SurfaceView is centered
~200ms delay
The margin resets and its back to top (top of SurfaceView at the top of holder)
Closing activity
Back pressed
~200ms delay
The margin sets in, putting my view to the right position
Activity closes
Code (Edited)
RelativeLayout holder = ( RelativeLayout ) findViewById( R.id.holder );
RelativeLayout.LayoutParams params = ( RelativeLayout.LayoutParams ) holder.getLayoutParams();
CustomCamera view = ( CustomCamera ) findViewById( R.id.surface ); // Extends SurfaceView
params.topMargin = margin;
view.setLayoutParams( params );
Example
I need the margin to work like this every time on every device.
On some devices the red (SurfaceView) is aligned with top of screen ignoring the margin and gravity.
To make your life easier, here is my simple camera implementation which can help you out. Note that this implementation relays on the old android.hardware.Camera API. Starting API level 21 there is a new way of working with the camera.
Anyway here is your basic xml file for your activity:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<FrameLayout
android:id="#+id/root_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SurfaceView
android:id="#+id/camera_surfaceview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<Button
android:id="#+id/take_picture_button"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="16dp"
android:text="Take picture" />
</RelativeLayout>
Your camera activity:
package com.your.package;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.hardware.Camera.Size;
import android.media.AudioManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;
import java.io.IOException;
/**
* #author Mike Reman
*
*/
public class MainActivity extends Activity {
// Constants
private static final float PREVIEW_SIZE_FACTOR = 1.3f;
private static final String STATE_SELECTED_CHECKBOX = "CameraType";
private Camera mCamera;
private SurfaceHolder mSurfaceHolder = null;
// Data
//private boolean mHasTwoCameras = (Camera.getNumberOfCameras() > 1);
private boolean mIsInPreview;
private boolean mIsUsingFFC = false;
private boolean mIsLandscape;
protected AutoFocusCallback autoFocusCallback = new AutoFocusCallback() {
#Override
public void onAutoFocus(boolean success, Camera camera) {
try {
camera.takePicture(shutterCallback, null, jpegCallback);
} catch (RuntimeException e) {
e.printStackTrace();
}
}
};
private ShutterCallback shutterCallback = new ShutterCallback() {
#Override
public void onShutter() {
AudioManager mgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
mgr.playSoundEffect(AudioManager.FLAG_PLAY_SOUND);
}
};
private PictureCallback jpegCallback = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
//Tadaaa, you got your picture taken
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFormat(PixelFormat.TRANSLUCENT);
mIsLandscape = getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
if (savedInstanceState != null) {
mIsUsingFFC = savedInstanceState.getBoolean(STATE_SELECTED_CHECKBOX, false);
}
setContentView(R.layout.activity_main);
initializeCameraAndViews();
}
#Override
public void onSaveInstanceState(#NonNull Bundle outState) {
outState.putBoolean(STATE_SELECTED_CHECKBOX, mIsUsingFFC);
}
/**
* Initialize the views used by the activity, including the SurfaceView
* displaying the 'camera'. There is a SurfaceHolder object which is
* initialized by the SurfaceView's SurfaceHolder
*/
private void initializeCameraAndViews() {
FrameLayout frame = (FrameLayout) findViewById(R.id.root_container);
SurfaceView surfaceView = (SurfaceView) frame.findViewById(R.id.camera_surfaceview);
if (mSurfaceHolder == null) {
mSurfaceHolder = surfaceView.getHolder();
}
mSurfaceHolder.addCallback(surfaceHolderCallback(mIsUsingFFC));
findViewById(R.id.take_picture_button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
mCamera.autoFocus(autoFocusCallback);
} catch (RuntimeException e) {
try {
mCamera.takePicture(shutterCallback, null, jpegCallback);
} catch (RuntimeException ex) {
// Failed to take the picture
ex.printStackTrace();
}
}
}
});
}
private SurfaceHolder.Callback surfaceHolderCallback(final boolean isUsingFFC) {
return new SurfaceHolder.Callback() {
private AsyncTask<Void, Void, Void> initCameraAsyncTask;
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (isUsingFFC) {
try {
mCamera = Camera.open(CameraInfo.CAMERA_FACING_FRONT);
CameraUtils.setCameraDisplayOrientation(MainActivity.this, CameraInfo.CAMERA_FACING_FRONT, mCamera);
} catch (RuntimeException e) {
// Open camera failed
}
} else {
try {
mCamera = Camera.open(CameraInfo.CAMERA_FACING_BACK);
CameraUtils.setCameraDisplayOrientation(MainActivity.this, CameraInfo.CAMERA_FACING_BACK, mCamera);
} catch (RuntimeException e) {
// Open camera failed
}
}
try {
if (mCamera != null) {
mCamera.setPreviewDisplay(holder);
} else {
// Most probably the device has no camera...yeah it is possible :)
}
} catch (IOException exception) {
mCamera.release();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, final int width, final int height) {
initCameraAsyncTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
if (mCamera != null) {
try {
Camera.Parameters parameters = mCamera.getParameters();
Size size = getOptimalSize(mCamera);
parameters.setPreviewSize(size.width, size.height);
mCamera.setParameters(parameters);
} catch (RuntimeException e) {
e.printStackTrace();
}
mCamera.startPreview();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
mIsInPreview = true;
// Set the initial FlashMode to OFF
if (mCamera != null) {
Camera.Parameters parameters = mCamera.getParameters();
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
mCamera.setParameters(parameters);
}
}
};
initCameraAsyncTask.execute();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
mCamera.stopPreview();
mIsInPreview = false;
initCameraAsyncTask.cancel(true);
mCamera.release();
}
}
};
}
/**
* Open the camera by creating a new instance of the Camera object depending
* on the given parameter. Use this method if you want to switch between Front facing camera(FFC) and back facing cameras
* <p>
* If the parameter value is true, a new Camera object will be created using
* the Front Facing Camera. Otherwise the newly created Camera object will
* use the Back Facing Camera
* </p>
*
* #param isWithFFC
* - the parameter to be the deciding factor on which camera is
* used
*/
private void openCamera(boolean isWithFFC) {
if (mIsInPreview) {
mCamera.stopPreview();
mIsInPreview = false;
}
mCamera.release();
int currentCameraId;
if (isWithFFC) {
currentCameraId = CameraInfo.CAMERA_FACING_FRONT;
} else {
currentCameraId = CameraInfo.CAMERA_FACING_BACK;
}
mCamera = Camera.open(currentCameraId);
CameraUtils.setCameraDisplayOrientation(MainActivity.this, currentCameraId, mCamera);
try {
mCamera.setPreviewDisplay(mSurfaceHolder);
} catch (IOException e) {
e.printStackTrace();
}
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
try {
Camera.Parameters parameters = mCamera.getParameters();
Size size = getOptimalSize(mCamera);
parameters.setPreviewSize(size.width, size.height);
mCamera.setParameters(parameters);
} catch (RuntimeException e) {
e.printStackTrace();
}
mCamera.startPreview();
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
mIsInPreview = true;
}
}.execute();
}
/**
* Gets an optimal size for the created Camera.
*
* #param camera
* - the built Camera object
* #return the optimal Size for the Camera object
*/
private Size getOptimalSize(Camera camera) {
Size result = null;
final Camera.Parameters parameters = camera.getParameters();
int width = ScreenUtils.getScreenWidth(this);
int height = ScreenUtils.getScreenHeight(this);
for (final Size size : parameters.getSupportedPreviewSizes()) {
if (size.width <= width * PREVIEW_SIZE_FACTOR && size.height <= height * PREVIEW_SIZE_FACTOR) {
if (mIsLandscape) {
size.width = width;
size.height = height;
} else {
size.height = width; // switching the values because the default camera is basically always in landscape mode and our camera isn't.
size.width = height;
}
if (result == null) {
result = size;
} else {
final int resultArea = result.width * result.height;
final int newArea = size.width * size.height;
if (newArea > resultArea) {
result = size;
}
}
}
}
if (result == null) {
result = parameters.getSupportedPreviewSizes().get(0);
}
return result;
}
}
Your needed utils classes. You can add these to your own CameraActivity if you won't use them elsewhere:
CameraUtils:
public final class CameraUtils {
/**
* Sets the orientation for the Camera object as the default orientation is
* in landscape mode.
* #param activity - the Activity where the orientation is applied to the Camera object
* #param cameraId - the id of the used Camera(using the Front Facing Camera or the Back Facing Camera)
* #param camera - the Camera object on which the orientation changes will be applied
*/
public static void setCameraDisplayOrientation(Activity activity, int cameraId, Camera camera) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror(basically turn the image upside down)
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}
}
ScreenUtils:
public final class ScreenUtils {
/**
* Calculates the screen's width and returns this value.
* #param activity - the activity where the method is called from
* #return - the screen's width
*/
public static int getScreenWidth(Activity activity) {
Display display = activity.getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
return size.x;
}
/**
* Calculates the screen's height and returns this value.
* #param activity - the activity where the method is called from
* #return - the screen's height
*/
public static int getScreenHeight(Activity activity) {
Display display = activity.getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
return size.y;
}
}
And last but not least, don't forget about you permissions in the manifest file:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
I hope this answer helps you to get your camera on the go!
Cheers,
Mike
Related
I have the following class in which I am trying to implement the prototype pattern:
public class Element extends Group implements Cloneable{
private final double ELEMENT_WIDTH = 50;
private final double ELEMENT_HEIGHT = 70;
private final double CIRCLE_RADIUS = 1;
private int minimalNumberOfInputs;
private Shape body = new Rectangle(ELEMENT_WIDTH, ELEMENT_HEIGHT, Color.WHITE);
private Circle output = new Circle(CIRCLE_RADIUS);
private Line outputLine = new Line(ELEMENT_WIDTH, ELEMENT_HEIGHT / 2, ELEMENT_WIDTH + 15, ELEMENT_HEIGHT / 2);
private ArrayList<Circle> inputs;
private ArrayList<Line> inputLines;
private Circle inversionDesignation;
private Text symbol;
private Integer identifier;
private double bodyCorX;
private double bodyCorY;
private double corX = 0;
private double corY = 0;
private double mouseX = 0;
private double mouseY = 0;
private boolean dragging = false;
public Integer getIdentifier() {
return identifier;
}
public ArrayList<Circle> getInputs() {
return inputs;
}
public Circle getOutput() {
return output;
}
public Circle getInversionDesignation() {
return inversionDesignation;
}
public double getELEMENT_WIDTH() {
return ELEMENT_WIDTH;
}
public double getELEMENT_HEIGHT() {
return ELEMENT_HEIGHT;
}
public double getCIRCLE_RADIUS() {
return CIRCLE_RADIUS;
}
public int getMinimalNumberOfInputs() {
return minimalNumberOfInputs;
}
public Shape getBody() {
return body;
}
public Line getOutputLine() {
return outputLine;
}
public ArrayList<Line> getInputLines() {
return inputLines;
}
public Text getSymbol() {
return symbol;
}
public double getBodyCorX() {
return bodyCorX;
}
public double getBodyCorY() {
return bodyCorY;
}
public double getCorX() {
return corX;
}
public double getCorY() {
return corY;
}
public double getMouseX() {
return mouseX;
}
public double getMouseY() {
return mouseY;
}
public boolean isDragging() {
return dragging;
}
public Element(){
}
public Element(Circle inversionDesignation, Text symbol, int minimalNumberOfInputs) {
this.minimalNumberOfInputs = minimalNumberOfInputs;
this.body.setStroke(Color.BLACK);
this.body.setStrokeType(StrokeType.INSIDE);
this.body.setStrokeWidth(2.5);
this.output.setFill(Color.BLACK);
this.output.toFront();
this.inversionDesignation = inversionDesignation;
this.symbol = symbol;
this.inputs = new ArrayList<>();
this.inputLines = new ArrayList<>();
this.identifier = this.hashCode();
this.outputLine.setStrokeWidth(2);
this.createStartInputs();
this.configureInputPoints();
this.bindGraphicalElements();
elementMovementEvents();
elementEnteredEvents();
}
private void bindGraphicalElements() {
this.getChildren().add(body);
this.getChildren().add(output);
this.getChildren().add(outputLine);
this.getChildren().addAll(inputs);
this.getChildren().addAll(inputLines);
if (this.symbol != null) {
this.getChildren().add(symbol);
symbol.relocate((ELEMENT_WIDTH / 2) - symbol.getTabSize() / 2, ELEMENT_HEIGHT / 8);
symbol.setFont(new Font("Consolas", 14));
}
if (this.inversionDesignation != null) {
this.getChildren().add(inversionDesignation);
this.inversionDesignation.setStrokeType(StrokeType.INSIDE);
this.inversionDesignation.setStrokeWidth(1);
this.inversionDesignation.setStroke(Color.BLACK);
this.inversionDesignation.relocate((this.bodyCorX + this.ELEMENT_WIDTH) - (this.inversionDesignation.getRadius() + 1), (this.bodyCorY + this.ELEMENT_HEIGHT / 2) - this.inversionDesignation.getRadius());
inversionDesignation.toFront();
}
}
private void addGraphicalElement(Shape shape) {
this.getChildren().add(shape);
}
private void createStartInputs() {
for (int i = 0; i < this.minimalNumberOfInputs; i++) {
inputs.add(new Circle(CIRCLE_RADIUS));
inputs.get(i).setFill(Color.BLACK);
inputs.get(i).toFront();
}
configureInputPoints();
for (int i = 0; i < inputs.size(); i++) {
Line line = new Line(inputs.get(i).getLayoutX() - 15, inputs.get(i).getLayoutY(), inputs.get(i).getLayoutX(), inputs.get(i).getLayoutY());
line.setStrokeWidth(2);
inputLines.add(line);
}
}
private void addNewInput() {
Circle newCircle = new Circle(CIRCLE_RADIUS);
newCircle.setFill(Color.BLACK);
this.inputs.add(newCircle);
this.configureInputPoints();
this.addGraphicalElement(newCircle);
}
private void configureInputPoints() {
this.output.relocate((this.bodyCorX + this.ELEMENT_WIDTH) - (output.getRadius() + 1), (this.bodyCorY + this.ELEMENT_HEIGHT / 2) - output.getRadius());
int distance = (int) ELEMENT_HEIGHT / (inputs.size() + 1); //Растояние между точками входа.
for (int i = 0; i < inputs.size(); i++) {
inputs.get(i).relocate(this.bodyCorX - (CIRCLE_RADIUS - 1), this.bodyCorY + (distance * (i + 1) - CIRCLE_RADIUS));
}
}
private void elementMovementEvents() {
onMousePressedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
mouseX = event.getSceneX();
mouseY = event.getSceneY();
corX = getLayoutX();
corY = getLayoutY();
}
});
onMouseDraggedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
double offsetX = event.getSceneX() - mouseX; //смещение по X
double offsetY = event.getSceneY() - mouseY;
corX += offsetX;
corY += offsetY;
double scaledX = corX;
double scaledY = corY;
setLayoutX(scaledX);
setLayoutY(scaledY);
dragging = true;
mouseX = event.getSceneX();
mouseY = event.getSceneY();
event.consume();
}
});
onMouseClickedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
dragging = false;
}
});
}
private void testEvent() {
this.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
this.addNewInput();
});
}
private void elementEnteredEvents() {
onMouseClickedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getTarget() instanceof Circle) {
System.out.println("Circle!");
}
}
});
}
#Override
public Element clone() throws CloneNotSupportedException{
return (Element)super.clone();
}
#Override
public String toString() {
return "Element " + this.hashCode() + ": location = " + this.getLayoutX() + ": output = " + this.minimalNumberOfInputs;
}
#Override
public boolean equals(Object obj) {
if(!(obj instanceof Element)) return false;
Element element = (Element) obj;
return element.getBodyCorX() == bodyCorX && element.getBodyCorY() == bodyCorY && element.getBody() == body;
}
}
I am trying to implement a pattern using the following method:
#Override
public Element clone() throws CloneNotSupportedException{
return (Element)super.clone();
}
I have a controller like this:
public class FXMLController {
#FXML
private AnchorPane anchorPane;
#FXML
private AnchorPane workPane;
//prototypes
private Element AndPrototype = new Element(null, new Text("&"), 2);
private Element OrPrototype = new Element(null, new Text("1"), 2);
private Element NotPrototype = new Element(new Circle(5, Color.WHITE), null, 1);
private Element AndNotPrototype = new Element(new Circle(5, Color.WHITE), new Text("&"), 2);
private Element OrNotPrototype = new Element(new Circle(5, Color.WHITE), new Text("1"), 2);
#FXML
public void initialize() {
}
#FXML
private void method() throws CloneNotSupportedException {
workPane.getChildren().add(AndPrototype.clone());
}
}
In this method, I am trying to make a clone and add it to the AnchorPane
#FXML
private void method() throws CloneNotSupportedException {
workPane.getChildren().add(AndPrototype.clone());
}
As a result, when I click on the button, I get an error of the following content:
Exception in thread "JavaFX Application Thread" java.lang.IndexOutOfBoundsException: Index -1 out of bounds for length 8
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:266)
at java.base/java.util.Objects.checkIndex(Objects.java:359)
at java.base/java.util.ArrayList.get(ArrayList.java:427)
at javafx.base/com.sun.javafx.collections.ObservableListWrapper.get(ObservableListWrapper.java:89)
at javafx.base/com.sun.javafx.collections.VetoableListDecorator.get(VetoableListDecorator.java:305)
at javafx.graphics/javafx.scene.Parent.updateCachedBounds(Parent.java:1704)
at javafx.graphics/javafx.scene.Parent.recomputeBounds(Parent.java:1648)
at javafx.graphics/javafx.scene.Parent.doComputeGeomBounds(Parent.java:1501)
at javafx.graphics/javafx.scene.Parent$1.doComputeGeomBounds(Parent.java:115)
at javafx.graphics/com.sun.javafx.scene.ParentHelper.computeGeomBoundsImpl(ParentHelper.java:84)
at javafx.graphics/com.sun.javafx.scene.NodeHelper.computeGeomBounds(NodeHelper.java:115)
at javafx.graphics/javafx.scene.Node.updateGeomBounds(Node.java:3847)
at javafx.graphics/javafx.scene.Node.getGeomBounds(Node.java:3809)
at javafx.graphics/javafx.scene.Node.doComputeLayoutBounds(Node.java:3657)
at javafx.graphics/javafx.scene.Node$1.doComputeLayoutBounds(Node.java:449)
at javafx.graphics/com.sun.javafx.scene.NodeHelper.computeLayoutBoundsImpl(NodeHelper.java:166)
at javafx.graphics/com.sun.javafx.scene.GroupHelper.computeLayoutBoundsImpl(GroupHelper.java:63)
at javafx.graphics/com.sun.javafx.scene.NodeHelper.computeLayoutBounds(NodeHelper.java:106)
at javafx.graphics/javafx.scene.Node$13.computeBounds(Node.java:3509)
at javafx.graphics/javafx.scene.Node$LazyBoundsProperty.get(Node.java:9782)
at javafx.graphics/javafx.scene.Node$LazyBoundsProperty.get(Node.java:9752)
at javafx.graphics/javafx.scene.Node.getLayoutBounds(Node.java:3524)
at javafx.graphics/javafx.scene.layout.AnchorPane.computeWidth(AnchorPane.java:272)
at javafx.graphics/javafx.scene.layout.AnchorPane.computeMinWidth(AnchorPane.java:248)
at javafx.graphics/javafx.scene.Parent.minWidth(Parent.java:1048)
at javafx.graphics/javafx.scene.layout.Region.minWidth(Region.java:1553)
at javafx.graphics/javafx.scene.layout.Region.computeChildPrefAreaWidth(Region.java:2012)
at javafx.graphics/javafx.scene.layout.AnchorPane.computeChildWidth(AnchorPane.java:315)
at javafx.graphics/javafx.scene.layout.AnchorPane.layoutChildren(AnchorPane.java:353)
at javafx.graphics/javafx.scene.Parent.layout(Parent.java:1207)
at javafx.graphics/javafx.scene.Scene.doLayoutPass(Scene.java:576)
at javafx.graphics/javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2476)
at javafx.graphics/com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:413)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at javafx.graphics/com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:412)
at javafx.graphics/com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:439)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:563)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:543)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:536)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:342)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:831)
I have absolutely no idea what this error may be connected with and in which direction they are moving.
Usually this is caused because you modified the scene graph or an attribute of a scene graph node off of the JavaFX thread.
Similar stack traces all caused by threading errors:
javafx vlcj play mutiple video get IndexOutOfBoundsException error
Exception on JavaFX when moving Labels around their container.(IndexOutOfBoundsException)
How to fix IndexOutOfBounds exception when javafx recomputes Parent/Node Bounds
How do I find out what's causing this Java FX Application Thread exception?
If it is a multi-threading issue, usually it can be fixed by either removing unnecessary threading. Or if multi-threading is unavoidable, using tools like the javafx.concurrent package or Platform.runLater to ensure nodes in the active scene graph are only modified on the JavaFX thread.
However, if you don’t have any multi-threading going on, it might be down to the weird cloning stuff you have going on which may be ill-advised. JavaFX nodes can only occur once in the scene and the clones may cause glitches in the framework.
I developing music app.so i create Music service that play in song background so that work perfectly good in when i play song.
But my question is we need different different service for all.
so when i use music Service for album for playing album song that not working.
i don'i know what to do
thanks in advance
that my code
package com.musicbox.google.musicboxlive.Musiclibrary;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentUris;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.PowerManager;
import android.util.Log;
import com.musicbox.google.musicboxlive.Musiclibrary.Song.Songpojo;
import com.musicbox.google.musicboxlive.R;
import java.util.ArrayList;
/**
* Created by Jiagr Fumakiya on 2/1/18.
*/
public class MusicService extends Service implements
MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener,
MediaPlayer.OnCompletionListener {
//media player
private MediaPlayer player;
//song list
private ArrayList<Songpojo> songpojos;
//current position
private int songPosn;
private final IBinder musicBind = new MusicBinder();
String songtitile;
#Override
public IBinder onBind(Intent intent) {
return musicBind;
}
#Override
public void onCreate() {
super.onCreate();
songPosn = 0;
//create player
player = new MediaPlayer();
initMusicPlayer();
}
public void initMusicPlayer() {
//set player properties
player.setWakeMode(getApplicationContext(),
PowerManager.PARTIAL_WAKE_LOCK);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setOnPreparedListener(this);
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
}
public void setList(ArrayList<Songpojo> theSongpojos) {
songpojos = theSongpojos;
}
public class MusicBinder extends Binder {
public MusicService getService() {
return MusicService.this;
}
}
#Override
public boolean onUnbind(Intent intent) {
player.stop();
player.release();
return false;
}
public void playSong() {
player.reset();
//get song
Songpojo playSongpojo = songpojos.get(songPosn);
songtitile = playSongpojo.getTitle();
//get id
long currSong = playSongpojo.getID();
//set uri
Uri trackUri = ContentUris.withAppendedId
(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, currSong);
try {
player.setDataSource(getApplicationContext(), trackUri);
} catch (Exception e) {
Log.e("MUSIC SERVICE", "Error setting data source", e);
}
player.prepareAsync();
//player.start();
}
public void nextsong() {
if (songPosn < (songpojos.size() - 1)) {
// play(currentSongIndex - 1);
songPosn = songPosn + 1;
} else {
songPosn = 0;
}
}
public void previousong() {
if (songPosn > 0) {
// play(currentSongIndex - 1);
songPosn = songPosn - 1;
} else {
songPosn = songpojos.size() - 1;
}
}
public void pause() {
if (player.isPlaying()) {
player.pause();
}
}
public void resume() {
int seek = player.getCurrentPosition();
if (!player.isPlaying()) {
player.seekTo(seek);
player.start();
}
}
public void isplaying() {
if (player.isPlaying()) {
player.reset();
}
}
#Override
public void onStart(Intent intent, int startId) {
Log.d("musicid", String.valueOf(startId));
super.onStart(intent, startId);
}
#Override
public void onCompletion(MediaPlayer mp) {
}
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
return false;
}
#Override
public void onPrepared(MediaPlayer mp) {
//start playback
/* if (mp.isPlaying()) {
mp.reset();
} else {
mp.start();
}*/
mp.start();
Intent notIntent = new Intent(this, MusiclibraryHome.class);
notIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendInt = PendingIntent.getActivity(this, 0,
notIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification.Builder builder = new Notification.Builder(this);
builder.setContentIntent(pendInt)
.setSmallIcon(R.drawable.play)
.setTicker(songtitile)
.setOngoing(true)
.setContentTitle("Playing")
.setContentText(songtitile);
Notification not = builder.build();
startForeground(1, not);
}
public void setSong(int songIndex) {
songPosn = songIndex;
}
}
Yes..
After finding lot of online now i get that you need different method to handle each event in Music player (like album,song ,genres) all different.
that is method from playing song album wise in android.
public void playAlbum() {
player.reset();
//get Album song
AlbumPojo albumPojo = albumPojos.get(albumPosn);
id = albumPojo.getId();
songName = albumPojo.getTitle();
albumArt = albumPojo.getAlbumArt();
albumName = albumPojo.getAlbum();
artistName = albumPojo.getAlbum_artist();
Intent intent = new Intent("MusicServiceInfo");
intent.putExtra("songid", id);
intent.putExtra("songtitile", songName);
intent.putExtra("songimage", albumArt);
intent.putExtra("albumname", albumName);
intent.putExtra("artist", artistName);
sendBroadcast(intent);
Log.w("Albumart", "" + albumArt);
//set uri
Uri trackUri = ContentUris.withAppendedId
(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);
try {
player.setDataSource(getApplicationContext(), trackUri);
} catch (Exception e) {
Log.e("MUSIC SERVICE", "Error setting data source", e);
}
player.prepareAsync();
}
I create a Tooltip for a TableColumn header via fxml like this:
<TableColumn>
<cellValueFactory>
<PropertyValueFactory property="someProperty" />
</cellValueFactory>
<graphic>
<Label text="Column 1">
<tooltip>
<Tooltip text="Tooltip text" />
</tooltip>
</Label>
</graphic>
</TableColumn>
I would like to keep the tooltip open if I move the mouse over the tooltip. Eventually I would like to have clickable links in the tooltip text (Just like Eclipse JavaDoc tooltips).
Is that possible?
Edit:
Considering the answer, I am trying the following now:
Label label = new Label();
label.setText("test text");
DelayedTooltip beakerTip = new DelayedTooltip();
beakerTip.setDuration(3000);
beakerTip.setText("Science from Base: 12");
beakerTip.isHoveringTarget(label);
Tooltip tooltip = new Tooltip();
tooltip.setText("test tooltip text");
label.setTooltip(beakerTip);
myTableColumn.setGraphic(label);
Here the problem is that the label is not the same as the Tooltip. So if the mouse is over the Tooltip but not over the label, the Tooltip is hidden. I cannot pass the Tooltip itself as a hover target, since it is not a Node.
Indeed it is possible, but it involves basically gutting most of the basic functionality of the tooltip. This is how I implemented the same thing:
First I made a custom tooltip that was based off the basic tooltip(this code is a modification of a similar question)
public class DelayedTooltip extends Tooltip {
private int duration = 0;
private BooleanProperty isHoveringPrimary = new SimpleBooleanProperty(false);
private BooleanProperty isHoveringSecondary = new SimpleBooleanProperty(false);
public void setDuration(int d) {
duration = d;
}
public BooleanProperty isHoveringPrimaryProperty()
{
return isHoveringPrimary;
}
public BooleanProperty isHoveringSecondaryProperty()
{
return isHoveringSecondary;
}
public void isHoveringTargetPrimary(Node node){
node.setOnMouseEntered(e -> isHoveringPrimary.set(true));
node.setOnMouseExited(e -> isHoveringPrimary.set(false));
}
//Usually you will use the tooltip here so enter tooltip.getGraphic() for the node.
public void isHoveringTargetSecondary(Node node){
node.setOnMouseEntered(e -> isHoveringTooltip.set(true)):
node.setOnMouseExited(e -> isHoveringTooltip.set(false));
}
#Override
public void hide() {
if(isHoveringPrimary.get()==true || isHoveringTooltip.get()==true)
{
Timeline timeline = new Timeline();
KeyFrame key = new KeyFrame(Duration.millis(duration));
timeline.getKeyFrames().add(key);
timeline.setOnFinished(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
DelayedTooltip.super.hide();
}
});
timeline.play();
}
else
{
DelayedTooltip.super.hide();
}
}
}
And then this is how I installed the tooltip
DelayedTooltip beakerTip = new DelayedTooltip();
beakerTip.setDuration(999999);
beakerTip.setText("Science from Base: 12");
beakerTip.isHoveringTargetPrimary(beakerView);
beakerTip.isHoveringTargetSecondary(beakerTip.geoGraphic());
You could edit this and make it into one method with multiple parameters if you so wish, but otherwise, this does work.
I use this class now and it works as expected:
public class HoveringTooltip extends Tooltip {
private Timer timer = new Timer();
private Map<Object, Boolean> mapHoveringTarget2Hovering = new ConcurrentHashMap<>();
private final int duration;
public HoveringTooltip(int duration) {
super.setAutoHide(false);
this.duration = duration;
}
public void addHoveringTarget(Node object) {
mapHoveringTarget2Hovering.put(object, false);
object.setOnMouseEntered(e -> {
onMouseEntered(object);
});
object.setOnMouseExited(e -> {
onMouseExited(object);
});
}
public void addHoveringTarget(Scene object) {
mapHoveringTarget2Hovering.put(object, false);
object.setOnMouseEntered(e -> {
onMouseEntered(object);
});
object.setOnMouseExited(e -> {
onMouseExited(object);
});
}
#Override
public void hide() {
// super.hide();
}
public boolean isHovering() {
return isHoveringProperty().get();
}
public BooleanProperty isHoveringProperty() {
synchronized(mapHoveringTarget2Hovering) {
for(Entry<Object, Boolean> e : mapHoveringTarget2Hovering.entrySet()) {
if(e.getValue()) {
// if one hovering target is hovering, return true
return new ReadOnlyBooleanWrapper(true);
}
}
// no hovering on any target, return false
return new ReadOnlyBooleanWrapper(false);
}
}
private synchronized void onMouseEntered(Object object) {
// System.err.println("Mouse entered for " + object + ", canelling timer");
// stop a potentially running hide timer
timer.cancel();
mapHoveringTarget2Hovering.put(object, true);
}
private synchronized void onMouseExited(Object object) {
// System.err.println("Mouse exit for " + object + ", starting timer");
mapHoveringTarget2Hovering.put(object, false);
startTimer();
}
private void startTimer() {
timer.cancel();
timer = new Timer();
timer.schedule(new TimerTask() {
#Override
public void run() {
Platform.runLater(new Runnable() {
#Override
public void run() {
if(!isHovering())
HoveringTooltip.super.hide();
}
});
}
}, duration);
}
}
Here's a way to hack the behavior of a Tooltip:
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.control.Tooltip;
import javafx.util.Duration;
import java.lang.reflect.Field;
/**
* #author rdeardorff
*/
public class TooltipDelay {
public static void hackTooltipActivationTimer( Tooltip tooltip, int delay ) {
hackTooltipTiming( tooltip, delay, "activationTimer" );
}
public static void hackTooltipHideTimer( Tooltip tooltip, int delay ) {
hackTooltipTiming( tooltip, delay, "hideTimer" );
}
private static void hackTooltipTiming( Tooltip tooltip, int delay, String property ) {
try {
Field fieldBehavior = tooltip.getClass().getDeclaredField( "BEHAVIOR" );
fieldBehavior.setAccessible( true );
Object objBehavior = fieldBehavior.get( tooltip );
Field fieldTimer = objBehavior.getClass().getDeclaredField( property );
fieldTimer.setAccessible( true );
Timeline objTimer = (Timeline) fieldTimer.get( objBehavior );
objTimer.getKeyFrames().clear();
objTimer.getKeyFrames().add( new KeyFrame( new Duration( delay ) ) );
}
catch ( Exception e ) {
e.printStackTrace();
}
}
}
I have a problem with my current libGDX project.
The game starts with a main menu, where you can click on two buttons. After starting the game, you can pause it through Esc and get to the pause screen, which is very similar to the main menu.
I don't know why, but the buttons in the pause screen are not clickable.
Here is the code of the game screen with the problem:
public class GameScreen implements Screen{
private Texture[] monsterTextures = {Assets.manager.get(("Ressources/DemonHunter.jpg"), Texture.class), Assets.manager.get(("Ressources/WingedDemon.jpg"), Texture.class),
Assets.manager.get(("Ressources/Viking.jpg"), Texture.class), Assets.manager.get(("Ressources/DemonWarrior.jpg"), Texture.class)};
private Image[] monsterImages = {new Image(monsterTextures[0]), new Image(monsterTextures[1]), new Image(monsterTextures[2]), new Image(monsterTextures[3])};
private Stage gameStage = new Stage(), pauseStage = new Stage();
private Table table = new Table();
private Skin menuSkin = Assets.menuSkin;
private TextButton buttonContinue = new TextButton("Continue", menuSkin),
buttonExit = new TextButton("Exit", menuSkin);
private Label title = new Label ("Game", menuSkin);
private int randomMonster;
private int currentMonsterLife = 1 + (int)(Math.random() * ((5-1) + 1));
public static final int GAME_CREATING = 0;
public static final int GAME_RUNNING = 1;
public static final int GAME_PAUSED = 2;
private int gamestatus = 0;
#Override
public void show() {
randomMonster = 0 + (int)(Math.random() * ((3-0) + 1));
gameStage.addActor(monsterImages[randomMonster]);
}
public void newMonster() {
monsterImages[randomMonster].remove();
Gdx.gl.glClearColor(0,0,0,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
randomMonster = 0 + (int)(Math.random() * ((3-0) + 1));
currentMonsterLife = 1 + (int)(Math.random() * ((5-1) + 1));
gameStage.addActor(monsterImages[randomMonster]);
}
#Override
public void render(float delta) {
if(Gdx.input.isKeyJustPressed(Keys.ESCAPE)) pauseGame();
if(gamestatus == GAME_CREATING) {
buttonContinue.addListener(new ClickListener(){
public void clicked(InputEvent event, float x, float y) {
Gdx.gl.glClearColor(0,0,0,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
gamestatus = GAME_RUNNING;
}
});
buttonExit.addListener(new ClickListener(){
public void clicked(InputEvent event, float x, float y) {
Gdx.app.exit();
}
});
table.add(title).padBottom(40).row();
table.add(buttonContinue).size(150, 60).padBottom(20).row();
table.add(buttonExit).size(150, 60).padBottom(20).row();
table.setFillParent(true);
pauseStage.addActor(table);
Gdx.input.setInputProcessor(pauseStage);
Gdx.input.setInputProcessor(gameStage);
gamestatus = GAME_RUNNING;
}
if(gamestatus == GAME_RUNNING) {
Gdx.gl.glClearColor(0,0,0,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
gameStage.act();
gameStage.draw();
if(Gdx.input.justTouched())currentMonsterLife -= 1;
if(currentMonsterLife == 0)newMonster();
}
if(gamestatus == GAME_PAUSED) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
pauseStage.act();
pauseStage.draw();
}
}
public void pauseGame() {
gamestatus = GAME_PAUSED;
}
#Override
public void resize(int width, int height) {
// TODO Auto-generated method stub
}
#Override
public void pause() {
pauseGame();
}
#Override
public void resume() {
// TODO Auto-generated method stub
}
#Override
public void hide() {
// TODO Auto-generated method stub
}
#Override
public void dispose() {
for(int i = 0; i < monsterTextures.length; i++) {
monsterTextures[i].dispose();
}
gameStage.dispose();
pauseStage.dispose();
menuSkin.dispose();
}
}
And here is the code of the main menu, where the buttons are working:
public class MainMenu implements Screen {
private Stage stage = new Stage();
private Table table = new Table();
private Skin menuSkin = Assets.menuSkin;
private TextButton buttonPlay = new TextButton("Play", menuSkin),
buttonExit = new TextButton("Exit", menuSkin);
private Label title = new Label ("Hunt for Power", menuSkin);
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act();
stage.draw();
}
#Override
public void show() {
buttonPlay.addListener(new ClickListener(){
public void clicked(InputEvent event, float x, float y) {
((Game)Gdx.app.getApplicationListener()).setScreen(new GameScreen());
}
});
buttonExit.addListener(new ClickListener(){
public void clicked(InputEvent event, float x, float y) {
Gdx.app.exit();
}
});
table.add(title).padBottom(40).row();
table.add(buttonPlay).size(150, 60).padBottom(20).row();
table.add(buttonExit).size(150, 60).padBottom(20).row();
table.setFillParent(true);
stage.addActor(table);
Gdx.input.setInputProcessor(stage);
}
#Override
public void resize(int width, int height) {
// TODO Auto-generated method stub
}
#Override
public void pause() {
// TODO Auto-generated method stub
}
#Override
public void resume() {
// TODO Auto-generated method stub
}
#Override
public void hide() {
dispose();
}
#Override
public void dispose() {
stage.dispose();
}
}
I really hope someone is able to find the solution to this.
Greetings, Joshflux
There is something weird with how you set the InputProcessor:
in the show() method you set the Stage stage as the InputProcessor:
Gdx.input.setInputProcessor(stage);
so far so good, but in the render() method you set it to different stages than the one where your buttons are!
Gdx.input.setInputProcessor(pauseStage);
Gdx.input.setInputProcessor(gameStage);
==> remove this code from the render method! Also you should not have the other code that looks like it should only be executed when creating the screen inside the render() method. It seems like your code results in overlapping stages, buttons & tables and it is unclear which stage currently is set as InputProcessor.
Like #donfuxx said, you should not set your input processor in the render() method, but rather in show().
And you can only set one input processor at a time. Your second call to setInputProcessor replaces the first call. If you want two different stages as input processors, you must combine them with an InputMultiplexer:
public void show(){
Gdx.input.setInputProcessor(new InputMultiplexer(pauseStage, gameStage)); //list them in order of precedence
//...your other code
}
I am trying to build a simple menu-based GUI with J2ME. The menu entries are currently objects of classes derived from the class Button. Is there any way I can:
Replace the text in the button and have an image show instead, sort of an icon?
Make the text and image appear side by side on the same menu bar.
If my question is not clear, please let me know and I will edit it.
You can create your own Item that looks like a button by extending the CustomItem class.
This is a working MIDlet with a good MyButton class:
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.CustomItem;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemStateListener;
import javax.microedition.midlet.MIDlet;
public class TestMidlet extends MIDlet implements ItemStateListener {
class MyButton extends CustomItem {
private Image _image = null;
private boolean _down = false;
private int _clicks = 0;
public MyButton(Image image) {
super("");
_image = image;
}
// Button's image
public void setImage(Image image) {
_image = image;
repaint();
}
public Image getImage() {
return _image;
}
// Has the button been clicked?
public boolean isClicked() {
if(_clicks>0) {
_clicks -= 1;
return true;
}
return false;
}
// Is the button currently down?
public boolean isDown() {
return _down;
}
public void setDown(boolean down) {
if(_down)
_clicks += 1;
if(down!=_down) {
_down = down;
repaint();
notifyStateChanged();
}
}
public void setDown() {
setDown(true);
}
public void setUp() {
setDown(false);
}
// Minimal button size = image size
protected int getMinContentHeight() {
return getImage().getHeight();
}
protected int getMinContentWidth() {
return getImage().getWidth();
}
// Preferred button size = image size + borders
protected int getPrefContentHeight(int width) {
return getImage().getHeight()+2;
}
protected int getPrefContentWidth(int height) {
return getImage().getWidth()+2;
}
// Button painting procedure
protected void paint(Graphics g, int w, int h) {
// Fill the button with grey color - background
g.setColor(192, 192, 192);
g.fillRect(0, 0, w, h);
// Draw the image in the center of the button
g.drawImage(getImage(), w/2, h/2, Graphics.HCENTER|Graphics.VCENTER);
// Draw the borders
g.setColor(isDown()?0x000000:0xffffff);
g.drawLine(0, 0, w, 0);
g.drawLine(0, 0, 0, h);
g.setColor(isDown()?0xffffff:0x000000);
g.drawLine(0, h-1, w, h-1);
g.drawLine(w-1, 0, w-1, h);
}
// If FIRE key is pressed, the button becomes pressed (down state)
protected void keyPressed(int c) {
if(getGameAction(c)==Canvas.FIRE)
setDown();
}
// When FIRE key is released, the button becomes released (up state)
protected void keyReleased(int c) {
if(getGameAction(c)==Canvas.FIRE)
setUp();
}
// The same for touchscreens
protected void pointerPressed(int x, int y) {
setDown();
}
protected void pointerReleased(int x, int y) {
setUp();
}
}
MyButton button = null;
public void itemStateChanged(Item item) {
if(item==button) {
if(button.isClicked())
System.out.print("clicked, ");
System.out.println(button.isDown()?"currently down":"currently up");
}
}
public void startApp() {
try {
Form form = new Form("Example");
button = new MyButton(Image.createImage("/icon.png"));
form.append(button);
form.setItemStateListener(this);
Display.getDisplay(this).setCurrent(form);
} catch(Exception e) {
e.printStackTrace();
}
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
notifyDestroyed();
}
}