SlideShare una empresa de Scribd logo
1 de 105
Descargar para leer sin conexión
Building UI Consistent Android Apps
Nicola Corti
@cortinico
nco@yelp.com
Yelp Mission
Connecting people with great local businesses.
About me
Nicola Corti
Android @BizCore
nco@yelp.com
@cortinico
Community Addicted
!🍕🕹🎤📸✈🏞
What is
Consistency?
“Unified use of Design Elements, such as color,
typography, spatial layout and behaviors.
Functional
Internal Consistency
Visual
External Consistency - Across Product
External Consistency - Across Platform
Benefits
• Learnability
• Reduce frustrations
• Save money/time 💰
Photo by davelawler/CC BY - NC
How to tackle
Consistency?
⌘ R⇧+ +
Source: GIPHY
External Examples
• Google Material Design
• Apple Design Guidelines
• Github Primer
http://styleguides.io/
github.com/alexpate/awesome-design-systems
Consistency
@Yelp
Mobile Apps
yelp.com/styleguide
The Android
Styleguide
Library
Design Build Share
Design
Does it fit?
• Can it be reused?
• Is it visible to the user?
• Development plan?
User photo User name timestamp
friends, media
checkins
Elite badge
description
Attributes
<resources>

</resources>
Attributes
<resources>

<declare-styleable name="UserPassport">

</declare-styleable>

</resources>
Attributes
<resources>

<declare-styleable name="UserPassport">

<!-- Determines user's name -->

<attr name="userPassportName" format="string"/>

</declare-styleable>

</resources>
Attributes
<resources>

<declare-styleable name="UserPassport">

<!-- Determines user's name -->

<attr name="userPassportName" format="string"/>



<!-- Determines user's description/role -->

<attr name="userPassportDescription" format="string"/>

</declare-styleable>

</resources>
Attributes
<resources>

<declare-styleable name="UserPassport">

<!-- Determines user's name -->

<attr name="userPassportName" format="string"/>



<!-- Determines user's description/role -->

<attr name="userPassportDescription" format="string"/>

</declare-styleable>

<attr name="userPassportStyle" format="reference"/>

</resources>
Theme
Theme
<resources>

<!—- This theme is the parent of all themes of Yelp's android apps. —->

<style name="YelpStyleguideTheme"/>

</resources>
Theme
<resources>

<!—- This theme is the parent of all themes of Yelp's android apps. —->

<style name="YelpStyleguideTheme" parent=“Theme.AppCompat.Light.DarkActionBar"/>

</resources>
Theme
<resources>

<!—- This theme is the parent of all themes of Yelp's android apps. —->

<style name="YelpStyleguideTheme" parent=“Theme.AppCompat.Light.DarkActionBar">

<item name="userPassportStyle">@style/UserPassport</item>

</style>

</resources>
Styles
Styles
<style name=“UserPassport"/>
Styles
<style name=“UserPassport">

<item name="userPassportName">Joe Smith</item>

</style>
Styles
<style name=“UserPassport">

<item name="userPassportName">Joe Smith</item>

<item name="userPassportDescription">Owner of Sample Business</item>

</style>
UserPassport
public class UserPassport extends RelativeLayout {

public class UserPassport extends RelativeLayout {



private TextView mUserName;

private TextView mDescription;

public class UserPassport extends RelativeLayout {



private TextView mUserName;

private TextView mDescription;



public void setName(String name) {

mUserName.setText(name);

}





public class UserPassport extends RelativeLayout {



private TextView mUserName;

private TextView mDescription;



public void setName(String name) {

mUserName.setText(name);

}



public void setDescription(String description) {

if (TextUtils.isEmpty(description)) {

mDescription.setVisibility(GONE);

} else {

mDescription.setVisibility(VISIBLE);

mDescription.setText(description);

}

}



public class UserPassport extends RelativeLayout {



private TextView mUserName;

private TextView mDescription;

public class UserPassport extends RelativeLayout {



private TextView mUserName;

private TextView mDescription;



public UserPassport(final Context context) {

super(context);

init(context, null, 0);

}



public UserPassport(final Context context, final AttributeSet attrs) {

super(context, attrs);

init(context, attrs, R.attr.userPassportStyle);

}



public UserPassport(final Context context, final AttributeSet attrs,
final int defStyleAttr) {

super(context, attrs, defStyleAttr);

init(context, attrs, defStyleAttr);

}
public class UserPassport extends RelativeLayout {



private TextView mUserName;

private TextView mDescription;



public UserPassport(final Context context) {

super(context);

init(context, null, 0);

}



public UserPassport(final Context context, final AttributeSet attrs) {

super(context, attrs);

init(context, attrs, R.attr.userPassportStyle);

}



public UserPassport(final Context context, final AttributeSet attrs,
final int defStyleAttr) {

super(context, attrs, defStyleAttr);

init(context, attrs, defStyleAttr);

}
public class UserPassport extends RelativeLayout {



private TextView mUserName;

private TextView mDescription;



private void init(

final Context context, final AttributeSet attrs, final int defStyleAttr) {


}



public class UserPassport extends RelativeLayout {



private TextView mUserName;

private TextView mDescription;



private void init(

final Context context, final AttributeSet attrs, final int defStyleAttr) {


LayoutInflater.from(context).inflate(R.layout.user_passport, this, true);


}



public class UserPassport extends RelativeLayout {



private TextView mUserName;

private TextView mDescription;



private void init(

final Context context, final AttributeSet attrs, final int defStyleAttr) {


LayoutInflater.from(context).inflate(R.layout.user_passport, this, true);


mUserName = (TextView) findViewById(R.id.user_name);

mDescription = (TextView) findViewById(R.id.description);



}



public class UserPassport extends RelativeLayout {



private TextView mUserName;

private TextView mDescription;



private void init(

final Context context, final AttributeSet attrs, final int defStyleAttr) {


LayoutInflater.from(context).inflate(R.layout.user_passport, this, true);


mUserName = (TextView) findViewById(R.id.user_name);

mDescription = (TextView) findViewById(R.id.description);



final TypedArray styles = context.obtainStyledAttributes(attrs,
R.styleable.UserPassport, defStyleAttr, 0);



}

public class UserPassport extends RelativeLayout {



private TextView mUserName;

private TextView mDescription;



private void init(

final Context context, final AttributeSet attrs, final int defStyleAttr) {


LayoutInflater.from(context).inflate(R.layout.user_passport, this, true);


mUserName = (TextView) findViewById(R.id.user_name);

mDescription = (TextView) findViewById(R.id.description);



final TypedArray styles = context.obtainStyledAttributes(attrs,
R.styleable.UserPassport, defStyleAttr, 0);



setName(styles.getString(R.styleable.UserPassport_userPassportName));
setDescription(styles.getString(
R.styleable.UserPassport_userPassportDescription));



}



public class UserPassport extends RelativeLayout {



private TextView mUserName;

private TextView mDescription;



private void init(

final Context context, final AttributeSet attrs, final int defStyleAttr) {


LayoutInflater.from(context).inflate(R.layout.user_passport, this, true);


mUserName = (TextView) findViewById(R.id.user_name);

mDescription = (TextView) findViewById(R.id.description);



final TypedArray styles = context.obtainStyledAttributes(attrs,
R.styleable.UserPassport, defStyleAttr, 0);



setName(styles.getString(R.styleable.UserPassport_userPassportName));
setDescription(styles.getString(
R.styleable.UserPassport_userPassportDescription));



styles.recycle();

}



<style name="UserPassport">

<item name="userPassportName">Joe Smith</item>

<item name="userPassportDescription">@null</item>

<item name="userPassportTint">@color/orange_dark_interface</item>

<item name="userPassportNameColor">@color/black_regular_interface</item>

<item name="userPassportSize">Regular</item>

<item name="userPassportShowName">true</item>

<item name="userPassportShowIcons">true</item>

<item name="userPassportEliteYear">-1</item>

<item name="userPassportFriends">0</item>

<item name="userPassportReviews">0</item>

<item name="userPassportPhotos">0</item>

<item name="userPassportCheckIns">0</item>

<item name="userPassportShowCheckIn">false</item>

</style>
<style name="UserPassport.White">

<item name="userPassportName">Joe Smith</item>

<item name="userPassportDescription">@null</item>

<item name="userPassportTint">@color/orange_dark_interface</item>

<item name="userPassportNameColor">@color/black_regular_interface</item>

<item name="userPassportSize">Regular</item>

<item name="userPassportShowName">true</item>

<item name="userPassportShowIcons">true</item>

<item name="userPassportEliteYear">-1</item>

<item name="userPassportFriends">0</item>

<item name="userPassportReviews">0</item>

<item name="userPassportPhotos">0</item>

<item name="userPassportCheckIns">0</item>

<item name="userPassportShowCheckIn">false</item>

</style>
<style name="UserPassport.White">

<item name="userPassportName">Joe Smith</item>

<item name="userPassportDescription">@null</item>

<item name="userPassportTint">@color/white_interface</item>

<item name="userPassportNameColor">@color/white_interface</item>

<item name="userPassportSize">Regular</item>

<item name="userPassportShowName">true</item>

<item name="userPassportShowIcons">true</item>

<item name="userPassportEliteYear">-1</item>

<item name="userPassportFriends">0</item>

<item name="userPassportReviews">0</item>

<item name="userPassportPhotos">0</item>

<item name="userPassportCheckIns">0</item>

<item name="userPassportShowCheckIn">false</item>

</style>
<style name="UserPassport.White">

<item name="userPassportTint">@color/white_interface</item>

<item name="userPassportNameColor">@color/white_interface</item>

</style>
Color Palette
Illustrations
Assets
dependencies {



// Yelp asset libs

compile 'com.yelp:yelpicons:135.0.0'

compile ‘com.yelp:yelpdesign:4.0.4’
}
Assets
dependencies {



// Yelp asset libs

compile 'com.yelp:yelpicons:135.0.0'

compile ‘com.yelp:yelpdesign:4.0.4’
}
Color
Color
<color name="black_extra_light_interface">#666666</color>

<color name="black_regular_interface">#333333</color>

<color name="blue_dark_interface">#0073bb</color>

<color name="blue_extra_light_interface">#d0ecfb</color>

<color name="blue_regular_interface">#0097ec</color>

<color name="gray_dark_interface">#999999</color>

<color name="gray_extra_light_interface">#f5f5f5</color>

<color name="gray_light_interface">#e6e6e6</color>

<color name="gray_regular_interface">#cccccc</color>

<color name="green_extra_light_interface">#daecd2</color>

<color name="green_regular_interface">#41a700</color>

<color name="mocha_extra_light_interface">#f8e3c7</color>

<color name="mocha_light_interface">#f1bd79</color>

<color name="orange_dark_interface">#f15c00</color>

<color name="orange_extra_light_interface">#ffebcf</color>

<color name="purple_extra_light_interface">#dad1e4</color>

<color name="red_dark_interface">#d32323</color>

<color name="red_extra_light_interface">#fcd6d3</color>

<color name="slate_extra_light_interface">#cddae2</color>

<color name="white_interface">#ffffff</color>

<color name="yellow_dark_interface">#fec011</color>

<color name="yellow_extra_light_interface">#fff7cc</color>
Color
<color name="black_extra_light_interface">#666666</color>

<color name="black_regular_interface">#333333</color>

<color name="blue_dark_interface">#0073bb</color>

<color name="blue_extra_light_interface">#d0ecfb</color>

<color name="blue_regular_interface">#0097ec</color>

<color name="gray_dark_interface">#999999</color>

<color name="gray_extra_light_interface">#f5f5f5</color>

<color name="gray_light_interface">#e6e6e6</color>

<color name="gray_regular_interface">#cccccc</color>

<color name="green_extra_light_interface">#daecd2</color>

<color name="green_regular_interface">#41a700</color>

<color name="mocha_extra_light_interface">#f8e3c7</color>

<color name="mocha_light_interface">#f1bd79</color>

<color name="orange_dark_interface">#f15c00</color>

<color name="orange_extra_light_interface">#ffebcf</color>

<color name="purple_extra_light_interface">#dad1e4</color>

<color name="red_dark_interface">#d32323</color>

<color name="red_extra_light_interface">#fcd6d3</color>

<color name="slate_extra_light_interface">#cddae2</color>

<color name="white_interface">#ffffff</color>

<color name="yellow_dark_interface">#fec011</color>

<color name="yellow_extra_light_interface">#fff7cc</color>
Color
<color name="black_extra_light_interface">#666666</color>

<color name="black_regular_interface">#333333</color>

<color name="blue_dark_interface">#0073bb</color>

<color name="blue_extra_light_interface">#d0ecfb</color>

<color name="blue_regular_interface">#0097ec</color>

<color name="gray_dark_interface">#999999</color>

<color name="gray_extra_light_interface">#f5f5f5</color>

<color name="gray_light_interface">#e6e6e6</color>

<color name="gray_regular_interface">#cccccc</color>

<color name="green_extra_light_interface">#daecd2</color>

<color name="green_regular_interface">#41a700</color>

<color name="mocha_extra_light_interface">#f8e3c7</color>

<color name="mocha_light_interface">#f1bd79</color>

<color name="orange_dark_interface">#f15c00</color>

<color name="orange_extra_light_interface">#ffebcf</color>

<color name="purple_extra_light_interface">#dad1e4</color>

<color name="red_dark_interface">#d32323</color>

<color name="red_extra_light_interface">#fcd6d3</color>

<color name="slate_extra_light_interface">#cddae2</color>

<color name="white_interface">#ffffff</color>

<color name="yellow_dark_interface">#fec011</color>

<color name="yellow_extra_light_interface">#fff7cc</color>
Build
Review Template
VCS & CI
• git submodule
• Run the Build for
• submodule
• consumer app
• business app
Custom Lint Checks
Custom Lint Checks


Button b = new Button(context);



SwitchCompat switchCompat = new SwitchCompat(context);



Snackbar.make(getRootView(), “Test”, LENGTH_SHORT).show();
Custom Lint Checks
@SuppressLint("")

Button b = new Button(context);



@SuppressLint("")

SwitchCompat switchCompat = new SwitchCompat(context);



@SuppressLint("")

Snackbar.make(getRootView(), “Test”, LENGTH_SHORT).show();
Custom Lint Checks
@SuppressLint("NonStyleguideButtonInstance")

Button b = new Button(context);



@SuppressLint("")

SwitchCompat switchCompat = new SwitchCompat(context);



@SuppressLint("")

Snackbar.make(getRootView(), “Test”, LENGTH_SHORT).show();
Custom Lint Checks
@SuppressLint("NonStyleguideButtonInstance")

Button b = new Button(context);



@SuppressLint("NonStyleguideToggleInstance")

SwitchCompat switchCompat = new SwitchCompat(context);



@SuppressLint("")

Snackbar.make(getRootView(), “Test”, LENGTH_SHORT).show();
Custom Lint Checks
@SuppressLint("NonStyleguideButtonInstance")

Button b = new Button(context);



@SuppressLint("NonStyleguideToggleInstance")

SwitchCompat switchCompat = new SwitchCompat(context);



@SuppressLint("NonStyleguideSnackbarInstance")

Snackbar.make(getRootView(), “Test”, LENGTH_SHORT).show();
Custom Lint Checks
// Using stock Button because […] + Ticket number.
@SuppressLint("NonStyleguideButtonInstance")

Button b = new Button(context);



@SuppressLint("NonStyleguideToggleInstance")

SwitchCompat switchCompat = new SwitchCompat(context);



@SuppressLint("NonStyleguideSnackbarInstance")

Snackbar.make(getRootView(), “Test”, LENGTH_SHORT).show();
Custom Lint Checks
Custom Lint Checks
<Button

android:layout_width="match_parent"

android:layout_height="match_parent" />



<Switch

android:layout_width="match_parent"

android:layout_height="match_parent" />
Custom Lint Checks
<Button

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:ignore="NonStyleguideButtonTag" />



<Switch

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:ignore="NonStyleguideToggleTag" />
build.gradle
build.gradle
android {

lintOptions {



}

}
build.gradle
android {

lintOptions {

abortOnError true

warningsAsErrors true



}

}
build.gradle
android {

lintOptions {

abortOnError true

warningsAsErrors true



lintConfig file("lint.xml")

}

}
build.gradle
android {

lintOptions {

abortOnError true

warningsAsErrors true



lintConfig file("lint.xml")

baseline file("lint-baseline.xml")

}

}
Test your component
• Your component ❤ Espresso?
• Do you handle state changes?
• contentDescription ?
Share
Documentation
• Provide Javadoc
• Add Screenshots
• Document Attributes
• Document Styles
Screenshots capture
• v0.1: Manual Screenshots
• v0.2: Automated locally
• v0.3: Automated with CI 💫
StyleguideTestApp
• Components Showcase
• For Designer 🎨
• For Developer 🔧
Taking Screenshots with Espresso
public class ScreenshotViewActions {



}
Taking Screenshots with Espresso
public class ScreenshotViewActions {



public static ViewAction screenshot(final String folderName, final String fileName) {

return new ViewAction() {



};

}

}
Taking Screenshots with Espresso
public class ScreenshotViewActions {



public static ViewAction screenshot(final String folderName, final String fileName) {

return new ViewAction() {

// Other methods omitted.



@Override

public void perform(UiController uiController, View view) {

ScreenshotsUtil.takeScreenshot(folderName, fileName, view);

}

};

}

}
Sample Espresso Test
Sample Espresso Test
public class StarsViewActivityTests {



}
Sample Espresso Test
public class StarsViewActivityTests {



@Test

public void takeScreenshot() throws InterruptedException {

onView(withId(R.id.stars_view_4)).perform(setStarsNumber(4));



}

}
Sample Espresso Test
public class StarsViewActivityTests {



@Test

public void takeScreenshot() throws InterruptedException {

onView(withId(R.id.stars_view_4)).perform(setStarsNumber(4));

onView(withId(R.id.stars_view_5)).perform(setStarsNumber(5),

screenshot(FOLDER_NAME, "stars_with_text"));



}

}
Sample Espresso Test
public class StarsViewActivityTests {



@Test

public void takeScreenshot() throws InterruptedException {

onView(withId(R.id.stars_view_4)).perform(setStarsNumber(4));

onView(withId(R.id.stars_view_5)).perform(setStarsNumber(5),

screenshot(FOLDER_NAME, "stars_with_text"));



ScreenshotUtil.fullScreenshot(FOLDER_NAME, "stars_fullscreen");

}

}
Bend it, don’t break it!
Source: GIPHY
We are hiring!
www.yelp.com/careers/
Nicola Corti
@cortinico
nco@yelp.com
bit.ly/uiconsistency
@YelpEngineering
github.com/yelp
yelp.com/careers
engineeringblog.yelp.com

Más contenido relacionado

Destacado

Codemotion rome 2015 bluemix lab tutorial -- Codemotion Rome 2015
Codemotion rome 2015   bluemix lab tutorial -- Codemotion Rome 2015Codemotion rome 2015   bluemix lab tutorial -- Codemotion Rome 2015
Codemotion rome 2015 bluemix lab tutorial -- Codemotion Rome 2015
Codemotion
 
Lean@core lean startup e cloud- - Codemotion Rome 2015
Lean@core   lean startup e cloud- - Codemotion Rome 2015Lean@core   lean startup e cloud- - Codemotion Rome 2015
Lean@core lean startup e cloud- - Codemotion Rome 2015
Codemotion
 
Engineering Design for Facebook
Engineering Design for FacebookEngineering Design for Facebook
Engineering Design for Facebook
Codemotion
 

Destacado (20)

Anna Makarudze - Django Girls: Inspiring women to fall in love with programmi...
Anna Makarudze - Django Girls: Inspiring women to fall in love with programmi...Anna Makarudze - Django Girls: Inspiring women to fall in love with programmi...
Anna Makarudze - Django Girls: Inspiring women to fall in love with programmi...
 
Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...
Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...
Alberto Maria Angelo Paro - Isomorphic programming in Scala and WebDevelopmen...
 
Claudio Carboni - ArcGIS platformthe foundation of your idea - Codemotion Mil...
Claudio Carboni - ArcGIS platformthe foundation of your idea - Codemotion Mil...Claudio Carboni - ArcGIS platformthe foundation of your idea - Codemotion Mil...
Claudio Carboni - ArcGIS platformthe foundation of your idea - Codemotion Mil...
 
Giovanni Laquidara - Hello ARCore - Codemotion Milan 2017
Giovanni Laquidara - Hello ARCore - Codemotion Milan 2017Giovanni Laquidara - Hello ARCore - Codemotion Milan 2017
Giovanni Laquidara - Hello ARCore - Codemotion Milan 2017
 
James Williams - Demystifying Constraint Layout - Codemotion Milan 2017
James Williams - Demystifying Constraint Layout - Codemotion Milan 2017James Williams - Demystifying Constraint Layout - Codemotion Milan 2017
James Williams - Demystifying Constraint Layout - Codemotion Milan 2017
 
Tiffany Conroy - Remote device sign-in – Authenticating without a keyboard - ...
Tiffany Conroy - Remote device sign-in – Authenticating without a keyboard - ...Tiffany Conroy - Remote device sign-in – Authenticating without a keyboard - ...
Tiffany Conroy - Remote device sign-in – Authenticating without a keyboard - ...
 
Matteo Manchi - React Native for multi-platform mobile applications - Codemot...
Matteo Manchi - React Native for multi-platform mobile applications - Codemot...Matteo Manchi - React Native for multi-platform mobile applications - Codemot...
Matteo Manchi - React Native for multi-platform mobile applications - Codemot...
 
Erik Tiengo - Embedding Cisco Spark and Location applications (ESRI) into bus...
Erik Tiengo - Embedding Cisco Spark and Location applications (ESRI) into bus...Erik Tiengo - Embedding Cisco Spark and Location applications (ESRI) into bus...
Erik Tiengo - Embedding Cisco Spark and Location applications (ESRI) into bus...
 
Erik Wendel - Beyond JavaScript Frameworks: Writing Reliable Web Apps With El...
Erik Wendel - Beyond JavaScript Frameworks: Writing Reliable Web Apps With El...Erik Wendel - Beyond JavaScript Frameworks: Writing Reliable Web Apps With El...
Erik Wendel - Beyond JavaScript Frameworks: Writing Reliable Web Apps With El...
 
Agnieszka Naplocha - Breaking the norm with creative CSS - Codemotion Milan 2017
Agnieszka Naplocha - Breaking the norm with creative CSS - Codemotion Milan 2017Agnieszka Naplocha - Breaking the norm with creative CSS - Codemotion Milan 2017
Agnieszka Naplocha - Breaking the norm with creative CSS - Codemotion Milan 2017
 
Oded Coster - Stack Overflow behind the scenes - how it's made - Codemotion M...
Oded Coster - Stack Overflow behind the scenes - how it's made - Codemotion M...Oded Coster - Stack Overflow behind the scenes - how it's made - Codemotion M...
Oded Coster - Stack Overflow behind the scenes - how it's made - Codemotion M...
 
Cutting the Fat
Cutting the FatCutting the Fat
Cutting the Fat
 
9
99
9
 
Codemotion rome 2015 bluemix lab tutorial -- Codemotion Rome 2015
Codemotion rome 2015   bluemix lab tutorial -- Codemotion Rome 2015Codemotion rome 2015   bluemix lab tutorial -- Codemotion Rome 2015
Codemotion rome 2015 bluemix lab tutorial -- Codemotion Rome 2015
 
8
88
8
 
Lean@core lean startup e cloud- - Codemotion Rome 2015
Lean@core   lean startup e cloud- - Codemotion Rome 2015Lean@core   lean startup e cloud- - Codemotion Rome 2015
Lean@core lean startup e cloud- - Codemotion Rome 2015
 
5
55
5
 
Engineering Design for Facebook
Engineering Design for FacebookEngineering Design for Facebook
Engineering Design for Facebook
 
6
66
6
 
29
2929
29
 

Similar a Nicola Corti - Building UI Consistent Android Apps - Codemotion Milan 2017

Custom UI Components at Android Only 2011
Custom UI Components at Android Only 2011Custom UI Components at Android Only 2011
Custom UI Components at Android Only 2011
Johan Nilsson
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android Infrastructure
Alexey Buzdin
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android Infrastructure
C.T.Co
 

Similar a Nicola Corti - Building UI Consistent Android Apps - Codemotion Milan 2017 (20)

Custom UI Components at Android Only 2011
Custom UI Components at Android Only 2011Custom UI Components at Android Only 2011
Custom UI Components at Android Only 2011
 
Recyclerview in action
Recyclerview in action Recyclerview in action
Recyclerview in action
 
Data binding в массы! (1.2)
Data binding в массы! (1.2)Data binding в массы! (1.2)
Data binding в массы! (1.2)
 
Михаил Анохин "Data binding 2.0"
Михаил Анохин "Data binding 2.0"Михаил Анохин "Data binding 2.0"
Михаил Анохин "Data binding 2.0"
 
"Android Data Binding в массы" Михаил Анохин
"Android Data Binding в массы" Михаил Анохин"Android Data Binding в массы" Михаил Анохин
"Android Data Binding в массы" Михаил Анохин
 
Intro to Dependency Injection - Or bar
Intro to Dependency Injection - Or bar Intro to Dependency Injection - Or bar
Intro to Dependency Injection - Or bar
 
Architecture components - IT Talk
Architecture components - IT TalkArchitecture components - IT Talk
Architecture components - IT Talk
 
Architecture Components
Architecture Components Architecture Components
Architecture Components
 
07_UIAndroid.pdf
07_UIAndroid.pdf07_UIAndroid.pdf
07_UIAndroid.pdf
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android Infrastructure
 
Overview of Android Infrastructure
Overview of Android InfrastructureOverview of Android Infrastructure
Overview of Android Infrastructure
 
Boost Your Development With Proper API Design
Boost Your Development With Proper API DesignBoost Your Development With Proper API Design
Boost Your Development With Proper API Design
 
Android Architecture Components - Guy Bar on, Vonage
Android Architecture Components - Guy Bar on, VonageAndroid Architecture Components - Guy Bar on, Vonage
Android Architecture Components - Guy Bar on, Vonage
 
Binding business data to vaadin components
Binding business data to vaadin componentsBinding business data to vaadin components
Binding business data to vaadin components
 
Speaking Eloquent Eloquently
Speaking Eloquent EloquentlySpeaking Eloquent Eloquently
Speaking Eloquent Eloquently
 
Laravel - Speaking eloquent eloquently
Laravel - Speaking eloquent eloquentlyLaravel - Speaking eloquent eloquently
Laravel - Speaking eloquent eloquently
 
Codemotion appengine
Codemotion appengineCodemotion appengine
Codemotion appengine
 
05 content providers - Android
05   content providers - Android05   content providers - Android
05 content providers - Android
 
Petcube epic battle: architecture vs product. UA Mobile 2017.
Petcube epic battle: architecture vs product. UA Mobile 2017.Petcube epic battle: architecture vs product. UA Mobile 2017.
Petcube epic battle: architecture vs product. UA Mobile 2017.
 
Android development for iOS developers
Android development for iOS developersAndroid development for iOS developers
Android development for iOS developers
 

Más de Codemotion

Más de Codemotion (20)

Fuzz-testing: A hacker's approach to making your code more secure | Pascal Ze...
Fuzz-testing: A hacker's approach to making your code more secure | Pascal Ze...Fuzz-testing: A hacker's approach to making your code more secure | Pascal Ze...
Fuzz-testing: A hacker's approach to making your code more secure | Pascal Ze...
 
Pompili - From hero to_zero: The FatalNoise neverending story
Pompili - From hero to_zero: The FatalNoise neverending storyPompili - From hero to_zero: The FatalNoise neverending story
Pompili - From hero to_zero: The FatalNoise neverending story
 
Pastore - Commodore 65 - La storia
Pastore - Commodore 65 - La storiaPastore - Commodore 65 - La storia
Pastore - Commodore 65 - La storia
 
Pennisi - Essere Richard Altwasser
Pennisi - Essere Richard AltwasserPennisi - Essere Richard Altwasser
Pennisi - Essere Richard Altwasser
 
Michel Schudel - Let's build a blockchain... in 40 minutes! - Codemotion Amst...
Michel Schudel - Let's build a blockchain... in 40 minutes! - Codemotion Amst...Michel Schudel - Let's build a blockchain... in 40 minutes! - Codemotion Amst...
Michel Schudel - Let's build a blockchain... in 40 minutes! - Codemotion Amst...
 
Richard Süselbeck - Building your own ride share app - Codemotion Amsterdam 2019
Richard Süselbeck - Building your own ride share app - Codemotion Amsterdam 2019Richard Süselbeck - Building your own ride share app - Codemotion Amsterdam 2019
Richard Süselbeck - Building your own ride share app - Codemotion Amsterdam 2019
 
Eward Driehuis - What we learned from 20.000 attacks - Codemotion Amsterdam 2019
Eward Driehuis - What we learned from 20.000 attacks - Codemotion Amsterdam 2019Eward Driehuis - What we learned from 20.000 attacks - Codemotion Amsterdam 2019
Eward Driehuis - What we learned from 20.000 attacks - Codemotion Amsterdam 2019
 
Francesco Baldassarri - Deliver Data at Scale - Codemotion Amsterdam 2019 -
Francesco Baldassarri  - Deliver Data at Scale - Codemotion Amsterdam 2019 - Francesco Baldassarri  - Deliver Data at Scale - Codemotion Amsterdam 2019 -
Francesco Baldassarri - Deliver Data at Scale - Codemotion Amsterdam 2019 -
 
Martin Förtsch, Thomas Endres - Stereoscopic Style Transfer AI - Codemotion A...
Martin Förtsch, Thomas Endres - Stereoscopic Style Transfer AI - Codemotion A...Martin Förtsch, Thomas Endres - Stereoscopic Style Transfer AI - Codemotion A...
Martin Förtsch, Thomas Endres - Stereoscopic Style Transfer AI - Codemotion A...
 
Melanie Rieback, Klaus Kursawe - Blockchain Security: Melting the "Silver Bul...
Melanie Rieback, Klaus Kursawe - Blockchain Security: Melting the "Silver Bul...Melanie Rieback, Klaus Kursawe - Blockchain Security: Melting the "Silver Bul...
Melanie Rieback, Klaus Kursawe - Blockchain Security: Melting the "Silver Bul...
 
Angelo van der Sijpt - How well do you know your network stack? - Codemotion ...
Angelo van der Sijpt - How well do you know your network stack? - Codemotion ...Angelo van der Sijpt - How well do you know your network stack? - Codemotion ...
Angelo van der Sijpt - How well do you know your network stack? - Codemotion ...
 
Lars Wolff - Performance Testing for DevOps in the Cloud - Codemotion Amsterd...
Lars Wolff - Performance Testing for DevOps in the Cloud - Codemotion Amsterd...Lars Wolff - Performance Testing for DevOps in the Cloud - Codemotion Amsterd...
Lars Wolff - Performance Testing for DevOps in the Cloud - Codemotion Amsterd...
 
Sascha Wolter - Conversational AI Demystified - Codemotion Amsterdam 2019
Sascha Wolter - Conversational AI Demystified - Codemotion Amsterdam 2019Sascha Wolter - Conversational AI Demystified - Codemotion Amsterdam 2019
Sascha Wolter - Conversational AI Demystified - Codemotion Amsterdam 2019
 
Michele Tonutti - Scaling is caring - Codemotion Amsterdam 2019
Michele Tonutti - Scaling is caring - Codemotion Amsterdam 2019Michele Tonutti - Scaling is caring - Codemotion Amsterdam 2019
Michele Tonutti - Scaling is caring - Codemotion Amsterdam 2019
 
Pat Hermens - From 100 to 1,000+ deployments a day - Codemotion Amsterdam 2019
Pat Hermens - From 100 to 1,000+ deployments a day - Codemotion Amsterdam 2019Pat Hermens - From 100 to 1,000+ deployments a day - Codemotion Amsterdam 2019
Pat Hermens - From 100 to 1,000+ deployments a day - Codemotion Amsterdam 2019
 
James Birnie - Using Many Worlds of Compute Power with Quantum - Codemotion A...
James Birnie - Using Many Worlds of Compute Power with Quantum - Codemotion A...James Birnie - Using Many Worlds of Compute Power with Quantum - Codemotion A...
James Birnie - Using Many Worlds of Compute Power with Quantum - Codemotion A...
 
Don Goodman-Wilson - Chinese food, motor scooters, and open source developmen...
Don Goodman-Wilson - Chinese food, motor scooters, and open source developmen...Don Goodman-Wilson - Chinese food, motor scooters, and open source developmen...
Don Goodman-Wilson - Chinese food, motor scooters, and open source developmen...
 
Pieter Omvlee - The story behind Sketch - Codemotion Amsterdam 2019
Pieter Omvlee - The story behind Sketch - Codemotion Amsterdam 2019Pieter Omvlee - The story behind Sketch - Codemotion Amsterdam 2019
Pieter Omvlee - The story behind Sketch - Codemotion Amsterdam 2019
 
Dave Farley - Taking Back “Software Engineering” - Codemotion Amsterdam 2019
Dave Farley - Taking Back “Software Engineering” - Codemotion Amsterdam 2019Dave Farley - Taking Back “Software Engineering” - Codemotion Amsterdam 2019
Dave Farley - Taking Back “Software Engineering” - Codemotion Amsterdam 2019
 
Joshua Hoffman - Should the CTO be Coding? - Codemotion Amsterdam 2019
Joshua Hoffman - Should the CTO be Coding? - Codemotion Amsterdam 2019Joshua Hoffman - Should the CTO be Coding? - Codemotion Amsterdam 2019
Joshua Hoffman - Should the CTO be Coding? - Codemotion Amsterdam 2019
 

Último

Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 

Último (20)

Platformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityPlatformless Horizons for Digital Adaptability
Platformless Horizons for Digital Adaptability
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot ModelMcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Vector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxVector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptx
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontology
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
Biography Of Angeliki Cooney | Senior Vice President Life Sciences | Albany, ...
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
 
Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 

Nicola Corti - Building UI Consistent Android Apps - Codemotion Milan 2017

  • 1. Building UI Consistent Android Apps Nicola Corti @cortinico nco@yelp.com
  • 2.
  • 3. Yelp Mission Connecting people with great local businesses.
  • 4. About me Nicola Corti Android @BizCore nco@yelp.com @cortinico Community Addicted !🍕🕹🎤📸✈🏞
  • 6. “Unified use of Design Elements, such as color, typography, spatial layout and behaviors.
  • 8. External Consistency - Across Product
  • 9. External Consistency - Across Platform
  • 10. Benefits • Learnability • Reduce frustrations • Save money/time 💰 Photo by davelawler/CC BY - NC
  • 14.
  • 15. External Examples • Google Material Design • Apple Design Guidelines • Github Primer http://styleguides.io/
  • 17.
  • 20.
  • 21.
  • 23.
  • 24.
  • 28. Does it fit? • Can it be reused? • Is it visible to the user? • Development plan?
  • 29. User photo User name timestamp friends, media checkins Elite badge description
  • 32. Attributes <resources>
 <declare-styleable name="UserPassport">
 <!-- Determines user's name -->
 <attr name="userPassportName" format="string"/>
 </declare-styleable>
 </resources>
  • 33. Attributes <resources>
 <declare-styleable name="UserPassport">
 <!-- Determines user's name -->
 <attr name="userPassportName" format="string"/>
 
 <!-- Determines user's description/role -->
 <attr name="userPassportDescription" format="string"/>
 </declare-styleable>
 </resources>
  • 34. Attributes <resources>
 <declare-styleable name="UserPassport">
 <!-- Determines user's name -->
 <attr name="userPassportName" format="string"/>
 
 <!-- Determines user's description/role -->
 <attr name="userPassportDescription" format="string"/>
 </declare-styleable>
 <attr name="userPassportStyle" format="reference"/>
 </resources>
  • 35. Theme
  • 36. Theme <resources>
 <!—- This theme is the parent of all themes of Yelp's android apps. —->
 <style name="YelpStyleguideTheme"/>
 </resources>
  • 37. Theme <resources>
 <!—- This theme is the parent of all themes of Yelp's android apps. —->
 <style name="YelpStyleguideTheme" parent=“Theme.AppCompat.Light.DarkActionBar"/>
 </resources>
  • 38. Theme <resources>
 <!—- This theme is the parent of all themes of Yelp's android apps. —->
 <style name="YelpStyleguideTheme" parent=“Theme.AppCompat.Light.DarkActionBar">
 <item name="userPassportStyle">@style/UserPassport</item>
 </style>
 </resources>
  • 42. Styles <style name=“UserPassport">
 <item name="userPassportName">Joe Smith</item>
 <item name="userPassportDescription">Owner of Sample Business</item>
 </style>
  • 44. public class UserPassport extends RelativeLayout {

  • 45. public class UserPassport extends RelativeLayout {
 
 private TextView mUserName;
 private TextView mDescription;

  • 46. public class UserPassport extends RelativeLayout {
 
 private TextView mUserName;
 private TextView mDescription;
 
 public void setName(String name) {
 mUserName.setText(name);
 }
 
 

  • 47. public class UserPassport extends RelativeLayout {
 
 private TextView mUserName;
 private TextView mDescription;
 
 public void setName(String name) {
 mUserName.setText(name);
 }
 
 public void setDescription(String description) {
 if (TextUtils.isEmpty(description)) {
 mDescription.setVisibility(GONE);
 } else {
 mDescription.setVisibility(VISIBLE);
 mDescription.setText(description);
 }
 }
 

  • 48. public class UserPassport extends RelativeLayout {
 
 private TextView mUserName;
 private TextView mDescription;

  • 49. public class UserPassport extends RelativeLayout {
 
 private TextView mUserName;
 private TextView mDescription;
 
 public UserPassport(final Context context) {
 super(context);
 init(context, null, 0);
 }
 
 public UserPassport(final Context context, final AttributeSet attrs) {
 super(context, attrs);
 init(context, attrs, R.attr.userPassportStyle);
 }
 
 public UserPassport(final Context context, final AttributeSet attrs, final int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 init(context, attrs, defStyleAttr);
 }
  • 50. public class UserPassport extends RelativeLayout {
 
 private TextView mUserName;
 private TextView mDescription;
 
 public UserPassport(final Context context) {
 super(context);
 init(context, null, 0);
 }
 
 public UserPassport(final Context context, final AttributeSet attrs) {
 super(context, attrs);
 init(context, attrs, R.attr.userPassportStyle);
 }
 
 public UserPassport(final Context context, final AttributeSet attrs, final int defStyleAttr) {
 super(context, attrs, defStyleAttr);
 init(context, attrs, defStyleAttr);
 }
  • 51. public class UserPassport extends RelativeLayout {
 
 private TextView mUserName;
 private TextView mDescription;
 
 private void init(
 final Context context, final AttributeSet attrs, final int defStyleAttr) { 
 }
 

  • 52. public class UserPassport extends RelativeLayout {
 
 private TextView mUserName;
 private TextView mDescription;
 
 private void init(
 final Context context, final AttributeSet attrs, final int defStyleAttr) { 
 LayoutInflater.from(context).inflate(R.layout.user_passport, this, true); 
 }
 

  • 53. public class UserPassport extends RelativeLayout {
 
 private TextView mUserName;
 private TextView mDescription;
 
 private void init(
 final Context context, final AttributeSet attrs, final int defStyleAttr) { 
 LayoutInflater.from(context).inflate(R.layout.user_passport, this, true); 
 mUserName = (TextView) findViewById(R.id.user_name);
 mDescription = (TextView) findViewById(R.id.description);
 
 }
 

  • 54. public class UserPassport extends RelativeLayout {
 
 private TextView mUserName;
 private TextView mDescription;
 
 private void init(
 final Context context, final AttributeSet attrs, final int defStyleAttr) { 
 LayoutInflater.from(context).inflate(R.layout.user_passport, this, true); 
 mUserName = (TextView) findViewById(R.id.user_name);
 mDescription = (TextView) findViewById(R.id.description);
 
 final TypedArray styles = context.obtainStyledAttributes(attrs, R.styleable.UserPassport, defStyleAttr, 0);
 
 }

  • 55. public class UserPassport extends RelativeLayout {
 
 private TextView mUserName;
 private TextView mDescription;
 
 private void init(
 final Context context, final AttributeSet attrs, final int defStyleAttr) { 
 LayoutInflater.from(context).inflate(R.layout.user_passport, this, true); 
 mUserName = (TextView) findViewById(R.id.user_name);
 mDescription = (TextView) findViewById(R.id.description);
 
 final TypedArray styles = context.obtainStyledAttributes(attrs, R.styleable.UserPassport, defStyleAttr, 0);
 
 setName(styles.getString(R.styleable.UserPassport_userPassportName)); setDescription(styles.getString( R.styleable.UserPassport_userPassportDescription));
 
 }
 

  • 56. public class UserPassport extends RelativeLayout {
 
 private TextView mUserName;
 private TextView mDescription;
 
 private void init(
 final Context context, final AttributeSet attrs, final int defStyleAttr) { 
 LayoutInflater.from(context).inflate(R.layout.user_passport, this, true); 
 mUserName = (TextView) findViewById(R.id.user_name);
 mDescription = (TextView) findViewById(R.id.description);
 
 final TypedArray styles = context.obtainStyledAttributes(attrs, R.styleable.UserPassport, defStyleAttr, 0);
 
 setName(styles.getString(R.styleable.UserPassport_userPassportName)); setDescription(styles.getString( R.styleable.UserPassport_userPassportDescription));
 
 styles.recycle();
 }
 

  • 57. <style name="UserPassport">
 <item name="userPassportName">Joe Smith</item>
 <item name="userPassportDescription">@null</item>
 <item name="userPassportTint">@color/orange_dark_interface</item>
 <item name="userPassportNameColor">@color/black_regular_interface</item>
 <item name="userPassportSize">Regular</item>
 <item name="userPassportShowName">true</item>
 <item name="userPassportShowIcons">true</item>
 <item name="userPassportEliteYear">-1</item>
 <item name="userPassportFriends">0</item>
 <item name="userPassportReviews">0</item>
 <item name="userPassportPhotos">0</item>
 <item name="userPassportCheckIns">0</item>
 <item name="userPassportShowCheckIn">false</item>
 </style>
  • 58. <style name="UserPassport.White">
 <item name="userPassportName">Joe Smith</item>
 <item name="userPassportDescription">@null</item>
 <item name="userPassportTint">@color/orange_dark_interface</item>
 <item name="userPassportNameColor">@color/black_regular_interface</item>
 <item name="userPassportSize">Regular</item>
 <item name="userPassportShowName">true</item>
 <item name="userPassportShowIcons">true</item>
 <item name="userPassportEliteYear">-1</item>
 <item name="userPassportFriends">0</item>
 <item name="userPassportReviews">0</item>
 <item name="userPassportPhotos">0</item>
 <item name="userPassportCheckIns">0</item>
 <item name="userPassportShowCheckIn">false</item>
 </style>
  • 59. <style name="UserPassport.White">
 <item name="userPassportName">Joe Smith</item>
 <item name="userPassportDescription">@null</item>
 <item name="userPassportTint">@color/white_interface</item>
 <item name="userPassportNameColor">@color/white_interface</item>
 <item name="userPassportSize">Regular</item>
 <item name="userPassportShowName">true</item>
 <item name="userPassportShowIcons">true</item>
 <item name="userPassportEliteYear">-1</item>
 <item name="userPassportFriends">0</item>
 <item name="userPassportReviews">0</item>
 <item name="userPassportPhotos">0</item>
 <item name="userPassportCheckIns">0</item>
 <item name="userPassportShowCheckIn">false</item>
 </style>
  • 60. <style name="UserPassport.White">
 <item name="userPassportTint">@color/white_interface</item>
 <item name="userPassportNameColor">@color/white_interface</item>
 </style>
  • 63. Assets dependencies {
 
 // Yelp asset libs
 compile 'com.yelp:yelpicons:135.0.0'
 compile ‘com.yelp:yelpdesign:4.0.4’ }
  • 64. Assets dependencies {
 
 // Yelp asset libs
 compile 'com.yelp:yelpicons:135.0.0'
 compile ‘com.yelp:yelpdesign:4.0.4’ }
  • 65. Color
  • 66. Color <color name="black_extra_light_interface">#666666</color>
 <color name="black_regular_interface">#333333</color>
 <color name="blue_dark_interface">#0073bb</color>
 <color name="blue_extra_light_interface">#d0ecfb</color>
 <color name="blue_regular_interface">#0097ec</color>
 <color name="gray_dark_interface">#999999</color>
 <color name="gray_extra_light_interface">#f5f5f5</color>
 <color name="gray_light_interface">#e6e6e6</color>
 <color name="gray_regular_interface">#cccccc</color>
 <color name="green_extra_light_interface">#daecd2</color>
 <color name="green_regular_interface">#41a700</color>
 <color name="mocha_extra_light_interface">#f8e3c7</color>
 <color name="mocha_light_interface">#f1bd79</color>
 <color name="orange_dark_interface">#f15c00</color>
 <color name="orange_extra_light_interface">#ffebcf</color>
 <color name="purple_extra_light_interface">#dad1e4</color>
 <color name="red_dark_interface">#d32323</color>
 <color name="red_extra_light_interface">#fcd6d3</color>
 <color name="slate_extra_light_interface">#cddae2</color>
 <color name="white_interface">#ffffff</color>
 <color name="yellow_dark_interface">#fec011</color>
 <color name="yellow_extra_light_interface">#fff7cc</color>
  • 67. Color <color name="black_extra_light_interface">#666666</color>
 <color name="black_regular_interface">#333333</color>
 <color name="blue_dark_interface">#0073bb</color>
 <color name="blue_extra_light_interface">#d0ecfb</color>
 <color name="blue_regular_interface">#0097ec</color>
 <color name="gray_dark_interface">#999999</color>
 <color name="gray_extra_light_interface">#f5f5f5</color>
 <color name="gray_light_interface">#e6e6e6</color>
 <color name="gray_regular_interface">#cccccc</color>
 <color name="green_extra_light_interface">#daecd2</color>
 <color name="green_regular_interface">#41a700</color>
 <color name="mocha_extra_light_interface">#f8e3c7</color>
 <color name="mocha_light_interface">#f1bd79</color>
 <color name="orange_dark_interface">#f15c00</color>
 <color name="orange_extra_light_interface">#ffebcf</color>
 <color name="purple_extra_light_interface">#dad1e4</color>
 <color name="red_dark_interface">#d32323</color>
 <color name="red_extra_light_interface">#fcd6d3</color>
 <color name="slate_extra_light_interface">#cddae2</color>
 <color name="white_interface">#ffffff</color>
 <color name="yellow_dark_interface">#fec011</color>
 <color name="yellow_extra_light_interface">#fff7cc</color>
  • 68. Color <color name="black_extra_light_interface">#666666</color>
 <color name="black_regular_interface">#333333</color>
 <color name="blue_dark_interface">#0073bb</color>
 <color name="blue_extra_light_interface">#d0ecfb</color>
 <color name="blue_regular_interface">#0097ec</color>
 <color name="gray_dark_interface">#999999</color>
 <color name="gray_extra_light_interface">#f5f5f5</color>
 <color name="gray_light_interface">#e6e6e6</color>
 <color name="gray_regular_interface">#cccccc</color>
 <color name="green_extra_light_interface">#daecd2</color>
 <color name="green_regular_interface">#41a700</color>
 <color name="mocha_extra_light_interface">#f8e3c7</color>
 <color name="mocha_light_interface">#f1bd79</color>
 <color name="orange_dark_interface">#f15c00</color>
 <color name="orange_extra_light_interface">#ffebcf</color>
 <color name="purple_extra_light_interface">#dad1e4</color>
 <color name="red_dark_interface">#d32323</color>
 <color name="red_extra_light_interface">#fcd6d3</color>
 <color name="slate_extra_light_interface">#cddae2</color>
 <color name="white_interface">#ffffff</color>
 <color name="yellow_dark_interface">#fec011</color>
 <color name="yellow_extra_light_interface">#fff7cc</color>
  • 69. Build
  • 71. VCS & CI • git submodule • Run the Build for • submodule • consumer app • business app
  • 73. Custom Lint Checks 
 Button b = new Button(context);
 
 SwitchCompat switchCompat = new SwitchCompat(context);
 
 Snackbar.make(getRootView(), “Test”, LENGTH_SHORT).show();
  • 74. Custom Lint Checks @SuppressLint("")
 Button b = new Button(context);
 
 @SuppressLint("")
 SwitchCompat switchCompat = new SwitchCompat(context);
 
 @SuppressLint("")
 Snackbar.make(getRootView(), “Test”, LENGTH_SHORT).show();
  • 75. Custom Lint Checks @SuppressLint("NonStyleguideButtonInstance")
 Button b = new Button(context);
 
 @SuppressLint("")
 SwitchCompat switchCompat = new SwitchCompat(context);
 
 @SuppressLint("")
 Snackbar.make(getRootView(), “Test”, LENGTH_SHORT).show();
  • 76. Custom Lint Checks @SuppressLint("NonStyleguideButtonInstance")
 Button b = new Button(context);
 
 @SuppressLint("NonStyleguideToggleInstance")
 SwitchCompat switchCompat = new SwitchCompat(context);
 
 @SuppressLint("")
 Snackbar.make(getRootView(), “Test”, LENGTH_SHORT).show();
  • 77. Custom Lint Checks @SuppressLint("NonStyleguideButtonInstance")
 Button b = new Button(context);
 
 @SuppressLint("NonStyleguideToggleInstance")
 SwitchCompat switchCompat = new SwitchCompat(context);
 
 @SuppressLint("NonStyleguideSnackbarInstance")
 Snackbar.make(getRootView(), “Test”, LENGTH_SHORT).show();
  • 78. Custom Lint Checks // Using stock Button because […] + Ticket number. @SuppressLint("NonStyleguideButtonInstance")
 Button b = new Button(context);
 
 @SuppressLint("NonStyleguideToggleInstance")
 SwitchCompat switchCompat = new SwitchCompat(context);
 
 @SuppressLint("NonStyleguideSnackbarInstance")
 Snackbar.make(getRootView(), “Test”, LENGTH_SHORT).show();
  • 80. Custom Lint Checks <Button
 android:layout_width="match_parent"
 android:layout_height="match_parent" />
 
 <Switch
 android:layout_width="match_parent"
 android:layout_height="match_parent" />
  • 81. Custom Lint Checks <Button
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 tools:ignore="NonStyleguideButtonTag" />
 
 <Switch
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 tools:ignore="NonStyleguideToggleTag" />
  • 84. build.gradle android {
 lintOptions {
 abortOnError true
 warningsAsErrors true
 
 }
 }
  • 85. build.gradle android {
 lintOptions {
 abortOnError true
 warningsAsErrors true
 
 lintConfig file("lint.xml")
 }
 }
  • 86. build.gradle android {
 lintOptions {
 abortOnError true
 warningsAsErrors true
 
 lintConfig file("lint.xml")
 baseline file("lint-baseline.xml")
 }
 }
  • 87. Test your component • Your component ❤ Espresso? • Do you handle state changes? • contentDescription ?
  • 88. Share
  • 89. Documentation • Provide Javadoc • Add Screenshots • Document Attributes • Document Styles
  • 90. Screenshots capture • v0.1: Manual Screenshots • v0.2: Automated locally • v0.3: Automated with CI 💫
  • 91.
  • 92.
  • 93.
  • 94. StyleguideTestApp • Components Showcase • For Designer 🎨 • For Developer 🔧
  • 95. Taking Screenshots with Espresso public class ScreenshotViewActions {
 
 }
  • 96. Taking Screenshots with Espresso public class ScreenshotViewActions {
 
 public static ViewAction screenshot(final String folderName, final String fileName) {
 return new ViewAction() {
 
 };
 }
 }
  • 97. Taking Screenshots with Espresso public class ScreenshotViewActions {
 
 public static ViewAction screenshot(final String folderName, final String fileName) {
 return new ViewAction() {
 // Other methods omitted.
 
 @Override
 public void perform(UiController uiController, View view) {
 ScreenshotsUtil.takeScreenshot(folderName, fileName, view);
 }
 };
 }
 }
  • 99. Sample Espresso Test public class StarsViewActivityTests {
 
 }
  • 100. Sample Espresso Test public class StarsViewActivityTests {
 
 @Test
 public void takeScreenshot() throws InterruptedException {
 onView(withId(R.id.stars_view_4)).perform(setStarsNumber(4));
 
 }
 }
  • 101. Sample Espresso Test public class StarsViewActivityTests {
 
 @Test
 public void takeScreenshot() throws InterruptedException {
 onView(withId(R.id.stars_view_4)).perform(setStarsNumber(4));
 onView(withId(R.id.stars_view_5)).perform(setStarsNumber(5),
 screenshot(FOLDER_NAME, "stars_with_text"));
 
 }
 }
  • 102. Sample Espresso Test public class StarsViewActivityTests {
 
 @Test
 public void takeScreenshot() throws InterruptedException {
 onView(withId(R.id.stars_view_4)).perform(setStarsNumber(4));
 onView(withId(R.id.stars_view_5)).perform(setStarsNumber(5),
 screenshot(FOLDER_NAME, "stars_with_text"));
 
 ScreenshotUtil.fullScreenshot(FOLDER_NAME, "stars_fullscreen");
 }
 }
  • 103. Bend it, don’t break it! Source: GIPHY