android drawing and graphics api
TRANSCRIPT
Android drawing & graphics API
JorgeCastilloPrz
@JorgeCastilloPr
Jorge Castillo
Android Engineer at
Custom Views
● Good way to wrap drawing logic semantically
● Simplify layout composition
● Extend from View / ViewGroup or whatever fits better
● Custom attributes to customize behavior
● Use <merge> tag on layout root (performance)
How Android draws its Views
● It requests the layout’s root node to measure and draw it’s tree
● Tree traversal is pre-order
○ Parents are drawn before their children
○ Siblings drawn in the order they are in the tree
● ViewGroups are responsible for requesting
its children to be drawn
● Every View is responsible for drawing itself F, B, A, D, C, E, G, I, H
Drawing steps
● Measure pass - measure(int, int)
● Layout pass - layout(int, int, int, int)
The draw itself is internally divided in two steps:
measure(int, int)
● Top to bottom
● At the end, getMeasuredWidth() and getMeasuredHeight() must be set for
current view and all its descendants
● Each view pushes dimension specifications down the tree during recursion
● Measured width and height must respect constraints imposed by parent
● A parent may call measure() more than once on its children. (If the children
does not agree on dimensions among themselves).
(1/3)
measure(int, int)
● MeasureSpec are used to push requirements down the tree. Three modes
available:
○ UNESPECIFIED: Used by parents to determine the desired dimen of a
child. (LinearLayout calls measure() on its child with a width of 240 and
height UNESPECIFIED to find out how tall the child would be for an
exact given width)
○ EXACTLY: Used by parent to impose exact size on the child
○ AT_MOST: Used by the parent to impose a maximum size on the child
(2/3)
● MeasureSpec are implemented as ints to reduce object allocation
● Class MeasureSpec is used to pack / unpack <size, mode> tuples into the
given int arguments
● MeasureSpec.getMode(int)
● MeasureSpec.getSize(int)
● MeasureSpec.makeMeasureSpec(int size, int mode) to create a new one
measure(int, int) (3/3)
layout(int, int, int, int)
● Top to bottom
● To assign position to a view and all its descendants
● Each parent is responsible for positioning all of its children using the sizes
computed in the measure pass
● Each parent calls layout() on all of its children
● Four given int arguments are the Left, Top, Right and Bottom positions,
relative to the view’s parent
More about drawing
● Override onSizeChanged() to update your view calculations,
don’t do it in the onDraw() method
● 16 ms / 60 fps every drawing cycle for smoothness
● Update the drawing logic according to states
● You can force a View to draw calling invalidate()
● You can force init the layout pass using requestLayout()
● Keep an eye on overdraw
Detecting Overdraw
● When a time frame of pixels are drawn
multiple times (more rendering work than
necessary)
● Enable “Show overdraw areas” in your
developer console
● Some overdraw is unavoidable. Try to get
mostly true color or 1X as maximum
Some causes
● Multiple full screen background
○ DecorView theme bg can be removed
(after the brand screen is gone):
getWindow().setBackgroundDrawable(null)
● Backgrounds behind avatars to mimic borders
● View hierarchy too deep (Flatten it as much as
possible. Use Hierarchy Viewer to check it)
About Canvas
● Draw to Canvas when your View needs to re-draw so often.
● You can use it into a View’s onDraw() method or into SurfaceView /
TextureView
● TetureView is like a SurfaceView but behaves as a View, and not just as a
dumb Surface. So it can be moved, transformed, animated… etc.
● SurfaceView and TextureView perform draws to the Canvas as fast as your
thread is capable of
(1/3)
About Canvas
● Canvas relies on underlying Bitmap
● To create a new Canvas from zero you need to provide it
(2/3)
● Multiple clipping and drawing methods
● Every drawing method receives a Paint
About the Paint
● Paint is our drawing tool. Like a brush.
● Paints are configured properly to be used in canvas methods
AndroidFillableLoaders
https://github.com/JorgeCastilloPrz/AndroidFillableLoaders
http://jorgecastillo.xyz
ClippingTransform
● Every onDraw() tick we draw depending o the current phase
○ build clipping path
○ apply an offset for the height depending on the phase
○ apply the clipping Path