Currently, I am trying to update the ImageViews within each Tile of a TilePane.
Variable at top of class available to all methods: ImageView imgArray[] = new ImageView[20];
Creating the original TilePane:
public TilePane createTilePane() {
TilePane tile = new TilePane();
tile.setPrefColumns(5);
for (int i = 0; i < 20; i++) {
tile.getChildren().add(this.createImageView("https://media.forgecdn.net/avatars/141/115/636539774401917932.jpeg", i));
} //for
return tile;
} //createTilePane()
public ImageView createImageView(String url, int x) {
Image image = new Image(url);
imgArray[x] = new ImageView();
imgArray[x].setImage(image);
imgArray[x].setFitWidth(100.0);
imgArray[x].setFitHeight(100.0);
imgArray[x].setImage(image);
return imgArray[x];
} //createImageView()
The problem is whenever I want to change an image within the tilepane I can't. For example I want to be able to do: this.createImageView("url", 2); to change a Tile within the TilePane.
I can do that with no errors, but the image is not changing.
Any ideas?
createImageView does not update the scene. It simply creates a new ImageView and updates the array containing the data. You need to update the scene, if you want to change the display. The best way of doing this in this scenario is modifying the image property of the ImageView.
Note that I've rewritten your code a bit to reuse the Image instance. createImageView has been renamed to setImage, since this seems like a better name for what it's doing to me:
public TilePane createTilePane() {
// reuse large object
Image image = new Image("https://media.forgecdn.net/avatars/141/115/636539774401917932.jpeg");
TilePane tile = new TilePane();
tile.setPrefColumns(5);
for (int i = 0; i < 20; i++) {
ImageView imageView = createImageView();
imageView.setImage(image);
imgArray[i] = imageView;
tile.getChildren().add(imageView);
} //for
return tile;
} //createTilePane()
private ImageView createImageView() {
ImageView image = new ImageView();
image.setFitWidth(100d);
image.setFitHeight(100d);
return image;
}
public void setImage(int index, String url) {
ImageView imageView = imgArray[index];
Image image = new Image(url);
// change image in ImageView that was added to the scene
imageView.setImage(image);
}
Related
Each stack pane is supposed to have two nodes. However the bottom node disappears outside the for loop for the first 2 indices in the stack pane array.
public class Main extends Application {
GridPane images;
StackPane[] stackPane;
ImageView cardBack;
ImageView[] cardImages;
#Override
public void start(Stage stage) throws FileNotFoundException {
images = new GridPane(); images.setAlignment(Pos.CENTER);
images.setVgap(5); images.setHgap(5);
cardBack = new ImageView(new Image(new FileInputStream("images/b1fv.gif")));
cardImages = new ImageView[]{
new ImageView(new Image(new FileInputStream("images/c1.gif"))),
new ImageView(new Image(new FileInputStream("images/c2.gif"))),
new ImageView(new Image(new FileInputStream("images/c3.gif")))
};
final Button[] flip = new Button[cardImages.length];
stackPane = new StackPane[cardImages.length];
for (int i = 0; i < cardImages.length; i++) {
stackPane[i] = new StackPane();
stackPane[i].getChildren().addAll(cardBack, cardImages[i]);
images.add(stackPane[i], i, 0, 1, 1);
flip[i] = new Button("Flip");
GridPane.setHalignment(flip[i], HPos.CENTER);
images.add(flip[i], i, 1, 1, 1);
// Debug
System.out.println(stackPane[i].getChildren().toString());
final int j = i;
flip[j].setOnAction(event -> doFlip(j));
}
// Debug
System.out.println("");
for (StackPane pane : stackPane)
System.out.println(pane.getChildren().toString());
stage.setTitle("Assignment 11");
stage.setScene(new Scene(images, 500,200));
stage.show();
}
void doFlip(int loc) {
// Debug
System.out.println("");
for (StackPane pane : stackPane)
System.out.println(pane.getChildren().toString());
ObservableList<Node> children = stackPane[loc].getChildren();
Node topNode = children.get(children.size()-1);
topNode.toBack();
}
public static void main(String[] args) {
launch(args);
}
}
[ImageView#5e1bc54a[styleClass=image-view], ImageView#25389181[styleClass=image-view]]
[ImageView#5e1bc54a[styleClass=image-view], ImageView#ff7cf97[styleClass=image-view]]
[ImageView#5e1bc54a[styleClass=image-view], ImageView#18b8669d[styleClass=image-view]]
[ImageView#25389181[styleClass=image-view]]
[ImageView#ff7cf97[styleClass=image-view]]
[ImageView#5e1bc54a[styleClass=image-view], ImageView#18b8669d[styleClass=image-view]]
[ImageView#25389181[styleClass=image-view]]
[ImageView#ff7cf97[styleClass=image-view]]
[ImageView#5e1bc54a[styleClass=image-view], ImageView#18b8669d[styleClass=image-view]]
Inside the for loop it displays all objects. However, outside the loop in does not display the bottom nodes except for the last element of array.
You cannot add the same Node instance to multiple parents!
In the top of your loop you have the following line:
stackPane[i].getChildren().addAll(cardBack, cardImages[i]);
The cardBack Node would be added to all StackPanes, but since it can only have one parent, it will be removed from the previous. Javafx has provided a way to reuse Image resources though (which is I imagine the thing you want to achieve). Instead of creating one ImageView, you can cache the Image instance passes in the ImageView constructor. E.g:
cardBack = new Image(new FileInputStream("images/b1fv.gif"));
// some stuff
for (int i = 0; i < cardImages.length; i++) {
stackPane[i] = new StackPane();
stackPane[i].getChildren().addAll(new ImageView(cardBack) , cardImages[i]);
// rest of the loop
}
If you use the Image and ImageView as described above, things will work correctly.
I have a program in which it displays 3 random hieroglyphic images on the screen. I have added a "Refresh" button in order to refresh the hieroglyphic images. When I click the button, the images refresh and randomize properly. After the button has been clicked for the first time however, it disappears. I am almost certain it has something to do with my pane.getChildren().clear(); line, but I cant seem to figure it out. Any tips or advice?
If I have posted incorrectly or without using proper guidelines, I apologize. This is one of my first posts.
Here is my code:
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.control.Button;
import javafx.geometry.Pos;
import javafx.scene.layout.HBox;
public class Lab6a extends Application {
#Override
public void start(Stage myStage) {
//Create an HBox layout.
HBox hBox1 = new HBox();
//Set alignment.
hBox1.setAlignment(Pos.CENTER);
getRandomHieroglyphic(hBox1);
//Create a Refresh button.
Button refresh = new Button("Refresh");
refresh.setOnAction(e -> getRandomHieroglyphic(hBox1));
hBox1.getChildren().add(refresh);
//Set the title for the second window.
myStage.setTitle("Random Hieroglyphics with Refresh");
//Create a scene for the window.
Scene myScene = new Scene(hBox1, 400, 400);
//Place the scene in the second window.
myStage.setScene(myScene);
//Show the stage.
myStage.show();
}
public void getRandomHieroglyphic(HBox pane) {
pane.getChildren().clear();
//Create random generators to get a random image
int randomInt1 = (int) (Math.random() * 9) + 1;
int randomInt2 = (int) (Math.random() * 9) + 1;
int randomInt3 = (int) (Math.random() * 9) + 1;
//Create paths for the images to be called
String path1 = "Image/Hieroglyphics/h" + randomInt1 + ".png";
String path2 = "Image/Hieroglyphics/h" + randomInt2 + ".png";
String path3 = "Image/Hieroglyphics/h" + randomInt3 + ".png";
//Add the images into the pane
pane.getChildren().add(new ImageView (path1));
pane.getChildren().add(new ImageView (path2));
pane.getChildren().add(new ImageView (path3));
}
public static void main(String[] args) {
launch(args);
}
}
clear() removes all children from the HBox including the Button.
You've got 3 ImageViews and want to keep the number of ImageViews constant. This means you should not replace them, but replace the images they contain instead. Furthermore you should avoid reloading the images and load all 9 images at the start:
public class Lab6a extends Application {
private Image[] images;
private final Random random = new Random();
#Override
public void start(Stage myStage) {
// load all hieroglyphs
images = new Image[9];
for (int i = 0; i < images.length; i++) {
images[i] = new Image("Image/Hieroglyphics/h" + (i+1) + ".png");
}
// store all imageviews in array
final ImageView[] imageViews = Stream.generate(ImageView::new).limit(3).toArray(ImageView[]::new);
// set initial images
getRandomHieroglyphic(imageViews);
...
hBox1.getChildren().add(refresh);
for (ImageView iv : imageViews) {
hBox1.getChildren().add(iv);
}
...
refresh.setOnAction(e -> getRandomHieroglyphic(imageViews));
...
}
public void getRandomHieroglyphic(ImageView[] imageViews) {
for (ImageView iv : imageViews) {
iv.setImage(images[random.nextInt(images.length)]);
}
}
You're right. You Hbox has 4 cildren initially, 3 images and the button. you should add the button too in the getRandomHieroglyphic() method.
But the better way was separate the button from the images. Best is this done by Adding a BorderPane to the Scene, and put the HBox with the images only in the center and the button in the bottom area.
#Override
public void start(Stage myStage) {
HBox hBox1 = new HBox();
hBox1.setAlignment(Pos.CENTER);
getRandomHieroglyphic(hBox1);
Button refresh = new Button("Refresh");
refresh.setOnAction(e -> getRandomHieroglyphic(hBox1));
BorderPane borderPane = new BorderPane();
borderPane.getBottom().add(refresh);
myStage.setTitle("Random Hieroglyphics with Refresh");
Scene myScene = new Scene(borderPane, 400, 400);
I want to make simple animation using multi-images on android studio
i have 3 images (img1,img2,img3)
and i want img1 visible firstly then after half second img1 invisible and img2 visible,then after half second img2 invisible and img3 visible,then after half second img3 invisible and img1 visible, so return to the first image like circle
1>2>3>1>2>3>1>2>3 to unlimited time, so how can i do that, please
class ImageAnimation {
Activity activity;
android.os.Handler handler;
ImageView[] images = new ImageView[3];//three images array
int current = 0;//current image iterator
public ImageAnimation(Activity activity) {
handler = new android.os.Handler();
images[0] = (ImageView) activity.findViewById(R.id.you_image1_id);
images[1] = (ImageView) activity.findViewById(R.id.you_image2_id);
images[2] = (ImageView) activity.findViewById(R.id.you_image3_id);
current = 0;
}
public void animateImages() {
handler.postDelayed(new Runnable() {
#Override
public void run() {
//loop for all images and animate it
for (int i = 0; i < images.length; i++) {
if (i!=current)
images[i].animate().alpha(1);//show
else
images[i].animate().alpha(0);//hide
}
if (current<images.length)
current++;
else
current=0;//again first one
//recurring using the same method - infinite loop
animateImages();
}
}, 500); //half a second
}
};
So i've come across this problem that i simply can't manage to sort out.
I'm making a game with the help of LibGdx and am trying to create a chat bubble functionality. The problem is, when i try to change the background of the label style to a 9patch drawable, it doesn't scale it well, or at all?
public class ChatBubble
{
private Label textLabel;
private BitmapFont font;
private Label.LabelStyle lStyle;
private int scaledWidth = 0;
private int scaledHeight = 0;
private Timer.Task currentTask;
private Texture bkg;
public ChatBubble()
{
font = new BitmapFont();
font.setColor(Color.BLACK);
bkg = new Texture("data/ui/chatb.9.png");
NinePatch np = new NinePatch(bkg,11,11,9,10);
NinePatchDrawable npd = new NinePatchDrawable(np);
lStyle = new Label.LabelStyle(font,font.getColor());
lStyle.background = npd;
textLabel = new Label("",lStyle);
textLabel.setVisible(false);
textLabel.setAlignment(Align.center);
currentTask = new Timer.Task() {
#Override
public void run() {
textLabel.setVisible(false);
}};
}
public void show(String text, float duration)
{
if(currentTask.isScheduled())currentTask.cancel();
textLabel.setText(text);
textLabel.setVisible(true);
scaledHeight = (int)textLabel.getPrefHeight();
scaledWidth = (int)textLabel.getWidth()/2;
Timer.schedule(currentTask,duration);
}
public void show(String text)
{
if(currentTask.isScheduled())currentTask.cancel();
textLabel.setText(text);
textLabel.setVisible(true);
scaledHeight = (int)textLabel.getPrefHeight();
scaledWidth = (int)textLabel.getWidth()/2;
Timer.schedule(currentTask,(float)(text.length()*0.1));
}
public void draw(SpriteBatch batch, float x, float y)
{
if(!textLabel.isVisible())return;
textLabel.setPosition(x - scaledWidth, y + scaledHeight);
batch.begin();
textLabel.draw(batch, 1);
batch.end();
}
}
How it looks ingame:
How the 9batch looks:
Any help would be appreciated!
Update:
I've found out that my 9patch scales ok, the problem being in label not updating it's size when setText() is called, thus having it width and height 0 since constructor was "".. calling layout() on label doesn't solve this either.
Call .pack() on the label after .setText() to tell it to size itself to its text (plus whatever padding there is in the background drawable). You don't need to call layout() since that's handled automatically.
I'm not sure the exact reason you have to manually call pack(), but this is generally the case with Widgets that you are not children of a WidgetGroup subclass (i.e. Table, VerticalGroup, etc.).
Let's say I've got a StackPane which has a BackgroundImage as background and another StackPane (or another component, if neccessary) as a child. The child covers only a part of the parent StackPane.
I'd like to know how to apply a GaussianBlur just to the area the child covers, so that the BackgroundImageis blurry in this area.
The size of the child changes when the parent is resized. It would be perfect to get a solution that will resize just in time, too.
If you want to do it manually, you can use the snapshot function to create a snapshot image, blur it and apply it to the child every time the parent is resized.
However, invoking snapshot all the time will cause performance loss. I rather suggest you create 2 images, one normal and one blurred, and display a viewport of the blurred one.
Here's a more "complex" example with a circle where the viewport isn't sufficient. The clip method is used in this case:
public class Lens extends Application {
Image image = new Image( "http://upload.wikimedia.org/wikipedia/commons/thumb/4/41/Siberischer_tiger_de_edit02.jpg/800px-Siberischer_tiger_de_edit02.jpg");
CirclePane circlePane;
#Override
public void start(Stage primaryStage) {
ImageView normalImageView = new ImageView( image);
ImageView blurredImageView = new ImageView( image);
blurredImageView.setEffect(new GaussianBlur( 40));
Group root = new Group();
root.getChildren().addAll( normalImageView);
Scene scene = new Scene( root, 1024, 768);
primaryStage.setScene( scene);
primaryStage.show();
// pane with clipped area
circlePane = new CirclePane( blurredImageView);
makeDraggable( circlePane);
root.getChildren().addAll( circlePane);
}
public static void main(String[] args) {
launch(args);
}
private class CirclePane extends Pane {
ImageView blurredImageView;
ImageView clippedView = new ImageView();
public CirclePane( ImageView blurredImageView) {
this.blurredImageView = blurredImageView;
// new imageview
update();
getChildren().addAll( clippedView);
}
public void update() {
// create circle
Circle circle = new Circle( 200);
circle.relocate( getLayoutX(), getLayoutY());
// clip image by circle
blurredImageView.setClip(circle);
// non-clip area should be transparent
SnapshotParameters parameters = new SnapshotParameters();
parameters.setFill(Color.TRANSPARENT);
// new image from clipped image
WritableImage wim = null;
wim = blurredImageView.snapshot(parameters, wim);
clippedView.setImage( wim);
}
}
// make node draggable
class DragContext {
double x;
double y;
}
public void makeDraggable( Node node) {
final DragContext dragDelta = new DragContext();
node.setOnMousePressed(mouseEvent -> {
dragDelta.x = node.getBoundsInParent().getMinX() - mouseEvent.getScreenX();
dragDelta.y = node.getBoundsInParent().getMinY() - mouseEvent.getScreenY();
});
node.setOnMouseDragged(mouseEvent -> {
node.relocate( mouseEvent.getScreenX() + dragDelta.x, mouseEvent.getScreenY() + dragDelta.y);
circlePane.update();
});
}
}
Just click on the circle and drag it around.