Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Deflating the LayoutInflater
Search
Saket Narayan
August 28, 2016
Programming
5
1.3k
Deflating the LayoutInflater
https://droidconin.talkfunnel.com/2016/86-deflating-the-layoutinflater
Saket Narayan
August 28, 2016
Tweet
Share
More Decks by Saket Narayan
See All by Saket Narayan
IllegalStateException: Can not perform this action after onSaveInstanceState
saketme
5
610
Building offline first apps
saketme
1
240
Other Decks in Programming
See All in Programming
Open source software: how to live long and go far
gaelvaroquaux
0
640
Software Architecture
hschwentner
6
2.1k
SpringBoot3.4の構造化ログ #kanjava
irof
2
1k
ペアーズでの、Langfuseを中心とした評価ドリブンなリリースサイクルのご紹介
fukubaka0825
2
330
Honoとフロントエンドの 型安全性について
yodaka
7
1.3k
ソフトウェアエンジニアの成長
masuda220
PRO
12
1.7k
CDK開発におけるコーディング規約の運用
yamanashi_ren01
2
140
Amazon S3 TablesとAmazon S3 Metadataを触ってみた / 20250201-jawsug-tochigi-s3tables-s3metadata
kasacchiful
0
170
GAEログのコスト削減
mot_techtalk
0
120
昭和の職場からアジャイルの世界へ
kumagoro95
1
380
PHPのバージョンアップ時にも役立ったAST
matsuo_atsushi
0
120
AIの力でお手軽Chrome拡張機能作り
taiseiue
0
170
Featured
See All Featured
Rebuilding a faster, lazier Slack
samanthasiow
80
8.8k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
PRO
12
960
jQuery: Nuts, Bolts and Bling
dougneiner
63
7.6k
What's in a price? How to price your products and services
michaelherold
244
12k
Product Roadmaps are Hard
iamctodd
PRO
50
11k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.4k
A designer walks into a library…
pauljervisheath
205
24k
Reflections from 52 weeks, 52 projects
jeffersonlam
348
20k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
656
59k
Docker and Python
trallard
44
3.3k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
46
2.3k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
30
4.6k
Transcript
Deflating the LayoutInflater Saket • Uncommon
Parts 1. What and how 2. Applications
Part 1, What is LayoutInflater
Converts View hierarchies written in XML into Java objects XML
<TextView android:layout_width="wrap_content" android:layout_height=“wrap_content" /> Java public class TextView extends View { ... }
Common Usages
Fragments @Override public View onCreateView(LayoutInflater, ViewGroup, Bundle) { return inflater.inflate(
R.layout.fragment_layout, container, false ); }
ViewHolders @Override public ViewHolder onCreateViewHolder(ViewGroup, int) { return new ViewHolder(inflater.inflate(
R.layout.fragment_layout, container, false )); }
Activities @Override protected void onCreate(Bundle savedState) { super.onCreate(savedState); setContentView(R.layout.activity_main); }
PhoneWindow.java (or AppCompatDelegateImplV7.java) @Override public void setContentView(int layoutRes) { mLayoutInflater.inflate(layoutRes,
mParent); }
How it works
LayoutInflater#inflate() A. XmlPullParser B. AttributeSet C. Creating View objects
LayoutInflater#inflate() A. XmlPullParser GSON, Moshi for JSON is the same
as XmlPullParser for XML
LayoutInflater#inflate() B. AttributeSet <ImageView android:layout_width="100dp" android:layout_height="100dp" android:src="@drawable/ic_done" android:contentDescription="Save" />
LayoutInflater#inflate() B. AttributeSet <ImageView android:layout_width="100dp" android:layout_height="100dp" android:src="@drawable/ic_done" android:contentDescription="Save" />
LayoutInflater#inflate() B. AttributeSet <ImageView android:layout_width="100dp" android:layout_height="100dp" android:src="@drawable/ic_done" android:contentDescription="Save" />
LayoutInflater#inflate() B. AttributeSet <ImageView android:layout_width="100dp" android:layout_height="100dp" android:src="@drawable/ic_done" android:contentDescription="Save" />
LayoutInflater#inflate() B. AttributeSet <ImageView android:layout_width="100dp" android:layout_height="100dp" android:src="@drawable/ic_done" android:contentDescription="Save" />
LayoutInflater#inflate() B. AttributeSet <ImageView android:layout_width="100dp" android:layout_height="100dp" android:src="@drawable/ic_done" android:contentDescription="Save" />
LayoutInflater#inflate() B. AttributeSet <ImageView android:layout_width="@dimen/list_icon" android:layout_height="@dimen/list_icon" android:src="@drawable/ic_done" android:contentDescription="Save" /> res/values/dimens.xml
LayoutInflater#inflate() B. AttributeSet Figures precedence of attributes <ImageView style="@style/Button.Save" android:layout_width="100dp"
android:layout_height="100dp" android:src=“@drawable/ic_done" />
LayoutInflater#inflate() B. AttributeSet Figures precedence of attributes <ImageView style="@style/Button.Save" android:layout_width="100dp"
android:layout_height="100dp" android:src=“@drawable/ic_done" />
LayoutInflater#inflate() B. AttributeSet Figures precedence of attributes <style name="Button.Save"> <item
name="android:layout_width">200dp</item> <item name="android:layout_height">200dp</item> </style>
LayoutInflater#inflate() B. AttributeSet Figures precedence of attributes <ImageView style="@style/Button.Save" android:layout_width="100dp"
android:layout_height="100dp" android:src=“@drawable/ic_done" />
Finding a View’s source Creating the View object using Reflection
LayoutInflater#inflate() C. Creating View Objects
Finding a View’s source Creating the View object using Reflection
LayoutInflater#inflate() C. Creating View Objects
Black magic Slow Reflection?
Delegate creation to “Factories” Instantiate View by itself (if #1
fails) Creating View Objects
Delegate creation to “Factories” Instantiate View by itself (if #1
fails) context.getClassLoader().loadClass(); Creating View Objects
Delegate creation to “Factories” Instantiate View by itself a) Custom
View b) Framework View Creating View Objects
a) Custom View: <android.support.v7.widget.RecyclerView android:layout_width="match_parent" android:layout_height=“match_parent" /> Creating View Objects
a) Custom View: Class<RecyclerView> clazz = ClassLoader#loadClass( "android.support.v7.widget.RecyclerView" ); RecyclerView
view = clazz.getConstructor().newInstance(); Creating View Objects
a) Framework View: <TextView android:layout_width="match_parent" android:layout_height="match_parent" /> Creating View Objects
a) Custom View: Class<TextView> clazz = ClassLoader#loadClass( "TextView" ); Creating
View Objects
a) Custom View: Class<TextView> clazz = ClassLoader#loadClass( "android.widget.TextView" ); Creating
View Objects
a) Custom View: Class<TextView> clazz = ClassLoader#loadClass( "android.widget.TextView" ); TextView
view = clazz.getConstructor().newInstance(); Creating View Objects
Delegate creation to “Factories” Instantiate View by itself Creating View
Objects
LayoutInflater Factories
Installing a Factory @Override protected void onCreate(Bundle savedState) { getLayoutInflater().setFactory2(...);
super.onCreate(savedState); }
Common Factories a) Activity public class Activity extends ContextThemeWrapper implements
LayoutInflater.Factory2 { }
Common Factories a) Activity
Common Factories a) Activity <LinearLayout> <fragment android:name="is.uncommon.ShinyFragment" android:layout_width="match_parent" android:layout_height="match_parent" />
</LinearLayout>
Common Factories a) Activity <LinearLayout> <fragment android:name="is.uncommon.ShinyFragment" android:layout_width="match_parent" android:layout_height="match_parent" />
</LinearLayout>
Common Factories b) AppCompat Continued in Part 2.
Part 2, Hooking into LayoutInflater
example, AppCompat How it back-ports material design to pre-Lollipop devices
android:theme
AppCompat Kitkat
AppCompat Kitkat
AppCompat <Button android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/custom_btn" /> custom_btn.xml <selector> @drawable/custom_btn_normal @drawable/custom_btn_focussed
@drawable/custom_btn_pressed </selector>
AppCompat <Button android:layout_width="match_parent" android:layout_height="match_parent" android:theme="@style/CustomButtonTheme" /> styles.xml <style name="CustomButtonTheme"> <item
name=“colorButtonNormal”>#09F</item> <item name=“colorControlHighlight">#07F</item> </style>
example, AppCompat AppCompatActivity @Override protected void onCreate(Bundle savedState) { installViewFactory();
... }
example, AppCompat AppCompatViewInflater.java implements LayoutInflater.Factory2 { ... }
example, AppCompat AppCompatViewInflater.java public View onCreateView(String viewName, ...) { if
("TextView".equals(viewName) { return new AppCompatTextView(); } ... }
example, AppCompat
example, Calligraphy How it applies custom fonts without using any
custom Views. <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" fontPath="fonts/Roboto-Bold.ttf" />
example, Calligraphy class CalligraphyLayoutInflater extends LayoutInflater { ... }
example, Calligraphy @Override View onCreateView(String name, AttributeSet) { View view
= super.onCreateView(...); if (view instanceOf TextView) { applyTypeface((TextView) view); } return view; }
example, Calligraphy @Override View onCreateView(String name, AttributeSet) { View view
= super.onCreateView(...); if (view instanceOf TextView) { applyTypeface((TextView) view); } return view; }
What if… We could do this? <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" formatter="IndianRupees"
/>
What if… We could do this? <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" formatter="IndianRupees"
/> 250000
What if… We could do this? <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" formatter="IndianRupees"
/> Ũ2,50,000
What if… Or this? <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" formatter="CreditCardNumber" />
What if… Or this? <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" formatter="CreditCardNumber" /> 4652
3700 0055 5032
What if… Or this? <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" formatter="CreditCardNumber" /> 3400
123456 000032
What if… Or maybe this? <FrameLayout android:layout_width="wrap_content" android:layout_height=“wrap_content" paddings="16dp 20dp"
/>
WHAT IF I TOLD YOU IT’S POSSIBLE TO DO THAT?
Introducing, Balloon!
Balloon interface BalloonAttributeListener { void onApplyAttribute( View view, int attributeResId,
Object attributeValue ); }
Application#onCreate() Balloon.config() .registerAttr(R.attr.formatter, FormatterAttrListener) .build(); Balloon
Application#onCreate() Balloon.config() .registerAttr(R.attr.formatter, FormatterAttrListener) .build(); Balloon
Application#onCreate() Balloon.config() .registerAttr(R.attr.formatter, FormatterAttrListener) .build(); Balloon
Balloon class FormatterAttrListener implements BalloonAttributeListener <> { @Override void onApplyAttribute(EditText
view, int attrResId, String attrValue) { if ("IndianRupees".equals(attrValue)) { // Add rupee symbol and format digits } } }
Pros Composition > Inheritance Can be used for multiple types
of Views Quick
Cons “Experimental” No layout preview in Android Studio
Working class BalloonLayoutInflater extends LayoutInflater { ... }
Challenges Activity getLayoutInflater() -> return LayoutInflater
Challenges Activity getLayoutInflater() -> return LayoutInflater -> return BalloonLayoutInflater
Challenges public class BalloonContext extends Context { ... }
Challenges Activity @Override void attachBaseContext(Context base) { super.attachBaseContext(BalloonContext.wrap(base)); }
Challenges Activity @Override void attachBaseContext(Context base) { super.attachBaseContext(BalloonContext.wrap(base)); }
Challenges BalloonContext @Override Object getSystemService(String name) { if (LAYOUT_INFLATER_SERVICE.equals(name)) {
return new BalloonLayoutInflater(); } return super.getSystemService(name); }
Challenges BalloonContext @Override Object getSystemService(String name) { if (LAYOUT_INFLATER_SERVICE.equals(name)) {
return new BalloonLayoutInflater(); } return super.getSystemService(name); }
Challenges Activity getLayoutInflater() -> return BalloonLayoutInflater
saket.me/droidcon
Learnings Layout inflation is a slow process. Complex layouts can
affect an Activity’s startup time.
Learnings Why non-framework Views require their fully qualified names in
XML whereas framework Views don’t. <is.uncommon.FancyCustomView /> vs. <TextView />
That’s all folks!
Uncommon hello @ uncommon.is
Questions?