So when a user makes a purchase they get a new texture available that will replace the original one. I´ve sort of pulled it of. But I also want the user to ba able to replace back to the original one if the wish to do so.
At the moment the user makes a purchase and the texture changes fine. But as I store that change in preferences it makes it permanent. So when I try to press the button to change to the original stone, which simply calls "setStone(...)" , nothing happends.
Anyone that can see where I´ve gone wrong?
P.S the code is not copied straight of, just cut in the important parts.
ObjectMap<Integer, Texture> screenMap = new ObjectMap<Integer, Texture>();
ObjectMap<String, Boolean> mTexturesStatus = new ObjectMap<String, Boolean>();
public void setStone1() {
stoneImage = new Image(screenMap.get(0));
}
public void setStone(int screenId) {
stoneImage = new Image(screenMap.get(screenId));
}
screenMap.put(0, sdStone);
screenMap.put(1, stone_3);
mTexturesStatus.put("stone1", stone1);
mTexturesStatus.put("stone2", stone2);
btnArrow.addListener(new ChangeListener() {
#Override
public void changed(ChangeEvent event, Actor actor) {
game.setScreen(0);
//Saves the entered text.
Preferences prefs = Gdx.app.getPreferences("preferences");
prefs.putString("textField", textField.getText());
prefs.putString("textArea", textArea.getText());
prefs.getBoolean("stoneOne");
prefs.getBoolean("stoneTwo");
prefs.flush();
}
});
//screenMap.put(prefs.getInteger("stoneOne", 0), sdStone);
//screenMap.put(prefs.getInteger("stoneTwo", 1), stone_3);
setStone1();
if(stone1 = true) {
setStone(0);
prefs.putBoolean("stoneOne", true);
}
if(stone2 = true) {
setStone(1);
prefs.putBoolean("stoneTwo", true);
}
}
Related
I have an issue with my TableView and its items. I have created a small Dialog window to display warnings about my app, and inside the Dialog I have a TableView which displays the name of the warning and some information about it upon clicking on a button.
I have created a WarningUtil class (Singleton pattern) just to open / close the Dialog. The relevant code follows.
The constructor of the WarningUtil class (called once only) :
private WarningUtil(RootCtrl rootCtrl) {
this.rootCtrl = rootCtrl;
warnings = new HashMap<>();
setupWarningCallbacks(); // not relevant
setupTable();
setupColumns(); // not relevant
setupDialog();
}
The function managing the construction of the Dialog :
private void setupTable() {
// create the content pane
content = new AnchorPane(); // class variable - reference needed for further uses
content.setPrefSize(480, 240);
// create the root nodes of the view (table + 2 columns)
warningTable = new TableView<>(); // class variable - reference needed for further uses
warnDescriptionCol = new PTableColumn<>(); // class variable - reference needed for further uses
warnDetailsCol = new PTableColumn<>(); // class variable - reference needed for further uses
// settings anchors to keep the ration between dialog <-> table
AnchorPane.setBottomAnchor(warningTable, 15.0);
AnchorPane.setTopAnchor(warningTable, 15.0);
AnchorPane.setLeftAnchor(warningTable, 15.0);
AnchorPane.setRightAnchor(warningTable, 15.0);
// setting up the columns
warnDescriptionCol.setText(i18n("label.desc"));
warnDetailsCol.setText(i18n("label.details"));
warnDescriptionCol.setPercentageWidth(0.7);
warnDetailsCol.setPercentageWidth(0.3);
warnDescriptionCol.setResizable(false);
warnDetailsCol.setResizable(false);
// adding nodes to containers
warningTable.getColumns().addAll(warnDescriptionCol, warnDetailsCol);
content.getChildren().add(warningTable);
}
The function used to create the Dialog and set the content :
private void setupDialog() {
// creation and saving of the dialog in a variable reused later
warningDialog = DialogFactory.getInstance(rootCtrl.getPrimaryStage()).createWarningDialog();
warningDialog.getDialogPane().setContent(content);
warningDialog.getDialogPane().getScene().getWindow().sizeToScene();
}
// The DialogFactory function creating the dialog
public Dialog createWarningDialog(){
CustomDialog dialog = new CustomDialog(rootStage);
dialog.setTitle(i18n("warning.description"));
ButtonType cancelBt = new ButtonType(i18n("button.close"), ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().add(cancelBt);
return dialog.setupLayout();
}
The Main class is in charge of loading the warnings (stored in a .json file and deserialized upon starting the app). For now, the file only contains one entry.
When I click on my Warning button, the following function is called :
public void showWarnings() {
warningTable.getItems().clear(); // BP
warningTable.setItems(FXCollections.observableArrayList(warnings.values()));
warningDialog.showAndWait();
}
What happens is the following : When I have only one entry in my .json file, the first time I click on the button, only one warning is shown. If I click a second time, a second entry appears (the same) which should not be possible because of the following reasons :
Logic constraint : warnings.values() comes from an HashMap where the key is the type of the warning (WarningType class) > Not possible to have two identical keys
Debugging : When I set a breakpoint at "//BP", I clearly see that the warningTable has one item, and after clear the number of items is zero
Debugging : Still with the same breakpoint, I also check that warnings.values() has only one item, which is the case
After five clicks on the button, the Dialog clearly shows something is bugging.
More surprisingly, when I add a second warning (different from the first one, another type), the problem does not occur : No duplicates, warnings are correctly displayed and no matter how many times I open the window.
My question is : Could that be that the way I am creating this warning dialog leads to uncommon errors ? If so, why isn't it the case with two warnings ?
EDIT Include of the cellFactories / cellValueFactories
private void setupColumns() {
warnDescriptionCol.setCellFactory(new Callback<TableColumn<CustomWarning, String>, TableCell<CustomWarning, String>>() {
#Override
public TableCell<CustomWarning, String> call(TableColumn<CustomWarning, String> param) {
TableCell<CustomWarning, String> cell = new TableCell<CustomWarning, String>() {
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
Label label = new Label(item);
setGraphic(label);
}
}
};
return cell;
}
});
warnDetailsCol.setCellFactory(new Callback<TableColumn<CustomWarning, CustomWarning>, TableCell<CustomWarning, CustomWarning>>() {
#Override
public TableCell<CustomWarning, CustomWarning> call(TableColumn<CustomWarning, CustomWarning> param) {
TableCell<CustomWarning, CustomWarning> cell = new TableCell<CustomWarning, CustomWarning>() {
#Override
protected void updateItem(CustomWarning item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
Button button = new Button(i18n("button.view"));
button.getStyleClass().add("save");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
showWarning(item);
}
});
setGraphic(button);
}
}
};
return cell;
}
});
warnDescriptionCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<CustomWarning, String>, ObservableValue<String>>() {
TableViewObjectWrapper<CustomWarning, String> wrapper = new TableViewObjectWrapper<CustomWarning, String>() {
#Override
public String getData() {
return getModel().getTitle();
}
};
#Override
public ObservableValue<String> call(TableColumn.CellDataFeatures<CustomWarning, String> param) {
return new ReadOnlyObjectWrapper<>(wrapper.setModel(param.getValue()).getData());
}
});
warnDetailsCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<CustomWarning, CustomWarning>, ObservableValue<CustomWarning>>() {
TableViewObjectWrapper<CustomWarning, CustomWarning> wrapper = new TableViewObjectWrapper<CustomWarning, CustomWarning>() {
#Override
public CustomWarning getData() {
return getModel();
}
};
#Override
public ObservableValue<CustomWarning> call(TableColumn.CellDataFeatures<CustomWarning, CustomWarning> param) {
return new ReadOnlyObjectWrapper<>(wrapper.setModel(param.getValue()).getData());
}
});
}
You have to clear your cells in the cell factory if the cell is empty, as explained in the documentation:
It is very important that subclasses of Cell override the updateItem method properly, as failure to do so will lead to issues such as blank cells or cells with unexpected content appearing within them. Here is an example of how to properly override the updateItem method:
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
setText(item.toString());
}
}
Note in this code sample two important points:
We call the super.updateItem(T, boolean) method. If this is not done, the item and empty properties are not correctly set, and you are likely to end up with graphical issues.
We test for the empty condition, and if true, we set the text and graphic properties to null. If we do not do this, it is almost guaranteed that end users will see graphical artifacts in cells unexpectedly.
Since the cells are reused, you have to clear the graphic if it has become empty, not just set it if it's not.
Using the fantastic Native Google Maps cn1lib, I'm attempting to place markers on the map, first one representing the user, then another slew of them representing objects from my database. The first marker always plunks down fine, and I can relocate it (by erasing it and creating a new one in the new position), but the subsequent markers don't show on the map. I get no errors in the simulator or on an Android device, but on both the additional markers don't show. The code looks like this:
class MyClass{
LinkedList<MapContainer.MapObject> towerMarkers;
MapContainer towerMap;
public void displayTowers(ArrayList<HashMap<String,Object>> towers){
if(towerMarkers == null){
towerMarkers = new LinkedList<>();
}else{
for(MapContainer.MapObject marker: towerMarkers){
towerMap.removeMapObject(marker);
}
towerMarkers.clear();
}
for(HashMap<String, Object> obj: towers){
towerMarkers.add(towerMap.addMarker(EncodedImage.createFromImage(FontImage.createMaterial(
FontImage.MATERIAL_SETTINGS_INPUT_ANTENNA, cellTowerMarker, 3), false),
new Coord(Double.parseDouble((String)obj.get("latitude")), Double.parseDouble((String)obj.get("longitude"))),
"", "", null));
}
}
...
The code that works is very similar:
towerMap.addTapListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent evt) {
int x = evt.getX();
int y = evt.getY();
Coord tapCoord = towerMap.getCoordAtPosition(x, y);
if(myLocationMarker != null){
towerMap.removeMapObject(myLocationMarker);
}
myLocationMarker = towerMap.addMarker(EncodedImage.createFromImage(FontImage.createMaterial(FontImage.MATERIAL_LOCATION_ON, userLocationMarker, 4),
false), tapCoord, null, null, null);
findLongitudeField(f).setText(Double.toString(tapCoord.getLongitude()));
findLatitudeField(f).setText(Double.toString(tapCoord.getLatitude()));
findBtnUseChosenLocation(f).setEnabled(true);
}
});
The only odd thing that I noticed is when I inspect the map markers, they all (including the one that appears) seem to have coordinates in total different system. Instead of (35.789..., 32.1233...), they're more like (3897665.88999, 4277888.988884), but since the one that shows up is also like that, it doesn't seem to be a problem.
Any help will be appreciated!
I am trying to parse some pois from a xml download from a server and I saw that it is done after the program continues in the main thread. I haven't found a way to solve it because I need it.
using System.Threading;
namespace XML_Parser
{
class XMLParserPOI_Wiki
{
private static XMLParserPOI_Wiki objSingle = new XMLParserPOI_Wiki();
public static XMLParserPOI_Wiki ObjSingle
{
get { return objSingle; }
set { objSingle = value; }
}
private List<POI> places;
public List<POI> Places
{
get { return places; }
}
private XMLParserPOI_Wiki()
{
}
public void parseWikitude(string url)
{
places = new List<POI>();
WebClient wc = new WebClient();
wc.DownloadStringCompleted += HttpsCompleted;
wc.DownloadStringAsync(new Uri(url));
}
private void HttpsCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
XDocument xdoc = XDocument.Parse(e.Result, LoadOptions.None);
XNamespace ns = "http://www.opengis.net/kml/2.2";
XNamespace ns2 = "http://www.openarml.org/wikitude/1.0";
var placemarkers = xdoc.Root.Descendants(ns + "Placemark");
places =
(from query in xdoc.Root.Descendants(ns + "Placemark")
select new POI
(
...
)).ToList();
System.Diagnostics.Debug.WriteLine("Lista");
System.Diagnostics.Debug.WriteLine(places.Count);
}
}
}
}
In my main class:
XMLParserPOI_Wiki parserXML = XMLParserPOI_Wiki.ObjSingle;
parserXML.parseWikitude("http://myurl.php");
System.Diagnostics.Debug.WriteLine("Lista de pois");
System.Diagnostics.Debug.WriteLine(parserXML.Places.Count);
for (int i = 0; i < parserXML.Places.Count; i++)
{
System.Diagnostics.Debug.WriteLine(parserXML.Places[i].getName());
}
It prints Lista de POis and 0, before Lista and X (number of pois)
I guess I should freeze main thread but I tried a couple of times with some examples and they didn't work.
Can you point me to any tutorial about this? More than get an answer I want to understand how to deal with this kind of operations
First of all, you don't want to block (freeze) the UI thread EVER!
This is called asynchronous programming. There are two things you can do to solve your problem (I recommend option 2!):
Use the classic callback model. You basically call some long operation on a background thread and give a function to it, to execute when the long operation is done. Here's how to do it in your case.
At the end of the HttpsCompleted method, invoke what you need on the UI Thread using:
Deployment.Current.Dispatcher.BeginInvoke(delegate() {
//The code here will be invoked on the UI thread
});
If you want to make the parseWikitude method reusable, you should pass an Action to it. This way you can call it from multiple places and tell it what to do on the UI thread when the parsing is done. Something like this:
public void parseWikitude(string url, Action callback) {
places = new List<POI>();
WebClient wc = new WebClient();
wc.DownloadStringCompleted += HttpsCompleted;
wc.DownloadStringAsync(new Uri(url), callback);
}
private void HttpsCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
...
var callback = (Action)e.UserState;
Deployment.Current.Dispatcher.BeginInvoke(callback);
}
}
//And then when you use it, you do it like that
parserXML.parseWikitude("http://myurl.php", delegate() {
//The code here will be executed on the UI thread, after the parsing is done
});
Use the (rather) new asnyc pattern in .NET. You should read about this, as it is one of the best features of .NET if you ask me. :) It basically does the callback thing automatically and makes the code a lot easier to read/maintain/work-with. Once you get used to it, that is.
Here's an example:
public Task<List<POI>> parseWikitude(string url) {
TaskCompletionSource<List<POI>> resultTaskSource = new TaskCompletionSource<List<POI>>();
WebClient wc = new WebClient();
wc.DownloadStringCompleted += HttpsCompleted;
wc.DownloadStringAsync(new Uri(url), resultTaskSource);
return resultTaskSource.Task;
}
private void HttpsCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
//If needed, run the code here in a background thread
//...
var resultTaskSource = (TaskCompletionSource<List<POI>>)e.UserState;
resultTaskSource.SetResult(places);
}
}
//And when you need to use it, do it like that (note, this must be invoked in an async method!)
var places = await parser.parseWikitude("http://myurl.php");
//The code here will be executed on the same thread when the parsing is done, but the thread will not be blocked while the download is happening.
So, these are the two ways you can handle it. Option one is old-school, classic and easy. Option two is the new and cool way of doing async stuff. It really is a must-know. Simplifies a lot of things once you get used to it.
P.S. Sorry if I got carried away. :D
I am new in the development of WP8. I have been following a online course for a couple of weeks and the second task of the course was to develop a app to show the weather, some news and photos related to the city.
So far, I have develop the app following the MVVM pattern using the Panorama control as the conteiner for the differents contents I need to show.
To no longer this, the problem I facing is at the moment to display the xml data that is retrieve from the webservices.
The XAML is:
<phone:panorama x:Name="myPanorama"
DataContext = {Binding Source="WeatherViewModel"}>
<PanoramaItem header="MyWeather">
<Textblock x:name="txtCity"
Text = {Binding Weather.City}
</Textblock>
</PanoramaItem>
<panoramaItem header="Config">
<Text x:Name="txtGetCity"/>
<Button x:Name="btnGetCity"
Command={Binding GetWeatherCommand}/>
</panoramaItem>
</phone:panorama>
My ViewModel:
public class WeaterViewModel : NotificationEnableObject
{
private Weather _currentWeather;
public Weather GetCurrentWeather
{
get
{
if (_currentWeather == null)
_currentWeather = new Weather();
return _currentWeather;
}
set { _currentWeather = value;
OnPropertyChanged("GetCurrentWeather");
}
}
//Constructor ServiceModel serviceModel = new ServiceModel();
public WeatherViewModel()
{
serviceModel.GetWeatherCompleted += (s, a) =>
{
_currentWeather = new Clima();
_currentWeather.City= a.Results[0].City;
_currentWeather.tempC = a.Results[0].tempC;
};
getWeatherCommand = new ActionCommand(null);
}
ActionCommand getWeatherCommand; // ActionCommand derivied from ICommand
public ActionCommand GetWeatherCommand
{
get
{
if (getWeatherCommand!= null)
{
getWeatherCommand = new ActionCommand(() =>
{
//Call the Service who retrieved the data
});
}
return getWeatherCommand;
}
}
}
The Weather specified is a public class which contain the City property. I have tried using an IObservableCollention as well howerver, the result is the same :-(
As you can see in the panorama control I have 2 sections. The one where I write the city I wanna see and the section where I show the information I get from the web services.
Any clue, or help would be very appreciate
Regards!
Ok, I think that is an easy fix.
You're setting GetCurrentWeather this way:
_currentWeather = new Clima();
_currentWeather.City= a.Results[0].City;
_currentWeather.tempC = a.Results[0].tempC;
This is not firing the PropertyChanged event. Change it to:
GetCurrentWeather= new Clima();
GetCurrentWeather.City= a.Results[0].City;
GetCurrentWeather.tempC = a.Results[0].tempC;
and you should be fine.
so I've been looking for this for a week now and reading though every problem similar but none seemed to ask the same problem as mine exactly(try reverse engineering other solution similar to what I want with no success.
explained caveman style: I'm trying to create list using Metadata.
I open with a multi dialog and select more than one mp3
I put the file in an ArrayList<File>
I loop though the files with an enhanced for loop and extract metadata using a media variable
The info for the metadata ( like "artist") is what i want to save in an ArrayList for example
the problem is that the listener only works way after the enhanced loop has finished which results in
ArrayList<String> having one object with nothing in it
here is a sample:
ArrayList<String> al;
String path;
public void open(){
files=chooser.showOpenMultipleDialog(new Stage());
for( File f:files){
path=f.getPath();
Media media = new Media("file:/"+path.replace("\\", "/").replace(" ", "%20"));
al= new ArrayList<String>();
media.getMetadata().addListener(new MapChangeListener<String, Object>() {
public void onChanged(Change<? extends String, ? extends Object> change) {
if (change.wasAdded()) {
if (change.getKey().equals("artist")) {
al.add((String) change.getValueAdded());
}
}
}
});
}//close for loop
//then i want to see the size of al like this
system.out.println(al.size());
//then it returns 1 no matter how much file i selected
//when i system out "al" i get an empty string
the other way to read a media source metadata with adding a listener is extract that information in the mediaplayer .setOnReady(); here is an example part of the java controller class
public class uiController implements Initializable {
#FXML private Label label;
#FXML private ListView<String> lv;
#FXML private AnchorPane root;
#FXML private Button button;
private ObservableList<String> ol= FXCollections.observableArrayList();
private List<File> selectedFiles;
private final Object obj= new Object();
#Override
public void initialize(URL url, ResourceBundle rb) {
assert button != null : "fx:id=\"button\" was not injected: check your FXML file 'ui.fxml'.";
assert label != null : "fx:id=\"label\" was not injected: check your FXML file 'ui.fxml'.";
assert lv != null : "fx:id=\"lv\" was not injected: check your FXML file 'ui.fxml'.";
assert root != null : "fx:id=\"root\" was not injected: check your FXML file 'ui.fxml'.";
// initialize your logic here: all #FXML variables will have been injected
lv.setItems(ol);
}
#FXML private void open(ActionEvent event) {
FileChooser.ExtensionFilter extention= new FileChooser.ExtensionFilter("Music Files", "*.mp3","*.m4a","*.aif","*.wav","*.m3u","*.m3u8");
FileChooser fc= new FileChooser();
fc.setInitialDirectory(new File(System.getenv("userprofile")));
fc.setTitle("Select File(s)");
fc.getExtensionFilters().add(extention);
selectedFiles =fc.showOpenMultipleDialog(root.getScene().getWindow());
if(selectedFiles != null &&!selectedFiles.isEmpty()){
listFiles();
}
}
/**
* Convert each fie selected to its URI
*/
private void listFiles(){
try {
for (File file : selectedFiles) {
readMetaData(file.toURI().toString());
synchronized(obj){
obj.wait(100);
}
}
} catch (InterruptedException ex) {
}
System.gc();
}
/**
* Read a Media source metadata
* Note: Sometimes the was unable to extract the metadata especially when
* i have selected large number of files reasons i don't known why
* #param mediaURI Media file URI
*/
private void readMetaData(String mediaURI){
final MediaPlayer mp= new MediaPlayer(new Media(mediaURI));
mp.setOnReady(new Runnable() {
#Override
public void run() {
String artistName=(String) mp.getMedia().getMetadata().get("artist");
ol.add(artistName);
synchronized(obj){//this is required since mp.setOnReady creates a new thread and our loopp in the main thread
obj.notify();// the loop has to wait unitl we are able to get the media metadata thats why use .wait() and .notify() to synce the two threads(main thread and MediaPlayer thread)
}
}
});
}
}
the few changes that have made is used an ObservableList to store the artist name from the metadata
in the code you will find this
synchronized(obj){
obj.wait(100);
}
I do this because the mediaplayer .setOnReady() creates a new thread and the loop is in the main application thread, The loop has to wait for some time before the other thread is created and we are able to extract the metadata, and in the .setOnReady() there is a
synchronized(obj){
obj.notify;
}
to wake up the main thread hence the loop is able to move to the next item
I admit that this may not be the best solution to do this but am welcomed to anyone who has any better way on how to read JavaFx media metadata from a list of files
The full Netbeans project can be found here https://docs.google.com/file/d/0BxDEmOcXqnCLSTFHbTVFcGIzT1E/edit?usp=sharing
plus have created a small MediaPlayer Application using JavaFX which expolits use of the metadata https://docs.google.com/file/d/0BxDEmOcXqnCLR1Z0VGN4ZlJkbUU/edit?usp=sharing
You can use the following function to retrieve the metadata for a given Media object:
public static void initializeMetaData(Media media) {
final Ref<Boolean> ready = new Ref<>(false);
MediaPlayer mediaPlayer = new MediaPlayer(media);
mediaPlayer.setOnReady(() -> {
synchronized (ready) {
ready.set(false);
ready.notify();
}
});
synchronized (ready) {
if (!ready.get()) {
try {
ready.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
However, do not call initializeMetaData from a JavaFX thread, otherwise the thread runs into a deadlock.
PS: It's really ridiculous that one has to build such a workaround. I hope that in future Media will provide an initialize() method which does this job.
My solution to that issue was this:
public class MediaListener implements MapChangeListener<String, Object>
{
public String title = null;
public String artist = null;
public String album = null;
private final Consumer<MediaListener> handler;
private boolean handled = false;
public MediaListener(Consumer<MediaListener> handler)
{
this.handler = handler;
}
#Override
public void onChanged(MapChangeListener.Change<? extends String, ?> ch)
{
if (ch.wasAdded())
{
String key = ch.getKey();
switch (key)
{
case "title":
title = (String) ch.getValueAdded();
break;
case "artist":
artist = (String) ch.getValueAdded();
break;
case "album":
album = (String) ch.getValueAdded();
break;
}
if (!handled && title != null && artist != null && album != null)
{
handler.accept(this);
handled = true;
}
}
}
}
It may not be the best way but it's way cleaner than creating a new MediaPlayer per file.
Example usage:
Media media = Util.createMedia(path);
media.getMetadata().addListener(new MediaListener((data) ->
{
// Use the data object to access the media
}));