Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Layout Traversals (Droidcon Turin 2015)
Search
Lucas Rocha
April 10, 2015
660
16
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Layout Traversals (Droidcon Turin 2015)
Presented at Droidcon Turin 2015
Lucas Rocha
April 10, 2015
More Decks by Lucas Rocha
See All by Lucas Rocha
Design-Engineering Workflow
lucasr
2
450
Layout Traversals (GDG Devfest 2014)
lucasr
9
580
Custom Layouts
lucasr
34
2k
Introducing Smoothie
lucasr
7
550
Bringing Firefox to Android
lucasr
6
640
A Million Times Pattrn
lucasr
6
470
Mozilla & Mobile
lucasr
2
230
The State of Firefox Mobile
lucasr
1
2.2k
Featured
See All Featured
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
AI in Enterprises - Java and Open Source to the Rescue
ivargrimstad
0
1.5k
Ruling the World: When Life Gets Gamed
codingconduct
0
400
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
350
Testing 201, or: Great Expectations
jmmastey
46
8.3k
WENDY [Excerpt]
tessaabrams
14
40k
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
850
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.8k
The Limits of Empathy - UXLibs8
cassininazir
2
720
Mobile First: as difficult as doing things right
swwweet
225
10k
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
Music & Morning Musume
bryan
48
7.4k
Transcript
LAYOUT TRAVERSALS Droidcon Turin, 2015
LUCAS ROCHA +LucasRocha | @lucasratmundo
None
BE DELIBERATE Less handwavy UI code
THE BASICS From inside out.
UI TOOLKITS Layout + Rendering + Input Events
OLD SCHOOL Nested boxes, rudimentary motion API.
PRE-JELLY BEAN public final class ViewRoot extends Handler ... {
... public void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; sendEmptyMessage(DO_TRAVERSAL); } } ... public void handleMessage(Message msg) { ... case DO_TRAVERSAL: performTraversals(); ... } } ViewRoot.java
MODERNIZE Predictable notion of time that drives input, layout, and
motion.
TICK TOCK VSYNC sets the pace. No tearing, no extra
work.
CHOREOGRAPHER f1 f2 f3 f4 f5 . . . .
. . VSYNC (60fps) Resize view Redraw view Input Events
CHOREOGRAPHER public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
... scheduleVsync(); ... } ... void doFrame(long frameTimeNanos, int frame) { ... if (!mFrameScheduled) { return; } ... doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); ... } Choreographer.java
ViewRootImpl Connects WindowManager with the View framework
THE ROOT * * SurfaceFlinger ViewRootImpl
VIEWS Measure + Layout + Draw
TRAVERSAL * performTraversals() f2 performTraversals()
MEASURE * measure(int, int) f2 M M M M M
M → onMeasure(int, int) measureHierarchy(...)
LAZY MEASURE Multi-MeasureSpec cache, invalidated in requestLayout()
LAZY MEASURE public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
... int cacheIndex = mMeasureCache.indexOfKey(key); if (cacheIndex < 0 || sIgnoreMeasureCache) { onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else { long value = mMeasureCache.valueAt(cacheIndex); setMeasuredDimensionRaw((int) (value >> 32), (int) value); mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } ... } View.java
LAZY MEASURE public void layout(int l, int t, int r,
int b) { if ((flags & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) { onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } ... } View.java
LAYOUT * f2 M L M L M L M
L M L M L layout(int, int, int, int) → onLayout(boolean, int, int, int, int) performLayout()
FRAME IT public void layout(int l, int t, int r,
int b) { ... boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b); ... } View.java
DRAW * f2 M L D M L D M
L D M L D M L D M L D draw(Canvas) → onDraw(Canvas) performDraw()
DISPLAY LISTS private DisplayList getDisplayList(...) { ... final HardwareCanvas canvas
= displayList.start(width, height); ... draw(canvas); ... displayList.end(); ... } View.java
INVARIANTS Layout means measurement is done. Drawing means layout is
done.
SMELLS 1. getMeasured*() calls outside onLayout() 2. New allocations during
traversal · onLayout: ok-ish · onMeasure: avoid · onDraw: nope 3. post(Runnable) to mean “after layout”
HAPPENS TO WORK public final class MyActivity extends Activity {
... @Override public void onCreate() { final View myView = findViewById(R.id.some_id); myView.post(new Runnable() { @Override public void run() { Log.d(“LOGTAG”, myView.getWidth()); } }); } ... }
LOOPER BARRIERS void scheduleTraversals() { ... mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); ...
} ViewRootImpl.java
TREE OBSERVER Use OnPreDrawListener!
TRANSITIONS http://lucasr.org/?p=3902
OnPreDrawListener // 1. Save layout state and wait for next
frame. getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() { @Override public boolean onPreDraw() { getViewTreeObserver().removeOnPreDrawListener(this); // 2. Restore original layout state. // 3. Trigger animators towards new layout state. } }
CHANGES Resize, redraw & animate.
* requestLayout() f1 f2 f3 f4 f5 . . .
. . .
requestLayout() public void requestLayout() { ... if (mParent != null
&& !mParent.isLayoutRequested()) { mParent.requestLayout(); } ... } void scheduleTraversals() { ... mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); ... } View.java ViewRootImpl.java
* Invalidate(...) f1 f2 f3 f4 f5 . . .
. . .
invalidate(...) public void invalidateInternal(...) { ... mPrivateFlags |= PFLAG_INVALIDATED; ...
if (mParent != null && mAttachInfo != null && l < r && t < b) { final Rect damage = mAttachInfo.mTmpInvalRect; damage.set(l, t, r, b); mParent.invalidateChild(this, damage); } ... } boolean draw(...) { ... mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) == PFLAG_INVALIDATED; ... } View.java
* postOnAnimation() f1 f2 f3 f4 f5 . . .
. . . ValueAnimator ...
postOnAnimation() public void postOnAnimation(Runnable action) { ... attachInfo.mViewRootImpl.mChoreographer.postCallback( Choreographer.CALLBACK_ANIMATION, action,
null); ... } void doFrame(long frameTimeNanos, int frame) { ... doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); ... } View.java Choreographer.java
BASIC TIPS 1. No layout requests during layout 2. No
layout requests during animations 3. Invalidate regions when possible
PERFORMANCE 1. Simplify your view hierarchy 2. Avoid multi-pass measurement
GO CUSTOM Simplify view hierarchy, performance, missing features. http://lucasr.org/?p=3920
PROBE IT! Intercept view methods. Dissect layout traversals. https://github.com/lucasr/probe
WRAP VIEW METHODS public class LogRequestLayout extends Interceptor { @Override
public void requestLayout(View view) { super.requestLayout(view); Log.d(LOGTAG, “requestLayout() on ” + view); } }
DEPLOY public final class MainActivity extends Activity { @Override protected
void onCreate(Bundle savedInstanceState) { Probe.deploy(this, new DrawGreen(), new Filter.ViewId(R.id.view2)); super.onCreate(savedInstanceState); setContentView(R.id.main_activity); } }
FANCY HACKING ON DEEP LAYOUT STUFF? We're hiring :-)
QUESTIONS? +LucasRocha | @lucasratmundo