2

It is said that onDraw() will be called when invalidate() is called. However, when I look at the android source code. I didn't find where the onDraw() gets called in invalidate(). So I am still confused about how the onDraw() method is called for a view. Who can help me figuring this out?

darklord
  • 5,077
  • 12
  • 40
  • 65
  • 3
    "it is said" by whom? Can you provide a link to your source? – Code-Apprentice Jul 10 '13 at 17:23
  • 1
    @Monad: I've seen that said in other answers, but it's more-or-less a misinterpretation. Functionally speaking, onDraw 'will get called' when 'invalidate()' is called. It's just that 'invalidate()' won't call onDraw() directly. – DeeV Jul 10 '13 at 17:26
  • @DeeV Yes, I realize that and upvoted your answer. I was asking the OP to provide sources for the statement. – Code-Apprentice Jul 10 '13 at 17:34
  • I cannot remember exactly where. But I am sure I have seen this many times. – darklord Jul 10 '13 at 19:10
  • @MonadNewb I think this answer is a bit misleading: http://stackoverflow.com/questions/11912406/view-ondraw-when-does-it-get-called – darklord Jul 10 '13 at 19:39

6 Answers6

8

Invalidate won't call onDraw() directly. It schedules a draw pass to the system which says that the Views need to draw at some point in the future which is usually almost immediately. If the UI thread is blocked, it can take some time though.

DeeV
  • 35,865
  • 9
  • 108
  • 95
8

Drawing

Drawing is handled by walking the tree and rendering each view that intersects the invalid region. Because the tree is traversed in-order, this means that parents will draw before (i.e., behind) their children, with siblings drawn in the order they appear in the tree. If you set a background drawable for a View, then the View will draw it for you before calling back to its onDraw() method. Note that the framework will not draw views that are not in the invalid region. To force a view to draw, call invalidate().

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().

10340    public void invalidate() {
10341        invalidate(true);
10342    }

Source

http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.2.2_r1/android/view/View.java#View.invalidate%28boolean%29

This is where the invalidate() work actually happens. A full invalidate() causes the drawing cache to be invalidated, but this function can be called with invalidateCache set to false to skip that invalidation step for cases that do not need it (for example, a component that remains at the same dimensions with the same content).

Parameters:

invalidateCache Whether the drawing cache for this view should be invalidated as well. This is usually true for a full invalidate, but may be set to false if the View's contents or dimensions have not changed.

10354
10355    void invalidate(boolean invalidateCache) {
10356        if (skipInvalidate()) {
10357            return;
10358        }
10359        if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) ||
10360                (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) ||
10361                (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED || isOpaque() != mLastIsOpaque) {
10362            mLastIsOpaque = isOpaque();
10363            mPrivateFlags &= ~PFLAG_DRAWN;
10364            mPrivateFlags |= PFLAG_DIRTY;
10365            if (invalidateCache) {
10366                mPrivateFlags |= PFLAG_INVALIDATED;
10367                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
10368            }
10369            final AttachInfo ai = mAttachInfo;
10370            final ViewParent p = mParent;
10371            //noinspection PointlessBooleanExpression,ConstantConditions
10372            if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {
10373                if (p != null && ai != null && ai.mHardwareAccelerated) {
10374                    // fast-track for GL-enabled applications; just invalidate the whole hierarchy
10375                    // with a null dirty rect, which tells the ViewAncestor to redraw everything
10376                    p.invalidateChild(this, null);
10377                    return;
10378                }
10379            }
10380
10381            if (p != null && ai != null) {
10382                final Rect r = ai.mTmpInvalRect;
10383                r.set(0, 0, mRight - mLeft, mBottom - mTop);
10384                // Don't call invalidate -- we don't want to internally scroll
10385                // our own bounds
10386                p.invalidateChild(this, r);
10387            }
10388        }
10389    }
Raghunandan
  • 132,755
  • 26
  • 225
  • 256
  • I think this is the answer I want. I noticed that in my code, which I didn't post here, some times the onDraw() is not called after I call invalidate(). I think probably the invalidate() is skipped because there is nothing to change. – darklord Jul 10 '13 at 17:43
  • If you call `invalidate` it will refresh your view. I don't think it will skip. In case you want to be sure you can log some info in `onDraw` and call `invalidate`. You will see the log messages.If there is no change you won't notice it but i think your view is refreshed. No change does not mean skip. – Raghunandan Jul 10 '13 at 18:01
  • I noticed that the component that I invalidated is outside the view parent. I think that's the reason why onDraw is not called. – darklord Jul 10 '13 at 20:55
1

I have to call setWillNotDraw(false). It is set to true by default for all subclasses of ViewGroup.

darklord
  • 5,077
  • 12
  • 40
  • 65
0

Take a look at this post. This explains you exactly when onDraw() will be called.

Dulanga
  • 1,326
  • 7
  • 15
0

According to View | Android Developers:

Drawing is handled by walking the tree and rendering each view that intersects the invalid region. Because the tree is traversed in-order, this means that parents will draw before (i.e., behind) their children, with siblings drawn in the order they appear in the tree. If you set a background drawable for a View, then the View will draw it for you before calling back to its onDraw() method.

Note that the framework will not draw views that are not in the invalid region.

To force a view to draw, call invalidate().

Which mean when the UI thread gets to it (the invalid region) it will call onDraw() for that region.

maclir
  • 3,218
  • 26
  • 39
-1

A View's onDraw() is called when:

  1. The view is initially drawn
  2. Whenever invalidate() is called on the view
Jainendra
  • 24,713
  • 30
  • 122
  • 169