Image in button - j2me - button

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();
}
}

Related

Keep Tooltip open as long as mouse is over it

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();
}
}
}

How to refresh pane in javafx

I recently have been learning JavaFX and have an issue about how to refresh the pane. In this simple program, I want the black square to move to the right into the next block when I am clicking the move button, but it doesn't work, I'm wondering how can I fix my code.
screen shot
Main Class:
public class Main extends Application {
private Cell cells[] = new Cell[5];
private Player player;
private Board board;
Button move = new Button("move");
public Main() throws Exception {
for (int i = 0; i < cells.length; i++) {
cells[i] = new Cell(i);
}
this.player = new Player(0, cells);
this.board = new Board(player, cells);
}
#Override
public void start(Stage primaryStage) throws Exception {
Main game = new Main();
BorderPane pane = new BorderPane();
pane.setCenter(board);
pane.setBottom(move);
Scene scene = new Scene(pane,400,80);
primaryStage.setTitle("Move");
primaryStage.setScene(scene);
primaryStage.show();
move.setOnAction(e -> game.move());
}
public void move() {
player.currentCell.index += 1;
board.paint();
}
public static void main(String[] args) {
launch(args);
}
}
Board class:
class Board extends Pane {
private Player player;
public Cell cells[];
private final int CELLWIDTH = 40;
private final int CELLHEIGHT = 40;
private final int LMARGIN = 100;
public Board(Player p, Cell cells[]) {
player = p;
this.cells = cells;
paint();
}
public void paint() {
Cell cell;
for (int i=0; i<cells.length; i++) {
cell = cells[i];
Rectangle r1 = new Rectangle(xCor(cell.index), 0, CELLWIDTH, CELLHEIGHT);
r1.setStroke(Color.BLACK);
r1.setFill(Color.WHITE);
getChildren().add(r1);
}
cell = player.currentCell;
Rectangle r2 = new Rectangle(xCor(cell.index), 0, CELLWIDTH, CELLHEIGHT);
r2.setFill(Color.BLACK);
getChildren().add(r2);
}
private int xCor(int col) {
return LMARGIN + col * CELLWIDTH;
}
}
Player Class:
class Player {
public int position;
public Cell currentCell;
public Player(int position, Cell cell[]) throws Exception {
this.currentCell = cell[0];
}
}
Cell Class:
class Cell {
public int index;
public Cell(int index) {
this.index = index;
}
}
You might want to reword your code, storing the player's location just in the Player class is going to make life difficult. I would also suggest adding a flag to the Cell class stating if the player is inside, e.g.
class Cell {
public int index;
private Player playerInCell;
public Cell(int index) {
this.index = index;
}
public void setPlayerInCell(Player p){
this.playerInCell = p;
}
public void clearPlayerInCell(){
this.playerInCell = null;
}
public Player getPlayerInCell(){
return this.playerInCell;
}
}
Then, upon moving a player to the Cell you can clear them from the previous Celland set them in the new one and in your Paint() function, if player is present, colour the cell in.
The other thing, if you wish to stick with your method, your issue is caused by that you are only changing the index property on the Cell class, you should also be either changing the Cell's position in the array cells[] or just changing the currentCell property of the Player class, otherwise your player always stays in the same place. Here is an example of changing the Player's currentCell property:
public void move() {
Cell currentCell = player.currentCell;
Cell nextCell = null;
for (int i = 0; i < cells.length; i++) {
if (cells[i] == currentCell && i+1 < cells.length){
nextCell = cells[i+1];
break;
}
}
if (nextCell != null)
player.currentCell = nextCell;
else{
//Error handling, next cell not found
}
board.paint();
}
[Edit]
I've done some major code cleanup, some of the ways you were doing things was a bit odd, I hope you don't mind, here are the classes that changed:
Main
public class Main extends Application {
private Cell cells[] = new Cell[5];
private Player player;
private Board board;
Button move = new Button("move");
public Main() throws Exception{
for (int i = 0; i < cells.length; i++) {
cells[i] = new Cell(i);
}
this.player = new Player(cells[0]);
this.board = new Board(player, cells);
}
#Override
public void start(Stage primaryStage) throws Exception{
BorderPane pane = new BorderPane();
pane.setCenter(board);
pane.setBottom(move);
Scene scene = new Scene(pane,400,80);
primaryStage.setTitle("Move");
primaryStage.setScene(scene);
primaryStage.show();
move.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
move();
}
});
}
public void move() {
//Get current players cell, we want to move them one right
Cell currentCell = player.getCurrentCell();
Cell nextCell = null;
//Searching for current cell in board, if found we need to clear the player from it and select the next cell
for (int i = 0; i < cells.length; i++) {
if (cells[i] == currentCell && i+1 < cells.length){
cells[i].clearPlayerInCell();
nextCell = cells[i+1];
break;
}
}
//We found it, let's move the player
if (nextCell != null) {
player.setCurrentCell(nextCell);
nextCell.setPlayerInCell(player);
}
//We didn't find it, or our index was out of range, what do we do now?
else{
//Error handling, next cell not found
//Example, let's put them back at the start
player.setCurrentCell(cells[0]);
cells[0].setPlayerInCell(player);
cells[cells.length-1].clearPlayerInCell();
}
board.paint();
}
public static void main(String[] args) {
launch(args);
}
}
Board
public class Board extends Pane {
private Player player;
private Cell cells[];
private final int CELLWIDTH = 40;
private final int CELLHEIGHT = 40;
private final int LMARGIN = 100;
public Board(Player p, Cell cells[]) {
player = p;
this.cells = cells;
paint();
}
public Cell[] getCells(){
return this.cells;
}
public Player getPlayer() {
return player;
}
public void paint() {
//Clear previous cells, we don't need them now
getChildren().clear();
//Redraw them
for(Cell cell : cells){
Rectangle r1 = new Rectangle(xCor(cell.getIndex()), 0, CELLWIDTH, CELLHEIGHT);
r1.setStroke(Color.BLACK);
//We've found a player in the cell, let's colour it black
if (cell.getPlayerInCell() != null)
r1.setFill(Color.BLACK);
//No, player in this cell, white it is
else
r1.setFill(Color.WHITE);
getChildren().add(r1);
}
}
private int xCor(int col) {
return LMARGIN + col * CELLWIDTH;
}
}
Player
public class Player {
private Cell currentCell;
public Player(Cell cell) throws Exception {
this.currentCell = cell;
cell.setPlayerInCell(this);
}
public Cell getCurrentCell(){
return this.currentCell;
}
public void setCurrentCell(Cell cell){
this.currentCell = cell;
}
}
Cell
public class Cell {
private int index;
private Player playerInCell;
public Cell(int index) {
this.index = index;
}
public void setPlayerInCell(Player p){
this.playerInCell = p;
}
public void clearPlayerInCell(){
this.playerInCell = null;
}
public Player getPlayerInCell(){
return this.playerInCell;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
That now works and I can move the cell along, I've also set it so that the cell goes back to the beginning if the player reaches the end, but that's an example.
It works off using the Cell property of playerInCell, if that isn't null then we know a player is in the cell and can colour it black. If it is null, no player is in the cell and we can colour it white. This also allows you in the future to maybe have more players with different colours. Though I don't know what your end goal is. Hope this helps and if you want any further explanation, feel free to ask
Also, for further reading, see here why it's better to use getter and setters like I have
Also, the reasoning behind this bit of code:
move.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
move();
}
});
Is because I'm using Java 1.7 instead of Java 1.8 and can't use predicates, you should be safe to change that to move.setOnAction(e -> this.move()); instead.

JavaFX8: Treeview, get mouseclick on icon or text

Can I get different mouse-click events from a TreeViewItem depending on the user clicks on the icon or on the text label? The icon is not the collapse image of the tree.
According to Disable TreeItem's default expand/collapse on double click JavaFX 2.2 I have changed the constructor of the TreeMouseEventDispatcher:
class TreeMouseEventDispatcher implements EventDispatcher {
private final EventDispatcher originalDispatcher;
private final TreeCell<?> cell;
public TreeMouseEventDispatcher(EventDispatcher originalDispatcher, TreeCell<?> cell) {
this.originalDispatcher = originalDispatcher;
this.cell = cell;
}
private boolean isMouseEventOnGraphic(Node icon, Point2D coord) {
if (icon == null) {
return false;
}
return icon.localToScene(icon.getBoundsInLocal()).contains(coord);
}
#Override
public Event dispatchEvent(Event event, EventDispatchChain tail) {
if (event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
if (mouseEvent.getButton() == MouseButton.PRIMARY
&& mouseEvent.getClickCount() >= 2) {
if (!mouseEvent.isConsumed()) {
if (isMouseEventOnGraphic(cell.getGraphic(), new Point2D(mouseEvent.getSceneX(), mouseEvent.getSceneY()))) {
// action for double click on graphic
} else {
// action for double click on all other elements of
// the TreeCell (like text, text gap, disclosure node)
}
}
event.consume();
}
}
return originalDispatcher.dispatchEvent(event, tail);
}
}
and then use this TreeMouseEventDispatcher for the TreeCell:
treeView.setCellFactory(new Callback<TreeView<T>, TreeCell<T>>() {
#Override
public TreeCell<T> call(TreeView<T> param) {
return new TreeCell<T>() {
#Override
protected void updateItem(T item, boolean empty) {
if (item != null && !empty) {
EventDispatcher originalDispatcher = getEventDispatcher();
setEventDispatcher(new TreeMouseEventDispatcher(originalDispatcher, this));
}
}
};
}
}

libgdx buttons not clickable

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
}

Android: RelativeLayout margin works but gets overridden

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

Resources