I am working on a 2D game in which sprites for player are stored in array list. Since player has multiple states, I want to remove previous states upon use. I have an ArrayList as:
List<Image> x = new ArrayList();
Now I use images from this list as: someImageView.setImage(x.getImage(y));
And I remove first (say 5: 0,1,2,3,4,5th images) states as:
for(int i = 0; i < 6; i++)
x.remove(y);
But these images still show instead of the next image set.
Could it be some sort of Caching? If so how can I delete this cache so my new images are applied?
My sprite images declaration is as follows (shown for one image, but its similar for all images):
Image someImg = new Image("path", true, false, true);
I don't know whether I understand you properly.
So, you use e.g. the first element of a list to be set on an ImageView (it is set properly), then you remove this element from the list.
At this point do you expect that the image will be removed from the ImageView also?
Why is shall be removed?
The list contains references to objects (pointers). When you set an image from the list the reference is passed to the ImageView, so now it will also point to that object. If you remove the reference from the list, it does not matter, the ImageView will still point to that reference.
So you can call setImage explicitly after you removed the elements, or use an ObservableList and set the image on remove (something like this):
ObservableList<Image> iList = FXCollections.observableArrayList();
iList.addListener(new ListChangeListener<Image>()
{
#Override
public void onChanged(javafx.collections.ListChangeListener.Change<? extends Image> change) {
while(change.next())
{
if(change.wasRemoved())
imgView.setImage(iList.get(0));
}
});
}
Related
Is there an easier way to write this code to make my image views invisible? I was hoping I could use some type of for loop. I am also new to programming so in your answer, can you please explain how the loop is working so I can use it in future references? Also, how do I assign 15 image views to 1 array?
iv_1.setVisibility(View.INVISIBLE);
iv_2.setVisibility(View.INVISIBLE);
iv_3.setVisibility(View.INVISIBLE);
iv_4.setVisibility(View.INVISIBLE);
iv_5.setVisibility(View.INVISIBLE);
iv_6.setVisibility(View.INVISIBLE);
iv_7.setVisibility(View.INVISIBLE);
iv_8.setVisibility(View.INVISIBLE);
iv_9.setVisibility(View.INVISIBLE);
iv_10.setVisibility(View.INVISIBLE);
iv_11.setVisibility(View.INVISIBLE);
iv_12.setVisibility(View.INVISIBLE);
iv_13.setVisibility(View.INVISIBLE);
iv_14.setVisibility(View.INVISIBLE);
iv_15.setVisibility(View.INVISIBLE);
iv_16.setVisibility(View.INVISIBLE);
iv_17.setVisibility(View.INVISIBLE);
iv_18.setVisibility(View.INVISIBLE);
iv_19.setVisibility(View.INVISIBLE);
iv_20.setVisibility(View.INVISIBLE);
iv_21.setVisibility(View.INVISIBLE);
iv_22.setVisibility(View.INVISIBLE);
iv_23.setVisibility(View.INVISIBLE);
iv_24.setVisibility(View.INVISIBLE);
iv_25.setVisibility(View.INVISIBLE);
iv_26.setVisibility(View.INVISIBLE);
iv_27.setVisibility(View.INVISIBLE);
iv_28.setVisibility(View.INVISIBLE);
iv_29.setVisibility(View.INVISIBLE);
iv_30.setVisibility(View.INVISIBLE);
iv_31.setVisibility(View.INVISIBLE);
iv_32.setVisibility(View.INVISIBLE);
iv_33.setVisibility(View.INVISIBLE);
iv_34.setVisibility(View.INVISIBLE);
iv_35.setVisibility(View.INVISIBLE);
iv_36.setVisibility(View.INVISIBLE);
iv_37.setVisibility(View.INVISIBLE);
iv_38.setVisibility(View.INVISIBLE);
iv_39.setVisibility(View.INVISIBLE);
iv_40.setVisibility(View.INVISIBLE);
iv_41.setVisibility(View.INVISIBLE);
iv_42.setVisibility(View.INVISIBLE);
iv_43.setVisibility(View.INVISIBLE);
iv_44.setVisibility(View.INVISIBLE);
iv_45.setVisibility(View.INVISIBLE);
iv_46.setVisibility(View.INVISIBLE);
iv_47.setVisibility(View.INVISIBLE);
iv_48.setVisibility(View.INVISIBLE);
iv_49.setVisibility(View.INVISIBLE);
iv_50.setVisibility(View.INVISIBLE);
iv_51.setVisibility(View.INVISIBLE);
iv_52.setVisibility(View.INVISIBLE);
iv_53.setVisibility(View.INVISIBLE);
iv_54.setVisibility(View.INVISIBLE);
iv_55.setVisibility(View.INVISIBLE);
iv_56.setVisibility(View.INVISIBLE);
iv_57.setVisibility(View.INVISIBLE);
iv_58.setVisibility(View.INVISIBLE);
iv_59.setVisibility(View.INVISIBLE);
iv_60.setVisibility(View.INVISIBLE);
Put your iv_xx objects in a list or array, then use a loop to iterate through the array:
ArrayList<IVType> ivList = new ArrayList<>();
// use ivList.add() to add each object to the list.
for (IVType iv: ivList)
{
iv.setVisibility(View.INVISIBLE);
}
First create a array of size N of type img(Your object).
N—> will be the number of images.
img[] a=new img[N];
Here, we are creating a array of type img with size N.
Now you need to add all your 50 image object to this array using for loop
Then loop through the array and set the property like below.
for(int i =0; i<N;i++){
a[i].setVisibilty(View.INVISIBLE);
}
Let's assume this is your POJO
public class ImageView {
private View visibility;
public ImageView(View view) {
visibility = view;
}
public View getVisibility() {
return visibility;
}
public void setVisibility(View visibility) {
this.visibility = visibility;
}
enum View {
VISIBLE, INVISIBLE
}
}
Also, consider the scenario that you have already ImageView list that contains 60 image instances.
List<ImageView> imageViews = new ArrayList<>();
//instead of creating 60 beans separately and adding it to list you can just follow the below code
IntStream.range(0, 60).forEach(e -> {
imageViews.add(new ImageView(ImageView.View.INVISIBLE));
});
//you can manipulate the ImageView instances as below
//imageViews.forEach(e -> e.setVisibility(ImageView.View.INVISIBLE));
//As per the requirement you can convert the list into array as below. There is no need to mention the pre-sized array (new ImageView[imageViews.size()]) here
ImageView[] imageViewsArr = imageViews.toArray(new ImageView[0]);
//If you want to make a arrays of first 15 instances then you can limit the size to 15 (your question mentioned just 15 imageViews to 1 array)
//ImageView[] imageViewsArr = imageViews.stream().limit(15).toArray(new ImageView[0]);
Newbie. I'm coding a quiz app full code on Github that loads an arrayList with four arguments:
question
image (from drawables)
key answer
possible answers presented in a radioGroup (sub-arrayList)
from the strings.xml as below
...
<string name="questionOne">Who is the "Modern Love" rock star singer?</string>
<string name="answerOne">David Bowie</string>
<string-array name="celebrityOne">
<item>Jaimie Hendrix</item>
<item>David Bowie</item>
<item>Jim Morrison</item>
<item>Elvis Presley</item>
</string-array>
...
Below is how the arguments are loaded in MainActivity (The third argument is a sub-arraylist)
ArrayList<Object> arrayList = new ArrayList<>();
loaddata()
...
public void loadData() {
arrayList.add(new Quiz(getResources().getString(R.string.questionOne),
getResources().getDrawable(R.drawable.celebrity_one_image, null),
new ArrayList<>(Arrays.asList(getResources().getStringArray(R.array.celebrityOne))),
getResources().getString(R.string.answerOne)));
arrayList.add(new Quiz(getResources().getString(R.string.questionTwo),
getResources().getDrawable(R.drawable.celebrity_two_image, null),
new ArrayList<>(Arrays.asList(getResources().getStringArray(R.array.celebrityTwo))),
getResources().getString(R.string.answerTwo)));
...
}
The issue is after N iterations, the sub-arrayList starts repeating itself (See image below).
Also I think maybe the source of the problem is in the Adapter, where for each string in sub-array is assigned to a radioButton;
void createRadioButtons(String[] arrayAnswer) {
if (mRadioGroup.getChildAt(0) != null)
return;
for (int i = 0; i < arrayAnswer.length; i++) {
mRadioGroup.addView(createRadioButtonAnswerAndSetOnClickListener(arrayAnswer[i]));
}
}
RadioButton createRadioButtonAnswerAndSetOnClickListener(String string) {
RadioButton radioButton = new RadioButton(mContext);
radioButton.setText(string);
radioButton.setOnClickListener(this);
return radioButton;
}
My situation might be similar to this but I have no static fields and arrayList is initialized as new so no need to clear().
From Documentation:
The RecyclerView creates only as many view holders as are needed to display the on-screen portion of the dynamic content, plus a few extra. As the user scrolls through the list, the RecyclerView takes the off-screen views and rebinds them to the data which is scrolling onto the screen.
This means RecyclerView reuses already created view holders when you are scrolling it(that is why your data repeats), and you must repopulate views with new data. So, instead of returning from createRadioButtons method, when mRadioGroup.getChildAt(0) != null, you must change RadioButtons texts to your new data from arrayAnswer.
in your adapter just change this:
if (mRadioGroup.getChildAt(0) != null)
return;
To this:
if (mRadioGroup.getChildAt(0) != null)
mRadioGroup.removeAllViews();
At some moment your adapter, began to reuse view holders which were created at the top of the recyclerView, but it was already filled with data, so when you call return, you just leave your old data, while you need to delete it and then add new data...
It is hard to explain so I'll use an example:
#Override
public void start(Stage primaryStage) throws Exception
{
final VBox vbox = new VBox();
final Scene sc = new Scene(vbox);
primaryStage.setScene(sc);
final TableView<Person> table = new TableView<>();
final TableColumn<Person, String> columnName = new TableColumn<Person, String>("Name");
table.getColumns().add(columnName);
final ObservableList<Person> list = FXCollections.observableArrayList();
list.add(new Person("Hello"));
list.add(new Person("World"));
Bindings.bindContent(table.getItems(), list);
columnName.setCellValueFactory(new PropertyValueFactory<>("name"));
vbox.getChildren().add(table);
final Button button = new Button("test");
button.setOnAction(event ->
{
final Person removed = list.remove(0);
removed.setName("Bye");
list.add(0, removed);
});
vbox.getChildren().add(button);
primaryStage.show();
}
public static class Person
{
private String name = "";
public Person(String n)
{
name = n;
}
public String getName()
{
return name;
}
public void setName(String n)
{
name = n;
}
}
In this example, I show a TableView with a single column named "Name". Running this sample code, you will get two rows: first row with "Hello" in "Name" column; and second row with "World" in "Name" column.
Additionally, there is a button, this button removes the first Person object from the list, then makes some changes to the object, then adds it back in at the same index. Doing so would cause any ListChangeListener added to the ObservableList to be triggered, and I have tested this to be true.
I would expect the row with "Hello" to be replaced with "Bye", but it seems like the TableView continues to show "Hello". If I used a TimeLine to add delay before I add the removed Person object back to the list, it would change to "Bye".
final Timeline tl = new Timeline(new KeyFrame(Duration.millis(30), ae -> list.add(0, removed)));
tl.play();
Is there something weird with the API? Is there any way to do this without this problem?
This is essentially expected behavior.
Note that (and I'm guessing you are trying to work around this issue), if you simply called
list.get(0).setName("Bye");
which has the same effect in terms of the underlying data, the table would not update as it has no way of being notified that the String field name in the element of the list has changed.
The code
Person removed = list.remove(0);
removed.setName("Bye");
list.add(0, removed);
is really equivalent to list.get(0).setName("Bye");: you just temporarily remove the item from the list before changing it, and then add it back. As far as the list is concerned, the net result is the same. I guess you are doing this in the hope that removing and replacing the item from the list will persuade the table to notice the state of the item has changed. There's no guarantee this will be the case. Here is what's happening:
The binding between your two lists:
Bindings.bindContent(table.getItems(), list);
works like any other binding: it defines how to get the value of the binding (the elements of list), and marks the data as invalid if list is invalidated at any time. The latter happens when you add and remove elements from list.
The TableView will not perform layout every time the binding to the list changes; instead, when then binding is invalidated (add or remove an element), then the table view marks itself as potentially needing to be redrawn. Then, on the next rendering pulse, the table will check the data and see if it really needs to be redrawn, and re-render if needed. There are obvious performance-saving features of this implementation.
So what happens with your code is that an item is removed from the list, causing the binding to be marked as invalid. The item is then changed (by calling setName(...)), and the same item is then added back into the list at the same position. This also causes the binding to be marked as invalid, which has no effect (it is already invalid).
No rendering pulse can occur between the removal and re-addition of this element. Consequently, the first time the table actually looks at the changes that were made to the list has to be after the entire remove-change-add process. At that point, the table will see that the list still contains the exact same elements in the exact same order that it previously contained. (The internal state of one of the elements has changed, but since this is not an observable value - not a JavaFX property - the table is unaware of this.) Consequently, the table sees no changes (or sees that all the changes have cancelled each other out), and doesn't re-render.
In the case where you add the pause, then a rendering frame (or two) occurs between the removal of the item and its re-addition. Consequently, the table actually renders one or two frames without the item, and when it is added back in, it adds it back and renders the current value. (You might, possibly, be able to make the behavior unpredictable, by pausing for 16 or 17 milliseconds, which is right on the cusp of the time for one rendering frame.)
It's not clear what you really intend to do. If you are trying to persuade the table to update without using JavaFX properties, you can do
list.get(0).setName("Bye");
table.refresh();
though this is not a very satisfactory solution.
Note too that
list.remove(0);
list.add(0, new Person("Bye"));
will also work (since now the added element is not the same as the removed element).
The better approach is to implement your model class with JavaFX properties:
public static class Person
{
private final StringProperty name = new SimpleStringProperty("");
public Person(String n)
{
setName(n);
}
public StringProperty nameProperty() {
return name ;
}
public final String getName()
{
return nameProperty().get();
}
public final void setName(String n)
{
nameProperty().set(n);
}
}
and then simply calling
list.get(0).setName("Bye");
will update the table (because the cell will be observing the property).
private void displayGroupsInRanking() {
for (int i = 0; i < 4; i++)
{
RankingANames.setItems(FXCollections.observableArrayList(groupModel.getListA().get(i).getName()));
System.out.println(RankingANames);
}
RankingBNames.setItems(FXCollections.observableArrayList(groupModel.getListB()));
RankingCNames.setItems(FXCollections.observableArrayList(groupModel.getListC()));
RankingDNames.setItems(FXCollections.observableArrayList(groupModel.getListD()));
}
I´m trying to, to get a specific attribute from an arraylist into a new arraylist. This works fine, but the listview only shows one object?
[The output1
the Code
I'll not rewrite code from your screen to show you the right way to do this but I can tell you what is wrong here.
On every iteration you are creating new collection with exactly one item and then you are using it as items list for table.
That's clearly wrong.
To solve it, you have to first prepare full list of items and then pass it to setItems method.
Well it appears I have been stumped by one of the simplest ListView implementations out there. For a few days I have found it impossible to properly reallocate a JavaFX ListView. I am working on making an EntityView completely dynamic, being able to remove elements whenever needed through a ContextMenu. So I have a ListView that is populated by an ArrayList, which we will refer to as "renderable". When you select "Remove" in the context menu, it removes the Entity from the renderable list, which also happens to be the "value" of the List Cell on whom I right clicked. Afterwards, I wish to refresh the ListView and remove now the nonexistent cell. So by creating a new ObservableList with the new renderable list (which removes the correct entity and that works fine), I set the items in the ListView, which does jack shit. I can set the list to null in this case, which removes all the elements. But I cannot reset the list with the new array and it remove the now missing entity. Somebody point me in the right direction please!
When I use the method I stated above, it removes it from the list, but not visually. There becomes an unusable cell at the bottom of the list, which has its name.
public void createContextMenu(final Entity curr, MouseEvent me){
MenuItem[] items = {new MenuItem("EDIT TYPE"), new MenuItem("REMOVE")};
ContextMenu menu = new ContextMenu(items);
menu.show(list, me.getScreenX(), me.getScreenY());
items[1].setOnAction(new EventHandler<ActionEvent>(){
public void handle(ActionEvent arg0) {
CanvasTab tab = (CanvasTab) core.canvasTabbedPane.getSelectionModel().getSelectedItem();
Kanvas k = tab.canvas;
k.renderable.remove(curr);
System.out.println(k.renderable);
k.redraw();
EntityView.this.list.getItems().remove(curr);
ObservableList<Entity> temp = FXCollections.observableList(k.renderable);
EntityView.this.list.setItems(temp);
}
});
}
This is the context menu: