How do run a function after the view and its subviews have finished loading like in iOS (viewDidAppear / viewDidLayoutSubviews)
I basically want to run a UI animation after the program loads but as my programs too heavy(and has many subviews) the animation doesn't run at all(I tried handler.postDelayed and it worked fine but I wanted to know the more efficient way to do this(for performance reason as a phone I tested on is quite old and takes 4s to load while another takes just a second, etc.).
I think you can use the ViewTreeObserver for that as stated here.
You will have something like:
yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// do something here but make sure you unregister if you just want to get notified only once..
}
});
The developers page is stating the following: Register a callback to be invoked when the global layout state or the visibility of views within the view tree changes
You can then add this listener on the root view of your activity for example in onCreate and inside that method you can execute your animation.
The method gets called whenever the layout state or the visibility of the view gets changed. It is not like viewDidAppear but it is something similar to "viewDidLayoutSubviews".
You can also try to run the animation in onResume but that will run the animation whenever the activity resumes not only the first time you create it and I am not sure if you want or not that.
Related
Let's imagine situation:
User click on 'Login' button and Fragment(View) call Presenter's method doLogin().
Presenter starts some async work and now Boom! app is closed(moved to recent apps)
Presenter survives and async work is still happening.
Async work finished while app was in the background.
User came back to app, but he doesn't see any notification that work is finished as view was de-attached:
if(isViewAttached()) {
getView().setLoaded(workResult);
}
And I want to fix it. The only way that I see is to use Queue<MessageToView> and when View has attached again, execute every "Message".
I think that there is a library that can handle my case. So, is it? Or what pattern can I use?
See github pages FAQ section:
Can the Presenter and its view be out of sync during a screen
orientation change?
Excellent question. Mosby assumes that all interaction from Presenter
with the View happens on android’s main UI thread. Hence the answer is
no that cannot happen since screen orientation changes are executed on
the main UI thread as well. So either is a screen orientation executed
completely (view reattached) or the presenter invokes the views method
after view is reattached since both run on main UI thread or the
presenter invokes the views methods before starting screen orientation
change.
So as long as your Presenter invokes View methods on main UI thread everything works out of the box.
Try using the Fragment's onResume() lifecycle method, and then call something like presenter.updateViews()
The answer I expected at first was the show method. Unfortunately if show has a lot of work to do, there is a delay between method call and screen appearance. So how does one get notified when screen appears?
Using show() should be fine, the screen will be shown straight after so just put stuff you want done at the end where the delay should be negligible.
If the work that show() does is in a super class, then just override it, call super.show() and then do your stuff after, like so...
#Override
public void show () {
super.show();
doAnyOtherSlowStuffThatMightNeedDoing();
doTimeCriticalStuff();
}
I should probably add that a better solution would be to not do slow stuff in the show() method to start with, but that's a whole other debate.
I have this problem for over a week now and after going through dozens of topics here and on google, I still don't have an answer.
The issue is that I am dynamically loading data to long ListView, so the initialization doesn't last for ages. After changing phone orientation ListView is beeing resized and have empty elements inside (thats ok). So i thought that OnConfigurationChanged event will handle this. Unfortunately it's fired before widgeds are resized, so still I have empty list. And here is my question: Is it anyway to get an event (or Listener) that will fore after the widged is resized and drawn?
I already have in manifest:
android:configChanges="keyboardHidden|orientation|screenSize
And there is no way to turn it "off", because loading takes few seconds and I don't want to have a few seconds lag while chenging phone orientation.
P.S. Sorry for any language mistakes ;)
yes. You can use setOnGlobalLayoutListener() that will listen and you use a callback once layout is completed
Also on screen rotation, activity should be destroyed, recreate, so your loading should restart. Are you doing something strange?
I do this in one of my fragment :
root = inflater.inflate(R.layout.fragment_booking_accepted, null);
root.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Dbg.e(TAG, "- On Layout Completed ! ");
// If you need this to be called again, then run again addOnGlobalLayoutListener.
// root.getViewTreeObserver().removeGlobalOnLayoutListener(this);
isLayoutCompleted = true;
// YOU write here all the coed you might need. }}}} close brackets
Edit :
Most people think it's horrible to not let android do its job: destroy and recreate when screen rotates. But you can do what you want.
Loading an empty list view is very light. It's impossible it takes few seconds to load the layout. You HAVE to load the layout and your listview with a spinner to let the user know what his phone is doing (loading). Once data is loaded, notify data change to the adapter.
does it change your point of view? ask me, if I know, I'll try to answer.
I need a way to run some code at the exact moment in which the activity is fully loaded, laid out, drawn and ready for the user's touch controls. Which method/listener does that?
Commonsware is right, without explaining what your are trying to do and why, it's not possible to answer your question and I suspect, with detail, you are probably thinking about it the wrong way.
However, I do have some code where I needed to do some very funky layout stuff after everything had been measured.
I could have extended each of the view classes in the layout and overriden onMeasure() but that would have been a lot of work. So, I ended up doing this. Not great, but it works.
mainMenuLayout is the layout I needed to get funky with. The onGlobalLayout callback is called when the layout has completed drawing. Utils.setTitleText() is where the funkiness takes place and as I pass mainMenuLayout to it, it has access to the position and size of all of the child views.
mainMenuLayout.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// only want to do this once
mainMenuLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
// set the menu title, the empty string check prevents sub-classes
// from blanking out the title - which they shouldn't but belt and braces!
if (!titleText.equals("")){
Utils.setTitleText(_context,mainMenuLayout,titleText);
}
}
});
I've found that if I post a Runnable to the message queue, it will run after the content for the activity has been drawn. For example, if I want the width and height of a View, I would do this:
view.post( new Runnable() {
#Override
public void run() {
int width = view.getWidth(); // will be non-zero
int height = view.getHeight(); // will be non-zero
}
} );
I've found success with this anytime after I call setContentView().
onRestoreInstanceState method is the one called to restore UI state which is called after onResume .I think you can use this onRestoreInstanceState method.. and put your code after restoring UI state from the savedInstanceState...
Try onPostResume() called after onResume() at this moment the Activity instance should be visible and all underlying Views are rendered. In many situations this is true when onResume() is called as well.
Maybe it little helps:
#Override
public void onAttachedToWindow(){}
I'm developing an Android 3.1 application.
I want to execute an AsyncTask after activity is shown. I want to show something to user before execute AsyncTask.
I've read that it is not recommend to execute AsyncTask on onCreate().
Where I have to execute AsyncTask on onStart() or onResume()?
I want to left enough time to show activity before execute it.
onCreate(), onStart() and onResume() are lifecycle methods called by the operating system and shouldn't be called directly. You can however override them to have your code executed at these stages of the activities lifecycle:
However, if you want your AsyncTask to start after all of your Views have been inflated and drawn to the screen then you need to put the code in this:
toReturn.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
public void onGlobalLayout() {
toReturn.getViewTreeObserver().removeGlobalOnLayoutListener(this);
// asyncTask.execute();
}
});
In the above example toReturn is a view in your onCreate() method. It can be any view you like.
This pulls a ViewTreeObserver from the View and add's a listener to it which will be called when the view has finished being drawn to the screen. It's important you keep the "removeGlobalOnLayoutListener()` line in as this will stop the code firing every time the View is drawn.
Answer is in onResume()
I hade same requirement in my activity where i need to show some list with other buttons and images..
List were getting data from server so used AsyncTask for that..
But before that required to show empty listview and other part of the screen..
so first when it goes to onCreate() I set empty arraylist to listview's adapter then in onResume() call the Asynctask and in that task fill the ArrayList and call adapter.notifyDataSetChanged()
Then another problem occure..when i go to next activity and come back it always call the asynctask even if i dont require..
So had put some condition like if(arrayList.size()==0) then call asynctask else dont.
You can put yur code in the onWindowsFocusChanged method. You can use a thread inside it to manage the timer to start your specific asynctask.
Be aware that this would be performed each time your activity have the focus, not only the first time you launch your activity (I don't know if this could be a problem for you).
implement a View object and override the onDraw().
that way you'll know exactly when the first screen is visible to the user