├── .gitignore
├── LICENSE
├── README.md
├── _config.yml
├── app-debug.apk
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── amalbit
│ │ └── asynclayout
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── amalbit
│ │ │ └── asynclayout
│ │ │ └── LazyLayoutInflatorActivity.java
│ └── res
│ │ ├── layout
│ │ ├── activity_layout_inflation.xml
│ │ └── layout_dummy.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── amalbit
│ └── asynclayout
│ └── ExampleUnitTest.java
├── build.gradle
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | build/
19 |
20 | app/build/*
21 |
22 | # Local configuration file (sdk path, etc)
23 | local.properties
24 |
25 | # Proguard folder generated by Eclipse
26 | proguard/
27 |
28 | # Log Files
29 | *.log
30 |
31 | # Android Studio Navigation editor temp files
32 | .navigation/
33 |
34 | # Android Studio captures folder
35 | captures/
36 |
37 | # Intellij
38 | *.iml
39 | .idea/workspace.xml
40 | .idea/tasks.xml
41 | .idea/gradle.xml
42 | .idea/dictionaries
43 | .idea/libraries
44 |
45 | # Keystore files
46 | *.jks
47 |
48 | # External native build folder generated in Android Studio 2.2 and later
49 | .externalNativeBuild
50 |
51 | # Google Services (e.g. APIs or Firebase)
52 | google-services.json
53 |
54 | # Freeline
55 | freeline.py
56 | freeline/
57 | freeline_project_description.json
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Amal Chandran
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Problem
2 |
3 | Every app developer might have faced this issue. You get the design and mock up from the designer. You start the UI and implement all the animation your desginers need. Now when you start writing your business logic and integrate all your moving parts, you come across drops in your animations. You have no clue whats wrong.
4 |
5 | You break your head and run all kinds of profiling tools on your code during tight deadlines to ship your product. All this boils down to adding more load to your UI thread, when your UI thread is loaded, the choreographer drops frames. This is ripps ur animation off completely.
6 |
7 | ## Solution:
8 |
9 | When iOS and Andorid platforms were developed, the mobile devices at that time had limited computing power. So the UI rendering is only done on UI thread at one go. With the current generation phones, having multi cores, its not effectively used.
10 |
11 | To maintain the 60fps on your phones android released [Async layout inflator](https://developer.android.com/reference/android/support/v4/view/AsyncLayoutInflater.html). This is intended for parts of the UI that are created lazily or in response to user interactions. This allows the UI thread to continue to be responsive & animate while the relatively heavy inflate is being performed.
12 |
13 | ## Demo
14 |
15 | Check out the demo app which has a loader. Try inflating views with and without asynchronous layout to see the difference for yourself.
16 |
17 |
18 | ```markdown
19 | LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
20 | View view = inflater.inflate(R.layout.layout_dummy, null);
21 | LinearLayout inflatedLayout = (LinearLayout) view.findViewById(R.id.layout_dummy);
22 | parentLayout.addView(inflatedLayout);
23 | ```
24 |
25 | When you load normally, there will be flicker on the loader. With async, this flicker is reduced to great extent.
26 | [Demo video of layoutinflation wint async](https://youtu.be/ZETNOieGZLA)
27 |
28 | # Sample data:
29 | Inflating 100 layouts took 1004 milli seconds without async inflation and 557 milli seconds with it.
30 |
31 | Do try the poc to feel the difference.
32 |
33 |
34 | ## iOS
35 |
36 | For iOS take a look at https://github.com/facebook/AsyncDisplayKit .
37 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/app-debug.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amalChandran/asyncLayoutInflator/994b9f328c1f334cc0afd3b0bad1b50982a48b05/app-debug.apk
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | build/
19 |
20 | build/*
21 |
22 | # Local configuration file (sdk path, etc)
23 | local.properties
24 |
25 | # Proguard folder generated by Eclipse
26 | proguard/
27 |
28 | # Log Files
29 | *.log
30 |
31 | # Android Studio Navigation editor temp files
32 | .navigation/
33 |
34 | # Android Studio captures folder
35 | captures/
36 |
37 | # Intellij
38 | *.iml
39 | .idea/workspace.xml
40 | .idea/tasks.xml
41 | .idea/gradle.xml
42 | .idea/dictionaries
43 | .idea/libraries
44 |
45 | # Keystore files
46 | *.jks
47 |
48 | # External native build folder generated in Android Studio 2.2 and later
49 | .externalNativeBuild
50 |
51 | # Google Services (e.g. APIs or Firebase)
52 | google-services.json
53 |
54 | # Freeline
55 | freeline.py
56 | freeline/
57 | freeline_project_description.json
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "24.0.3"
6 | defaultConfig {
7 | applicationId "com.amalbit.asynclayout"
8 | minSdkVersion 16
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
25 | exclude group: 'com.android.support', module: 'support-annotations'
26 | })
27 | compile 'com.android.support:appcompat-v7:25.1.1'
28 | testCompile 'junit:junit:4.12'
29 | }
30 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/amal.chandran/Documents/Tools/adt-bundle-mac-x86_64-20140702/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/amalbit/asynclayout/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.amalbit.asynclayout;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest {
18 | @Test public void useAppContext() throws Exception {
19 | // Context of the app under test.
20 | Context appContext = InstrumentationRegistry.getTargetContext();
21 |
22 | assertEquals("com.amalbit.asynclayout", appContext.getPackageName());
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
9 |
10 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/amalbit/asynclayout/LazyLayoutInflatorActivity.java:
--------------------------------------------------------------------------------
1 | package com.amalbit.asynclayout;
2 |
3 | import android.content.Context;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.support.v4.view.AsyncLayoutInflater;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.support.v7.widget.SwitchCompat;
9 | import android.util.Log;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.Button;
14 | import android.widget.CompoundButton;
15 | import android.widget.EditText;
16 | import android.widget.LinearLayout;
17 |
18 | /**
19 | * Created by amal.chandran on 19/09/16.
20 | */
21 | public class LazyLayoutInflatorActivity extends AppCompatActivity implements View.OnClickListener{
22 |
23 | private LinearLayout parentLayout;
24 | private Button btnAdd;
25 | private Button btnRemove;
26 | private EditText edtCount;
27 | private SwitchCompat switchAsync;
28 |
29 | private boolean isAsyncOn = false;
30 |
31 | @Override
32 | protected void onCreate(@Nullable Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 | setContentView(R.layout.activity_layout_inflation);
35 | bindViews();
36 | addListners();
37 | }
38 |
39 | private void bindViews(){
40 | parentLayout = (LinearLayout) findViewById(R.id.layout_parent);
41 | btnAdd = (Button) findViewById(R.id.btn_add);
42 | btnRemove = (Button) findViewById(R.id.btn_remove);
43 | edtCount = (EditText) findViewById(R.id.edt_viewcount);
44 | switchAsync = (SwitchCompat) findViewById(R.id.switch1);
45 | }
46 |
47 | private void addListners(){
48 | btnAdd.setOnClickListener(this);
49 | btnRemove.setOnClickListener(this);
50 | switchAsync.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
51 | @Override
52 | public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
53 | isAsyncOn = b;
54 | }
55 | });
56 | }
57 | @Override
58 | public void onClick(View v) {
59 | switch (v.getId()){
60 | case R.id.btn_add:
61 | String number = edtCount.getText().toString();
62 | if(number.isEmpty())number = "refactor1";
63 | int count = Integer.parseInt(number);
64 | loadInsaneNumberOfViews(count);
65 | break;
66 | case R.id.btn_remove:
67 | parentLayout.removeAllViews();
68 | break;
69 | }
70 | }
71 |
72 | private void loadInsaneNumberOfViews(int count){
73 | for(int i = 0; i < count; i++){
74 | log(System.currentTimeMillis()+"");
75 | if(!isAsyncOn) {
76 | LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
77 | View view = inflater.inflate(R.layout.layout_dummy, null);
78 | LinearLayout inflatedLayout = (LinearLayout) view.findViewById(R.id.layout_dummy);
79 | parentLayout.addView(inflatedLayout);
80 | }else {
81 | AsyncLayoutInflater asyncInflator = new AsyncLayoutInflater(this);
82 | asyncInflator.inflate(R.layout.layout_dummy, null, new AsyncLayoutInflater.OnInflateFinishedListener() {
83 | @Override
84 | public void onInflateFinished(View view, int resid, ViewGroup parent) {
85 | LinearLayout inflatedLayout = (LinearLayout) view.findViewById(R.id.layout_dummy);
86 | parentLayout.addView(inflatedLayout);
87 | }
88 | });
89 | }
90 | }
91 | }
92 |
93 | private void log(String log){
94 | Log.i(LazyLayoutInflatorActivity.class.getSimpleName(), ""+log);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_layout_inflation.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
18 |
19 |
23 |
24 |
27 |
28 |
36 |
37 |
42 |
43 |
48 |
49 |
50 |
51 |
54 |
55 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_dummy.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
18 |
19 |
22 |
23 |
28 |
29 |
36 |
37 |
40 |
41 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amalChandran/asyncLayoutInflator/994b9f328c1f334cc0afd3b0bad1b50982a48b05/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amalChandran/asyncLayoutInflator/994b9f328c1f334cc0afd3b0bad1b50982a48b05/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amalChandran/asyncLayoutInflator/994b9f328c1f334cc0afd3b0bad1b50982a48b05/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amalChandran/asyncLayoutInflator/994b9f328c1f334cc0afd3b0bad1b50982a48b05/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amalChandran/asyncLayoutInflator/994b9f328c1f334cc0afd3b0bad1b50982a48b05/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #33312e
4 | #22201e
5 | #602015
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | asynclayout
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/amalbit/asynclayout/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.amalbit.asynclayout;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test public void addition_isCorrect() throws Exception {
14 | assertEquals(4, 2 + 2);
15 | }
16 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.2.3'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------