Ztelur
Contact information: Segmentfault,csdn,github
Please indicate the original author, the source of the article, the link, the copyright belongs to the original author.
This article is the last article in the Android Scroll series, mainly on the Android view drawing mechanism, because this series of articles are related to the view scrolling, so this article from the View content scrolling perspective to comb the view drawing process.
? If you have not read this series of articles or do not know the relevant knowledge, please read the article:
- Android Motionevent Detailed
- Android Scroll Details (i): basic knowledge
- Android Scroll Detailed (ii): Overscroller combat
In order to save everyone's time, the main content of this article is as follows:
Scroller
Related mechanisms.
mScrollX
and mScrollY
How the view content is affected.
- The Android view draws logic, including related APIs and
Canvas
related operations.
everything Scroller
starts with the use of
Using the Scroller instance code, the next tutorial is how scroller and Computescroll are called.
? In the second installment of the series, we have specifically learned Scroller
how to use it. Through Scroller
the fling
and of the View
computeScroll
mates, realize the view scrolling effect. The instance code is as follows
..... mScroller.fling(0,getScrollY(),0,speed,0,0,-500,10000) invalidate(); ..... @Override publicvoidcomputeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } }
? This article takes you through the principles and mechanisms behind this code.
invalidate's path to seek the Father
? This section mainly analyzes the principle of call invalidate
to execution in view ViewRoot
, the performTraversals
Android view architecture is not very familiar with the students can first read the "Android view architecture in detail."
? Let View
's look at the code in the first invalidate
.
Public void Invalidate() {Invalidate (true); }voidInvalidateBooleanInvalidatecache) {invalidateinternal (0,0, Mright-mleft, Mbottom-mtop, Invalidatecache,true); }voidInvalidateinternal (intLintTintRintBBooleanInvalidatecache,BooleanFullinvalidate) {...if//drawn and Has_bounds are set to 1, indicating that the last UI drawing for the execution of the request has been completed, you can request execution again if(Mprivateflags & (Pflag_drawn | pflag_has_bounds) = = (Pflag_drawn | Pflag_has_bounds) | | (Invalidatecache && (mprivateflags & pflag_drawing_cache_valid) = = Pflag_drawing_cache_valid) || (Mprivateflags & pflag_invalidated)! = pflag_invalidated | | (Fullinvalidate && isopaque ()! = Mlastisopaque)) {if(fullinvalidate) {mlastisopaque = Isopaque (); Mprivateflags &= ~pflag_drawn; } mprivateflags |= Pflag_dirty;if(Invalidatecache) {//Whether to invalidate the view cacheMprivateflags |= pflag_invalidated; Mprivateflags &= ~pflag_drawing_cache_valid; }//Propagate The damage rectangle to the parent view. FinalAttachinfo ai = mattachinfo;FinalViewparent p = mparent;//through viewparent, if the current view is the top-level view or Decorview view, then its //mparent is the Viewroot object, so it is implemented by Viewroot objects. if(P! =NULL&& ai! =NULL&& L < R && T < b) {FinalRect damage = Ai.mtmpinvalrect; Damage.set (L, T, R, b); P.invalidatechild ( This, damage);//todo: This is the subject of invalidate execution} ..... } }
? We can see that the call invalidate()
causes the entire view to refresh and the cache is refreshed.
? Then we'll look at invalidateInternal
the code in detail. Let's take a look at if
the judgment condition of the sentence first.
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS) || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) || (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED || (fullInvalidate && isOpaque() != mLastIsOpaque))
- When
mPrivateFlags
the FLAG_DRAWN
and FLAG_HAS_BOUNDS
bit is set to 1 o'clock, the UI drawing that was last requested to execute has completed, you can request a redraw again. Bit will be set to 1 in the FLAG_DRAWN
draw
function, and will be 1 in the FLAG_HAS_BOUNDS
setFrame
function.
mPrivateFlags
PFLAG_DRAWING_CACHE_VALID
Indicates whether the cached view cache is valid, and if it is valid and invalidateCache
true, a redraw can be requested.
- The other two Boolean judgments of the specific meaning of the analysis is not clear, we are interested in your own research.
? Then set mPrivateFlags
PFLAG_DIRTY
to 1. And if it is to flush the cache, PFLAG_INVALIDATED
set the bit to 1, and PFLAG_DRAWING_CACHE_VALID
set the bit to 0, this step and the previous if
judgement in the last two Boolean judgments correspond, visible, if there is already a invalidate
set of the above two flag bits, then the next invalidate
No action will be made.
Then, call the Invalidatechild function of the Viewparent interface, in the "Android View architecture in detail", we already know ViewGroup
and ViewRoot
all implement the above interface, then, according to the Android View tree structure, ViewGroup
The corresponding method of the call will be called.
Public Final void Invalidatechild(View Child,FinalRect Dirty) {viewparent parent = This;FinalAttachinfo attachinfo = Mattachinfo;if(Attachinfo! =NULL) { ....//While always upward recursionDo {.... parent = parent.invalidatechildinparent (location, dirty); .... } while(Parent! =NULL); }} PublicViewparentinvalidatechildinparent(Final int[] location,FinalRect Dirty) {if((Mprivateflags & pflag_drawn) = = Pflag_drawn | | (Mprivateflags & pflag_drawing_cache_valid) = = Pflag_drawing_cache_valid) {if(Mgroupflags & (Flag_optimize_invalidate | Flag_animation_done))! = Flag_optimize_invalidate) {...returnMparent; }Else{ .....returnMparent; } }return NULL;}
? Through the code we can see ViewGroup
the invalidateChild
function through the loop constantly call its parent view, invalidateChildInParent
and we know ViewRoot
is DecorView
the parent view, that ViewRoot
is, the root of the Android view tree structure. So, the end result ViewRoot
invalidateChildInParent
will be called.
//In ViewGroup invalidatechildinparent while loop, always call here, and then call Invalidatechild PublicViewparentinvalidatechildinparent(Final int[] location,FinalRect dirty) {Invalidatechild (NULL, dirty);return NULL; } Public void Invalidatechild(View child, Rect Dirty) {//Check thread first, must be main threadCheckthread (); .....//If Mwilldrawsoon is true then there is already a do_traversal message in the message queue . if(!mwilldrawsoon) {//Call this directlyScheduletraversals (); }}
? Finally, in ViewRoot
the invalidateChild
function, called, the scheduleTraversals
view is opened to redraw the journey.
We've all been ViewRoot
cheated.
?ViewRoot
is the root node of the Android view tree structure, and it implements the ViewParent
interface, which is DecorView
the parent view. Then everyone will think it is a View
bar. Then we'll be fooled by it!! ViewRoot
is essentially a Handler
, we can see scheduleTraversals
performTraversals
the principle to know.
publicvoidscheduleTraversals() { if (!mTraversalScheduled) { true; sendEmptyMessage(DO_TRAVERSAL); }}
? in scheduleTraversals
, ViewRoot
just send yourself an DO_TRAVERSAL
empty message.
@Override public void handlemessage (Message msg) {switch (msg.what) {.... case do_traversal: //this is where handle handles travel information if (mprofile) {debug.startmethodtracing ( "Viewroot" ); } performtraversals (); if (Mprofile) {debug.stopmethodtracing (); Mprofile = false ; } break ; ..... } }
? Then we looked at the handleMessage
method and found that the function was called when it was processed DO_TRAVERSAL
ViewRoot
performTraversals
.
In performTraversals
, the view is going to be measure,layout, and draw three big steps, space is limited, we only study drawing related mechanism here.
?ViewRoot
In the performTraversals
call of their own draw
method, see, ViewRoot
camouflage is quite like, even draw
methods have. But we will find that in the method, we draw
ViewRoot
actually only invoke the method of our own mView
member variables draw
, and we all know that, that is, the mView
DecorView
drawing process came to the root node of the real view view.
Canvas for everyone to draw
? Next, we'll take a formal look at the Android drawing mechanism, and we'll follow the tree-like structure of the Android view to analyze the drawing principle.
? The first is DecorView
the drawing-related function. In ViewRoot
the draw
method, directly called the DecorView
draw(Canvas canvas)
function, we know DecorView
is FrameLayout
the subclass, its draw(Canvas canvas)
function is inherited from the View
. So let's look View
at the draw(Canvas canvas)
method first.
//Http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/ View/view.java#view Public void Draw(Canvas canvas) { ......../* * Draw traversal performs several drawing steps which must be executed * in the appropriate Order: * * 1. Draw the background * 2. If necessary, save the canvas ' layers to prepare for fading * 3. Draw View ' s content * 4. Draw Children * 5. If necessary, draw the fading edges and restore layers * 6. Draw Decorations (scrollbars for instance) */ //Step 1, draw the background, if needed if(!dirtyopaque) {drawbackground (canvas); } .......//Step 2, Save the canvas ' layers.......//Step 3, draw the content if(!dirtyopaque) OnDraw (canvas);//Step 4, Draw the childrenDispatchdraw (canvas);//Step 5, draw the fade effect and restore layers.......if(Drawtop) {Matrix.setscale (1, Fadeheight * topfadestrength); Matrix.posttranslate (left, top); Fade.setlocalmatrix (matrix); P.setshader (Fade); Canvas.drawrect (left, top, right, top + length, p); } .....//Step 6, Draw decorations (scrollbars)Ondrawscrollbars (canvas); ...... }
About the parts of the view, which I have already described in the previous article, please consult the articles or other materials by yourself who are not familiar with this section. As we can see from the above code, View
the dispatchDraw
function is called, which is the method of distributing the drawing instructions and related data to the child view. In View
, the above function is an empty function, but the ViewGroup
function is implemented.
protected void Dispatchdraw(Canvas canvas) { ....FinalArraylist<view> preorderedlist = usingrendernodeproperties?NULL: Buildorderedchildlist ();Final BooleanCustomorder = Preorderedlist = =NULL&& ischildrendrawingorderenabled (); for(inti =0; i < Childrencount; i++) {intChildindex = Customorder? Getchilddrawingorder (Childrencount, i): i;FinalView Child = (Preorderedlist = =NULL) ? Children[childindex]: Preorderedlist.get (Childindex);if((Child.mviewflags & visibility_mask) = = VISIBLE | | child.getanimation ()! =NULL) {//Here DrawchildMore |= drawchild (canvas, Child, drawingtime); } } .... }protected Boolean Drawchild(Canvas canvas, View child,LongDrawingtime) {//This calls the child's draw method instead of the draw (canvas) method!!!!! returnChild.draw (Canvas, This, drawingtime); }
? We can see from the above code that we have ViewGroup
called our own sub-view draw
method, and it is important to note that this draw and the previous draw method are not the same method, their parameters are different. So, we go to View
the source code again, and see what this draw method did.
BooleanDraw (canvas canvas, viewgroup parent,LongDrawingtime) {....//Perform calculation scrolling if(!hasdisplaylist) {Computescroll (); SX = MSCROLLX; sy = mscrolly; } ...//translation is done here. if(Offsetforscroll) {canvas.translate (MLEFT-SX, Mtop-sy); } .....if(!layerrendered) {if(!hasdisplaylist) {//Fast path for layouts with no backgrounds if((Mprivateflags & pflag_skip_draw) = = Pflag_skip_draw) {mprivateflags &= ~pflag_dirty_mask; Dispatchdraw (canvas); }Else{//Call draw hereDraw (canvas); } } ...... } ...... }
? First, we find that the computeScroll
method is called in which to calculate the new mScrollX
and mScrollY
then translate the canvas, resulting in the content panning effect.
? Then we find PFLAG_SKIP_DRAW
that through the flag bit judgment, some view is called directly dispatchDraw
function, stating that it does not need to draw the content, and some view is to call their own draw
methods. We should all know that the ViewGroup
default is not to draw the content, we generally call the setNotWillDraw
method to let it draw its own content, by invoking setNotWillDraw
the method, will cause the PFLAG_SKIP_DRAW
bit to be set to 1, so that it can draw its own content.
Analysis here, we will find that the draw function is constantly being called along the Android View tree structure, knowing that all views are drawn.
to connect everything together, Computescroll.
Read here that you should have a basic understanding of the Android view drawing process, so let's take a look at the beginning of the article. In the computeScroll
method, we call the postInvalidate
method, what is the purpose?
In fact, in the computeScroll
middle does not postInvalidate
seem to be able to achieve the correct effect, specific reasons I do not know, guess should be Android Auto Refresh interface can replace postInvalidate
the effect bar. If the students know the specific reasons, please tell me.
? In "Android Scroll (i): Basics", we've talked about
postInvalidate
In fact, it is called invalidate
, then the whole process is connected, mScrollX
and mScrollY
each cycle will change a little, and then lead to the interface scrolling, resulting in an interface scroll effect.
PostScript
? The series of articles on Android Scroll is over and I hope you will learn useful knowledge from it. If there is any mistake or misunderstanding, please notify me in time. Thank you, readers and classmates.
Http://www.cppblog.com/fwxjj/archive/2013/01/13/197231.html
http://blog.csdn.net/luoshengyang/article/details/8372924
Android Scroll in detail (iii): Android drawing process detailed