Best place to create and keep the animation objects in Flutter? - performance

If I want to define a custom Animation driven from the AnimationController, which is the best place for doing that?
In case if animation isn't in the same Widget with the controller and the latter is just passed down to it as an argument, it's totally clear that the best place are final class fields, as the animation will be computed on class creation, not in build method. And there's even an example of code implying that.
In the second case, we can have the controller and the driven animations inside the same class. In this case it's not clear for me, which is the best place to initialize my animations:
To make the a class fields and create them in initState
Or to define them as a final local variables in build method
Is there any real difference in performance between these to options?
See the code examples below:
#1
class _AState extends State<A> with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<Color> _animation;
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 300));
_animation = ColorTween(begin: Colors.black, end: Colors.white).animate(_controller);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (BuildContext context, Widget child) => //...,
);
}
}
#2
class _BState extends State<B> with SingleTickerProviderStateMixin {
AnimationController _controller;
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration:const Duration(milliseconds: 300));
}
#override
void dispose(){
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
final Animation<Color> animation = ColorTween(begin: Colors.black, end: Colors.white).animate(_controller);
return AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) => //...,
);
}
}

If I want to define a custom Animation driven from the AnimationController, which is the best place for doing that?
from the performance perspective, it's absolutely obvious that storing animation objects as class fields is better than having them as build methods variables
Is there any real difference in performance between these to options?
it depends
actually, sometimes it wouldn't have any difference at all, if your widget will not be very often rebuilt, so Animation objects will not be often reinstantiated - in these cases you can safely neglect the ideal approach in favor of:
shorter syntax
better development experience
by better development experience i mean that, for example if you may want to play a bit with parameters of your animation. you can do with hot-reload, if you construct animations in build method, which is not possible if you create animations in initState
however, assume, you plan to use such widget inside ListView, or you just find that you often call setState - in this case you will unarguably want to optimize each item build method as much as possible, and this is one of the most effective ways to save a lot of performance and memory resources

Related

How can I correctly run a timed process in my app the will start on stop when the application is in use or in the background?

I have an application that starts up and runs a background check of the database every minute. Below is the code for this.
I'm getting what I think is a memory leak and am looking at all areas of the code that loop.
Is there any possibility that this code could be left in a looping state and contribute to a memory leak or is the way the onSleep and onResume coded a 100% sure way to correctly stop and start the timer loop?
Note that I only want the timed part of the code to run once a minute when the application is being used and in the foreground.
namespace Japanese
{
public partial class App : Application
{
private static Stopwatch stopWatch = new Stopwatch();
public App()
{
InitializeComponent();
MainPage = new Japanese.MainPage();
}
protected override void OnStart()
{
App.DB.InitData();
if (!stopWatch.IsRunning)
stopWatch.Start();
Device.StartTimer(new TimeSpan(0, 0, 1), () =>
{
if (stopWatch.IsRunning && stopWatch.Elapsed.Minutes >= defaultTimespan)
{
Debug.WriteLine("Checking database");
PointChecker.CheckScore();
stopWatch.Restart();
}
return true;
});
}
protected override void OnSleep()
{
stopWatch.Reset();
}
protected override void OnResume()
{
stopWatch.Start();
}
}
}
The App class is the class that represents the cross-platform mobile application, it is running even your "MainPage" was not, so i think you need to use OnAppearing and OnDisappearing methods in your main page (a :ContentPage).
Maybe something like :
protected override void OnAppearing()
{
stopWatch.Start();
base.OnAppearing();
}
and,
protected override void OnDisappearing()
{
stopWatch.Reset();
base.OnDisappearing();
}
I hope that helps,
Mabrouk.

Is it possible to use transitions on glisten layers?

I created a custom layer, according to this material:
http://docs.gluonhq.com/charm/2.1.1/#_creating_a_layer, and added it to my application:
MobileApplication.getInstance().addLayerFactory(LAYER_NAME, () -> customLayer);
Now I would like to add a transition to this layer. You can use transitions on View like: view.setShowTransitionFactory(BounceInDownTransition:new)
Layer doesn't provide a method like that. So I tried this approach to apply a transition:
private void showLayer() {
MobileApplication.getInstance().showLayer(LAYER_NAME);
new BounceInDownTransition(customLayer).play();
}
When I call showLayer() for the first time the transition appears to be incomplete. The first part, where the layer should get transitioned out of view, is missing. Each further invocation of showLayer() shows the complete transition.
Are layers meant to be used in conjuction with transitions at all?
If possible what is the recommended way?
You can use the built-in transitions in Gluon Charm, since all you need is pass a node to them, and call play to start the animation.
In case of the Gluon's Layers, there's no built-in mechanism as for Views, but you can easily add it to your class.
This will create a bounce-in effect for showing and bounce-out effect for hiding.
public class MyLayer extends Layer {
private final Node root;
private final double size = 150;
public MyLayer() {
final BounceInDownTransition transitionIn = new BounceInDownTransition(this, true);
final BounceOutDownTransition transitionOut = new BounceOutDownTransition(this, true);
transitionOut.setOnFinished(e -> hide());
Button button = new Button("", MaterialDesignIcon.CLOSE.graphic());
button.setOnAction(e -> transitionOut.playFromStart());
root = new StackPane(button);
root.setStyle("-fx-background-color: white;");
getChildren().add(root);
getGlassPane().getLayers().add(this);
showingProperty().addListener((obs, ov, nv) -> {
if (nv) {
layoutChildren();
setOpacity(0);
transitionIn.playFromStart();
}
});
}
#Override
public void show() {
getGlassPane().setBackgroundFade(GlassPane.DEFAULT_BACKGROUND_FADE_LEVEL);
super.show();
}
#Override
public void hide() {
getGlassPane().setBackgroundFade(0.0);
super.hide();
}
#Override
public void layoutChildren() {
root.setVisible(isShowing());
if (!isShowing()) {
return;
}
root.resize(size, size);
resizeRelocate((getGlassPane().getWidth() - size)/2, (getGlassPane().getHeight()- size)/2, size, size);
}
}
And now, add the layer:
#Override
public void init() {
addViewFactory(BASIC_VIEW, () -> new BasicView(BASIC_VIEW));
addLayerFactory("My Layer", () -> new MyLayer());
}

JavaFX FadeTransition.onSetOnFinished with CountdownLatch does not work as expected

I'm staring at my code and I'm pretty stuck with one issue:
I want to make a fade out transition, and i want to block current thread, while the fade transition is running.
So, my attempt was to create a CountDownLatch, which blocks the thread, until the transition.setOnFinished() is called, where I make a latch.countdown(). In short: i want to make sure, that the transition is always visible in full length.
Seemed pretty straight forward to me, but...
The setOnFinished() doesn't get called, BECAUSE the current thread mentioned above is blocked by the countdown latch.
How can i solve this issue? Thx in advance.
private void initView() {
Rectangle rect = new Rectangle();
rect.widthProperty().bind(widthProperty());
rect.heightProperty().bind(heightProperty());
rect.setFill(Color.BLACK);
rect.setOpacity(0.8f);
getChildren().add(rect);
MyUiAnimation animator = new MyUiAnimation();
fadeInTransition = animator.getShortFadeInFor(this);
fadeOutTransition = animator.getShortFadeOutFor(this);
fadeOutTransition.setOnFinished(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
Platform.runLater(new Runnable() {
#Override
public void run() {
latch.countDown();
setVisible(false);
}
});
}
});
}
public void hide() {
fadeInTransition.stop();
if (isVisible()) {
latch = new CountDownLatch(1);
fadeOutTransition.playFromStart();
try {
latch.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
You can only modify and query the active scene graph on the JavaFX application thread. All of your code works with the scene graph, so it all must be run on the JavaFX application thread. If everything is already running on the JavaFX application thread, there is no reason for concurrency related constructs in your code.
If you use blocking calls like latch.await(), you will block the JavaFX application thread, which will prevent any rendering, layout or animation steps to run. CountdownLatch should not be used in this context and should be removed from the code.
Calling Platform.runLater is unnecessary as it's purpose is to run code on the JavaFX application thread, and you are on the JavaFX application thread already. Platform.runLater should not be used in this context and should be removed from the code.

Slow loading of layout

I have a super class which is in a library. This library take care of initializing some basic layout components and other stuff. My problem is that it takes 1.x seconds to load the layout, and shows the default layout for a while, before setting the child-specified layout.
This is the method of my super class:
public void InitializeWindow(Activity act, int layoutResourceId, String windowTitle,
Object menuAdapter, int slideMenuMode) {
super.setContentView(layoutResourceId);
super.setBehindContentView(R.layout.menu_frame);
this.menuAdapter = menuAdapter;
this.slideMenuMode = slideMenuMode;
setWindowTitle(windowTitle);
initializeSlidingMenu();
}
This is called this way:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.InitializeWindow(this, R.layout.activity_home, "\t\tHome",
new MenuAdapter(this, R.menu.slide_menu), SlidingMenu.TOUCHMODE_FULLSCREEN);
}
The application works like a charm, but it takes, as I said around 1.x seconds to load the layout passed from the child-class. Why does this happen?
By request, this is my initializeSlideMenu() method:
public void initializeSlidingMenu() {
this.setSlidingActionBarEnabled(true);
getSlidingMenu().setBehindOffsetRes(R.dimen.actionbar_home_width);
getSlidingMenu().setShadowWidthRes(R.dimen.shadow_width);
getSlidingMenu().setShadowDrawable(R.drawable.shadow);
getSlidingMenu().setTouchModeAbove(slideMenuMode);
getSlidingMenu().setBehindScrollScale(0.25f);
ListView v = new ListView(this);
v.setBackgroundColor(Color.parseColor("#000000"));
v.setAdapter((ListAdapter) menuAdapter);
getSlidingMenu().setMenu(v);
}
To avoid such problems there are three ways in general.
Let your onCreate() finish after setContentView() call as early as possible. You can use postDelayed runnable to delay few initialization which may not be needed at early stages.
Do some task when the view is ready, it causes the Runnable to be added to the message queue of that view.
Snippet
view.post(new Runnable() {
#Override
public void run() {
}
});
If none of the above helps consider "Optimize with stubs" link : http://android-developers.blogspot.in/2009/03/android-layout-tricks-3-optimize-with.html
Hope it helps.
I suspect that the trouble spot for you is with:
v.setAdapter((ListAdapter) menuAdapter);
You should do this as part of an AsyncTask. It will often be very slow to execute the loading by the adapter.
Here is a snippet from a sample AsyncTask implementation:
//before starting the load, I pop up some indicators that I'm doing some loading
progressBar.setVisibility(View.VISIBLE);
loadingText.setVisibility(View.INVISIBLE);
AsyncTask<Void, Void, Void> loadingTask = new AsyncTask<Void, Void, Void>() {
private ArrayList<Thing> thingArray;
#Override
protected Void doInBackground(Void... params) {
//this is a slow sql fetch and calculate for me
thingArray = MyUtility.fetchThings(inputValue);
return null;
}
#Override
public void onPostExecute(Void arg0) {
EfficientAdapter myAdapter = new EfficientAdapter(MyActivity.this, thingArray);
listView.setAdapter(myAdapter);
//after setting up my adapter, I turn off my loading indicators
progressBar.setVisibility(View.INVISIBLE);
loadingText.setVisibility(View.INVISIBLE);
RelativeLayout layout = (RelativeLayout)MyActivity.this.findViewById(R.id.spacey);
if (layout != null) {
LayoutInflater inflater = LayoutInflater.from(MyActivity.this);
View view = inflater.inflate(R.layout.name_tabled_sub, layout);
NamedTableView tableView = new NamedTableView(MyActivity.this, view);
}
progressBar.setVisibility(View.INVISIBLE);
loadingText.setVisibility(View.INVISIBLE);
}
};
loadingTask.execute();
You can also do "PreExecute" items with the Async task, as well as update.

Closing a Stage from within its controller

I need a way to close a Stage from within itself by clicking a Button.
I have a main class from which I create the main stage with a scene. I use FXML for that.
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Builder.fxml"));
stage.setTitle("Ring of Power - Builder");
stage.setScene(new Scene(root));
stage.setMinHeight(600.0);
stage.setMinWidth(800.0);
stage.setHeight(600);
stage.setWidth(800);
stage.centerOnScreen();
stage.show();
}
Now in the main window that appears I have all the control items and menus and stuff, made through FXML and appropriate control class. That's the part where I decided to include the About info in the Help menu. So I have an event going on when the menu Help - About is activated, like this:
#FXML
private void menuHelpAbout(ActionEvent event) throws IOException{
Parent root2 = FXMLLoader.load(getClass().getResource("AboutBox.fxml"));
Stage aboutBox=new Stage();
aboutBox.setScene(new Scene(root2));
aboutBox.centerOnScreen();
aboutBox.setTitle("About Box");
aboutBox.setResizable(false);
aboutBox.initModality(Modality.APPLICATION_MODAL);
aboutBox.show();
}
As seen the About Box window is created via FXML with a controller. I want to add a Button to close the new stage from within the controller.
The only way I found myself to be able to do this, was to define a
public static Stage aboutBox;
inside the Builder.java class and reference to that one from within the AboutBox.java in method that handles the action event on the closing button. But somehow it doesn't feel exactly clean and right. Is there any better way?
You can derive the stage to be closed from the event passed to the event handler.
new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
// take some action
...
// close the dialog.
Node source = (Node) actionEvent.getSource();
Stage stage = (Stage) source.getScene().getWindow();
stage.close();
}
}
In JavaFX 2.1, you have few choices. The way like in jewelsea's answer or the way what you have done already or modified version of it like
public class AboutBox extends Stage {
public AboutBox() throws Exception {
initModality(Modality.APPLICATION_MODAL);
Button btn = new Button("Close");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
close();
}
});
// Load content via
// EITHER
Parent root = FXMLLoader.load(getClass().getResource("AboutPage.fxml"));
setScene(new Scene(VBoxBuilder.create().children(root, btn).build()));
// OR
Scene aboutScene = new Scene(VBoxBuilder.create().children(new Text("About me"), btn).alignment(Pos.CENTER).padding(new Insets(10)).build());
setScene(aboutScene);
// If your about page is not so complex. no need FXML so its Controller class too.
}
}
with usage like
new AboutBox().show();
in menu item action event handler.

Resources