Accessing JavaFX UI elements from a different thread? - javafx

If my controller class is
public class FXMLDocumentController implements Initializable {
#FXML
private TextArea msgArea;
public void initialize(URL url, ResourceBundle rb) {
someThread.start();
}
}
How can I change the value of my TextArea from the thread?
EDIT: I used tasks to to solve this problem. Thanks to everyone who tried to help.

Whenever needing to update a UI element from a thread, you must get the JavaFX UI thread to do so. Attempting to update an element from a different thread may lead to an exception, but could also lead to some unexpected behaviour.
Fortunately, JavaFX includes a useful way to do this. Simply add this in the code that runs on your separate thread:
Platform.runLater(() -> {
msgArea.setText("Your text");
});
It is better to use a task or service, because those provide in-built means of updating UI elements. For example, a task allows one to call updateMessage("...") or updateProgress("..."), which updates a bound element without you even needing to call the UI thread.

Related

OnNavigatedTo or Initialize too late for binding

I'm new to Prism with Xamarin.Forms. I've implemented the ViewModel approach using the Navigation and Commanding classes. It works just fine but there's just one thing I don't understand. With the AutowireViewModel set to true the ViewModelLocator automatically fills the BindingContext for me and that is sweet. The order of things is not what I expected. First the binding for the properties on the View fire and then the OnNavigatedTo is fired. This means that my init of the properties is already finished by the time I receive the parameters on the View. I can solve this by executing the RaisePropertyChanged. This causes the following:
I'm forced to write RaisePropertyChanged for every property on the ViewModel I want to see on the view with the new Data.
All bindings fire two times. For fast stuff that's not a problem but some are slower.
Data is refreshed after the View has become visible. Not disturbing but it would be nicer to show the finished View with it's data all at once.
All properties must be able to handle null references.
Is there a way to initialize the data in the ViewModel before the binding kicks in?
Actually Prism for Xamarin.Forms has long supported initializing your ViewModels prior to the View being pushed onto the NavigationStack. That said there is an order of operations that has to be taken into consideration. We cannot for instance perform a bunch of operations on a ViewModel and then attach it to a View.
The Order in which things are carried out are as follows:
The View is Created (Anything in the View's ctor is executed)
If you have specifically attached the ViewModelLocator.AutowireViewModel property this will resolve the ViewModel as part of the ctor
If you have not specifically opted out of the ViewModelLocator's Autowire, the Navigation Service will set it for you (after the ctor has completed)
The NavigationService will then call IAutoInitialize/IInitialize/InitializeAsync (for Prism 7.2+... INavigatingAware.OnNavigatingTo in older versions of Prism)
The NavigationService will then push the Page onto the Navigation Stack (note that this may or may not be visible to the user as additional pages may have to be added first when deep linking)
The NavigationService will then call OnNavigatedFrom / OnNavigatedTo (this is where people often report seeing a visible delay due to binding updates.
For an overwhelming number of scenarios if you have correctly initialized your ViewModel this process works exactly as you need. If you have some edge case where you absolutely have to ensure the ViewModel is initialized before it is set then you will need to handle this manually.
public partial class ViewA : ContentPage, IInitialize
{
private ViewAViewModel ViewModel { get; }
public ViewA(ViewAViewModel viewModel)
{
ViewModel = viewModel;
// Explicitly Opt Out of Autowiring
ViewModelLocator.SetAutowireViewModel(this, false);
InitializeComponent();
}
public void Initialize(INavigationParameters parameters)
{
ViewModel.Initialize(parameters);
BindingContext = ViewModel;
}
}

Run logic after window is shown

I have a small program which gets called by another program, downloads/renames some files and closes itself afterwards. I want to show the user the progress with some simple text. My problem is, that the logic of the code runs before the view is completely shown. The window is visible but the content (in this case the sample text) is not.
I have already tried setOnShown() (as seen in my example) or setOnShowning(). I also tried not using a fxml file for the layout. But nothing seems to work.
public void start(Stage primaryStage) throws Exception {
HBox root = new HBox();
Text t = new Text();
t.setText("sample Text");
root.getChildren().add(t);
Scene scene = new Scene(root, 300, 275);
primaryStage.setTitle("FXML Welcome");
primaryStage.setScene(scene);
primaryStage.setOnShown(e -> {
updaterLogic(); //do some work
});
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public void updaterLogic(){
try {
Thread.sleep(10 * 1000);
} catch (Exception e) {
e.printStackTrace();
}
}
I want the view to be completed before running the actual logic. How can i archieve this?
To execute something after a window has been shown you can:
Listen for a WindowEvent.WINDOW_SHOWN event.
See Window.onShown and Window.addEventHandler
Listen to the Window.showing property and react when it changes to true.
Put the code after the call to show().
But not showAndWait() as that method doesn't return until the window is closed (and is illegal to call on the primary stage).
However, how to do this is not your problem (you're already using onShown). Your problem is caused by how you execute the "updater logic". Inside your updaterLogic() method you are calling Thread.sleep (simulating work). Since event handlers are executed on the JavaFX Application Thread that causes said thread to block. While the FX thread is blocked it can't do anything related to the GUI (e.g. layout passes, trigger rendering pulses, process user events, etc...)—thus the frozen window with no content rendered.
Move the "updater logic" onto a background thread so that the FX thread remains free to do its work. For general information about concurrency in Java, see Lesson: Concurrency and the java.util.concurrent package. For JavaFX specifics, see Concurrency in JavaFX, the javafx.concurrent package, and Platform.runLater(Runnable).
Here are two golden rules of JavaFX:
You must never block, nor execute long running work on, the JavaFX Application Thread.
You must always access and modify a live scene graph on the JavaFX Application Thread.
This is documented by Node:
Node objects may be constructed and modified on any thread as long they are not yet attached to a Scene in a Window that is showing. An application must attach nodes to such a Scene or modify them on the JavaFX Application Thread.
Note: Some nodes, such as WebView, must be created on the FX thread; this will be documented as appropriate.

javaFX. Remove all event handlers (filters)

removeEventHandler() is ok, but what if I don't keep reference on handler ?
Can I remove any event handler(filter) by event type or even all handlers from my JavaFX. scene.Node instance? I guess that somewhere a list of handlers existed, and I can traverse it, and remove what I want.
Can I remove any event handler(filter) by event type or even all handlers from my javafx.scene.Node instance?
I don't think you can remove an event handler or filter which you didn't have a reference to originally. You can add extra event filters to filter out processing for events by type or you can set your own event dispatcher on the node and have your custom dispatcher only forward the events you want to the node's standard event dispatcher.
I guess that somewhere a list of handlers existed, and I can traverse it, and remove what I want.
Yes, but that is buried within the private implementation of the Node, so you probably don't want to hack the private Node code to do that.
I came across this question while looking for how to create event handlers that remove themselves. The answer to my question was here, I don't know if it will help you.
javafx have an eventfilter remove itself
Here is an example
EventHandler<MouseEvent> object_clicked=new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
// on click actions here
my_node.removeEventFilter(MouseEvent.MOUSE_CLICKED, this); // at the bottom
}
};
my_node.addEventFilter(MouseEvent.MOUSE_CLICKED, object_clicked); // add the eventhandler to the node

Can you have a loader without fragments

Very basic loader question.
My WIMMOne watch uses Android 2.1 (version 7).
There are no orientation changes, etc. with a watch. The small screen does not have room for any layout changes. So no need to deal with any kind of layout change.
The app I am working on now simply reads from a cursor, and displays an open ended scrolling list. My first app had a fragment and that was a pain. So I decided since I don't need fragments I will do away with the complexities of fragments.
I START WITH:
public class PhoneListActivity extends Activity
implements LoaderManager.LoaderCallbacks <Cursor>;
THEN:
protected void onCreate(Bundle savedInstanceState)
{ super.onCreate(savedInstanceState);
setContentView(R.layout.phone_list_activity);
FINALLY:
getLoaderManager().initLoader(0, null, this);
BUT:
Because it is 2.1 I need to use:
getSupportLoaderManager().initLoader(0, null, this);
BUT: -
That generates compile errors, so I need to use:
public class PhoneListActivity extends FragmentActivity . . . (not just Activity)
BUT: -
It immediately crashes on load in the ContentProvider.
Postings in various sites refer to "Activities and ActivityFragments".
SO: QUESTION 1: Can my main class use "extends FragmentActivity" without setting up a separate fragment (ie: leave it just as an Activity).
QUESTION 2: If not, does that mean that to use a loader I must set up a separate fragment and deal with the issues of fragments?
Many thanks,
Clark
The answer is YES.
I redid part of the program and it now works without using fragments. It has the open ended scrolling being loaded from a cursor and basically the same structure above
I think something in handling my cursor was causing it to crash.
Clark

Android navigation tabs: Restoring fragment view state

I'm trying to understand how to preserve fragment view state when fragments are used within navigation tabs. In my efforts, I've come across two problems that I cannot find any proper solutions to.
I have two tabs, Tab1 and Tab2. Tab1's layout is defined by FragmentA and Tab2's layout is defined by FragmentB.
I've followed the approach given here (edit: documentation has changed since this question was asked).
The first problem: Even though my views have IDs, their states are not fully restored when a fragment is re-attached (after a tab switch rotation). In particular: an EditText with an ID does indeed save its entered text, but it does not save its enabled status. Also, buttons do not save if they are enabled or disabled even though they have IDs. I've found two possible workarounds for this problem:
Use hide()/show() instead of attach()/detach() when switching tabs.
in onPause(), save the current fragment view state in a View instance variable of the fragment via getView(). In onCreateView(Bundle savedInstanceState) check if this field is non-null and if that is the case return the value of this field. This solution seems hacky, and I've been told that it might also introduce a memory leak in my app.
The second problem: Consider the following user interaction:
User starts on Tab1 and does some changes that put the view state of Tab1 in a different state than its default state (and we want the fragment to save this view state through tabswitches and device tilts).
User then goes to Tab2. User then tilts her/his device (still at Tab2).
User then swaps to Tab1 (at the new screen orientation).
Now, the problem is: when the user initially swaps from Tab1 to Tab2, the fragment is detached and thereby its view discarded (even though the fragment instance still lives). When the user then tilts the device, the activity - and thereby both FragmentA and FragmentB associated with it - are destroyed. Since FragmentA at this point does no longer have a view (remember: it was detached), we cannot save the state of its view elements (e.g., what buttons are enabled/disabled) during the call to FragmentA.onSaveInstanceState(Bundle savedInstanceState). How do you recover fragment view state in a situation like this? Is the only viable solution to save every single view element's different status flags as SharedPreferences? This seems way too complicated for such an "everyday job".
Problem 1:
Android does not save your view enabled state by default. It seems only things which are directly influenced by user actions (without additional code) are saved. For a normal View, no information is saved, and for a TextView, of which EditText is a subclass, the entered text is saved (if freezesText is set).
If you want to anything else to be saved, you will have to do it yourself. Here is an question with some answers that show how to implement custom view state saving. You can stick with attach/detach if you follow that approach.
Problem 2:
You are right in that Fragment.onSaveInstanceState(Bundle) can be called after your view has already been destroyed. However, this is not where you should be saving your view state. Android will call View.onSaveInstanceState() right before it destroys your views when detaching a fragment. It saves this state and gives it back to you when you attach the fragment again. This is exactly what happens when you flip between tabs normally with no rotations. Fragment.onSaveInstanceState(Bundle) is not called when detaching. Even if you rotate the device, the view state saved as a result of the detach will persist. If you implement View.onSaveInstanceState() as directed above, your view state will be saved and restored properly, even in the Tab1-Tab2-rotate-Tab1 scenario.
Side note:
The example code in the docs seems to have some problems when you try to rotate. The lifetime of the TabListener is the same as that of the Activity - a new one is created every time you rotate. This means it also loses its internal reference to the fragment, every time you rotate. Added fragments are recreated automatically and so there is no need for the TabListener to try to create a new instance and add it, after rotation. Instead, of the internal reference, it should just try to find the fragment with the appropriate tag in the fragment manager. After rotation it will still exist.
The other problem is with that the selected tab is not saved, but this is noted at the bottom of the example. You could save this in Activity.onSaveInstanceState(Bundle).
private ViewPager viewPager;
viewPager = (ViewPager) findViewById(R.id.pager);
mAdapter = new TabsPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(mAdapter);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// on changing the page
// make respected tab selected
actionBar.setSelectedNavigationItem(position);
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// on tab selected
// show respected fragment view
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}

Resources