I'd like to be notified after the view finishes redrawing after I ask it to invalidate. As said in this answer, the invalidate() method doesn't call a View's onDraw() the UI immediately, but schedules the repaint in a message queue which is executed after when the main thread is idle.
I'd like to show a progress dialog, do some UI modifications and then dismiss the dialog when the view is drawn properly. Is there some trick that I can do to know when the View was drawn? Maybe by subclassing the view, overriding the onDraw() method?
I think you answered your question yourself. Why not:
public class DrawListenerView extends View{
private Callback callback;
public DrawListenerView(Callback callback){
this.callback = callback;
}
#Override
protected void onDraw (Canvas canvas){
super.onDraw(canvas);
//add your method here you want to call
//Or use a Callback-pattern
callback.finish();
}
}
public interface Callback(){
public void finish();
}
If you look at the Source:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.1_r2/android/view/View.java#View.invalidate%28%29
The comment above says
Invalidate the whole view. If the view is visible, onDraw(android.graphics.Canvas) will >be called at some point in the future. This must be called from a UI thread. To call from >a non-UI thread, call postInvalidate().
Try it :)
Edit: probably you want to use a Callback to handle this.
You're finding it strange to try to do this because you're going about it all wrong. ;)
You're talking about wanting to wait to dismiss a dialog until something is finished drawing. That implies that you have a drawing operation that is long enough that you want to wait for it.
Drawing a frame in onDraw should be fast. You have 16 milliseconds per frame to do any sort of input processing and drawing if you want to hit 60fps and have a smooth UI. Drawing should never take long enough that you would want to show a progress dialog while it's finishing. (Aside from that, drawing as a result of invalidating part of your UI blocks your UI thread, and your progress dialog wouldn't illustrate any progress until it's done anyway.)
If you need to do some complex off-screen rendering to show later, you should do it in an AsyncTask or similar off of your UI thread, not in a view's actual onDraw method. Once you get the finished callback from that, you can quickly draw the prerendered image you just created and dismiss your progress dialog.
Related
What event signifies that the draw of a View is complete?
I know about ViewTreeObserver listeners, but I couldn't find the 'final' one, which indicates, that the job is done.
yourView.post(someRunnable) ensures, that someRunnable will be executed after the view is laid out and drawn.
What event signifies that the draw of a TextView is complete?
There is no such hook for View class (or TextView). There is, however, onDraw() method which is called when the view should render its content.
So you can do:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Finished drawing. Do other stuff.
// However you must check if this is the first or subsequent call.
// Each call to "invalidate()" will trigger re-drawing.
}
If I understand your question correctly the method you are looking for is onWindowFocusChanged(boolean hasFocus). Or else you could try the onPostResume() method.
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.
Progress dialog freeze when adding a large view in layout from onPost of AsyncTask. We have to add a dynamic layout so we have called that from onPost,( On calling that from doInBackground error occure, So we have called that from opPost) But now the progress dialog freeze when loading that layout.
Please Help...
I have created a separated class and called that class method
protected void onPostExecute(Void unused)
{
eLayout=new ExcelLayout(viewScreenActivity);
eLayout.LinearLayoutXLSView(ROW_HEIGHT, COL_WIDTH, ROW_Title_Width,NUM_COLS, NUM_ROWS, cols, rows, data);
progressdialog.dismiss();
}
Progress dialog freeze and disappear after task completed.
This problem raises when there is a huge process such as drawing a view happens in the main UI thread. The progress dialogs are very light weight process and when some other process takes priority then they just stop there without rotating (in simple) which makes it looks like it has hanged. But the actual reason is, there is some other process which takes more priority than showing the progress dialog.
In your case it is the View creation which takes priority. So what you have to do is, dismiss the dialog before starting to draw the layout.
But if you are looking for a way to show a progress dialog while you draw your view i am afraid it is not possible, from my experience so far.
EDIT
it is very much clear that your main UI does a lot of work. YOu can try this but I am not sure how far this will work. Instead of just calling the methods immediately after the progressdialog.dismiss(), you could use the dismiss listener and call the methods from there. For example,
progressDialog.setOnDismissListener(new OnDismissListener(){
public void onDismiss(DialogInterface dialog) {
eLayout=new ExcelLayout(viewScreenActivity);
eLayout.LinearLayoutXLSView(ROW_HEIGHT, COL_WIDTH, ROW_Title_Width,NUM_COLS, NUM_ROWS, cols, rows, data);
}});
So this gets called only when the progress dialog's progressdialog.dismiss(); is called. So it should work probably.
What is the difference between Android's invalidate() and postInvalidate() methods? When does each one get called? Must the methods be called only in classes which extend View?
If you want to re-draw your view from the UI thread you can call invalidate() method.
If you want to re-draw your view from a non-UI thread you can call postInvalidate() method.
Each class which is derived from the View class has the invalidate and the postInvalidate method. If invalidate gets called it tells the system that the current view has changed and it should be redrawn as soon as possible. As this method can only be called from your UI thread another method is needed for when you are not in the UI thread and still want to notify the system that your View has been changed. The postInvalidate method notifies the system from a non-UI thread and the view gets redrawn in the next event loop on the UI thread as soon as possible. It is also shortly explained in the SDK documentation:
CLICK HERE
UPDATE:
There are some problems that arise when using postInvalidate from other threads (like not having the UI updated right-away), this will be more efficient:
runOnUiThread(new Runnable() {
public void run() {
myImageView.setImageBitmap(image);
imageView.invalidate();
}
});
I'm new to programming androids but I have quite a bit of experience programming blackberries.
I created an app that has an activity class (main.java) and a view class (game.java).
Inside the view class I have some bitmaps being drawn to the screen. I created a thread and I'm moving the images around in the thread. However when I call invalidate() inside the thread it never redraws the screen.
Are you not able to invalidate() the screen from a thread? I know the thread is running and the invalidate is being called, it just never makes the changes on the screen.
You have to use View.postInvalidate() if you call it from a non-UI thread.
According to docs:
public void postInvalidate ()
Since: API Level 1
Cause an invalidate to happen on a subsequent cycle through the event loop. Use this to invalidate the View from a non-UI thread.