Slide 1

Slide 1 text

Building Beautiful Apps with the Android Support Libraries

Slide 2

Slide 2 text

Joe Birch Android Engineer @Buffer @hitherejoe / hitherejoe.com

Slide 3

Slide 3 text

Android Support Libraries

Slide 4

Slide 4 text

Support Libraries Design Support Custom Tabs Recycler View + more Animations

Slide 5

Slide 5 text

Recycler View

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Slide 8

Slide 8 text

Layout Manager - How to layout views Item Animator - How to animate views Adapter - Provides views

Slide 9

Slide 9 text

mRecycler.setLayoutManager(new LinearLayoutManager(this)); mRecycler.setAdapter(mRecyclerAdapter);

Slide 10

Slide 10 text

public class YourAdapter extends RecyclerView.Adapter { @Override public YourViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { } @Override public void onBindViewHolder(YourViewHolder holder, int position) { } class YourViewHolder extends RecyclerView.ViewHolder { public YourViewHolder(View itemView) { } } }

Slide 11

Slide 11 text

public class YourAdapter extends RecyclerView.Adapter { @Override public YourViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // inflate layout return new YourViewHolder(view); } @Override public void onBindViewHolder(YourViewHolder holder, int position) { // set view data } class YourViewHolder extends RecyclerView.ViewHolder { public YourViewHolder(View itemView) { super(itemView); // construct view holder } } }

Slide 12

Slide 12 text

public class YourAdapter extends RecyclerView.Adapter { @Override public YourViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // inflate layout return new YourViewHolder(view); } @Override public void onBindViewHolder(YourViewHolder holder, int position) { // set view data } class YourViewHolder extends RecyclerView.ViewHolder { public YourViewHolder(View itemView) { super(itemView); // construct view holder } } }

Slide 13

Slide 13 text

public class YourAdapter extends RecyclerView.Adapter { @Override public YourViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // inflate layout return new YourViewHolder(view); } @Override public void onBindViewHolder(YourViewHolder holder, int position) { // set view data } class YourViewHolder extends RecyclerView.ViewHolder { public YourViewHolder(View itemView) { super(itemView); // construct view holder } } }

Slide 14

Slide 14 text

- More powerful and flexible than list view - Decouples list from container, easily add items at runtime

Slide 15

Slide 15 text

- More powerful and flexible than list view - Follows best-practices using ViewHolder - Enforced, ListView was optional - Decouples list from container, easily add items at runtime

Slide 16

Slide 16 text

- More powerful and flexible than list view - Follows best-practices using ViewHolder - Enforced, ListView was optional - No dividers by default = more material - Decouples list from container, easily add items at runtime

Slide 17

Slide 17 text

- More powerful and flexible than list view - Follows best-practices using ViewHolder - Enforced, ListView was optional - No dividers by default = more material - Use LayoutManager for different lists or grids - Decouples list from container, easily add items at runtime

Slide 18

Slide 18 text

LinearLayoutManager lm = new LinearLayoutManager(this); GridLayoutManager lm = new GridLayoutManager(this, 4); StaggeredGridLayoutManager lm = new StaggeredGridLayoutManager(3, VERTICAL); mRecyclerView.setLayoutManager(lm);

Slide 19

Slide 19 text

- More powerful and flexible than list view - Follows best-practices using ViewHolder - Enforced, ListView was optional - No dividers by default = more material - Use LayoutManager for different lists or grids - Simpler animations using ItemAnimator - Decouples animations, hands over to ItemAnimator - Decouples list from container, easily add items at runtime

Slide 20

Slide 20 text

Swipe Refresh layout

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

// child view(s) goes here

Slide 23

Slide 23 text

mSwipeRefreshLayout.setOnRefreshListener( new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { // do stuff! } });

Slide 24

Slide 24 text

- Allow the user to easily refresh content - Whilst also showing them it’s taking place

Slide 25

Slide 25 text

- Allow the user to easily refresh content - Customise the colour of the PTR circle - Whilst also showing them it’s taking place

Slide 26

Slide 26 text

mSwipeRefreshLayout.setProgressBackgroundColorSchemeResource(R.color.primary); mSwipeRefreshLayout.setColorSchemeResources(R.color.white);

Slide 27

Slide 27 text

- Allow the user to easily refresh content - Customise the colour of the PTR circle - Manually use setRefreshing() - Manually display PTR circle, this could be when updating from a service etc - Whilst also showing them it’s taking place

Slide 28

Slide 28 text

Floating Action Button

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

Normal - 56dp Mini - 40dp

Slide 31

Slide 31 text

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

mFloatingActionButton.setOnClickListener(new View.OnClickListener() {
 @Override
 public void onClick(View view) {
 Snackbar.make(view, "Hey! I'm a Snackbar”, Snackbar.LENGTH_LONG).show();
 }
 });

Slide 34

Slide 34 text

- One per screen, dominant action - Even on tabbed content

Slide 35

Slide 35 text

- One per screen, dominant action - Avoid minor or destructive actions - Even on tabbed content

Slide 36

Slide 36 text

Do:

Slide 37

Slide 37 text

Do: Don’t:

Slide 38

Slide 38 text

- One per screen, dominant action - Avoid minor or destructive actions - Even on tabbed content - Not a replacement for an overflow menu

Slide 39

Slide 39 text

- One per screen, dominant action - Avoid minor or destructive actions - Even on tabbed content - Not a replacement for an overflow menu - Get creative with animations and transitions

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

int centerX = (startView.getLeft() + startView.getRight()) / 2; int centerY = (startView.getTop() + startView.getBottom()) / 2; float finalRadius = (float) Math.hypot((double) centerX, (double) centerY); Animator mCircularReveal = ViewAnimationUtils.createCircularReveal( targetView, centerX, centerY, 0, finalRadius);

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

https://github.com/hitherejoe/animate

Slide 44

Slide 44 text

Snackbar

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

Snackbar.make(mDrawerLayout, "Your message”, Snackbar.LENGTH_SHORT) .setAction(getString(R.string.text_undo), this) .show();

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

final Snackbar snackbar = Snackbar.make(mShotRecycler, "Hey, I'm a Snackbar", Snackbar.LENGTH_INDEFINITE); 
 snackbar.setAction("UNDO", new View.OnClickListener() {
 @Override
 public void onClick(View v) {
 // Handle action click event
 }
 }); snackbar.setActionTextColor(color); snackbar.show();

Slide 49

Slide 49 text

LENGTH_INDEFINITE LENGTH_LONG LENGTH_SHORT

Slide 50

Slide 50 text

- Short informative messages

Slide 51

Slide 51 text

- Short informative messages - Can replace toast notifications in most places

Slide 52

Slide 52 text

- Short informative messages - Avoid adding iconography - Can replace toast notifications in most places

Slide 53

Slide 53 text

- Short informative messages - Avoid adding iconography - Can replace toast notifications in most places - Only display a single action

Slide 54

Slide 54 text

- Short informative messages - Avoid adding iconography - Can replace toast notifications in most places - Only display a single action - Don’t obstruct any displayed FAB

Slide 55

Slide 55 text

DrawerLayout

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text

// Views go here

Slide 58

Slide 58 text

- Display underneath status bar - Set transparent status bar in theme

Slide 59

Slide 59 text

true
 @android:color/transparent

Slide 60

Slide 60 text

- Display underneath status bar - Use to display navigation for app ‘sections’ - Set transparent status bar in theme

Slide 61

Slide 61 text

- Display underneath status bar - Use to display navigation for app ‘sections’ - Set transparent status bar in theme - Remember, pressing back should exit the app! - Common mistake to traverse back through fragments

Slide 62

Slide 62 text

NavigationView

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

Slide 65

Slide 65 text

- Should at least display navigation in the drawer! - Header section is optional

Slide 66

Slide 66 text

NavigationView - HeaderLayout

Slide 67

Slide 67 text

No content

Slide 68

Slide 68 text

Slide 69

Slide 69 text

No content

Slide 70

Slide 70 text

Slide 71

Slide 71 text

- Generally used for user profile

Slide 72

Slide 72 text

- Generally used for user profile - Great place for account switching

Slide 73

Slide 73 text

- Generally used for user profile - Avoid large amounts of text - Great place for account switching - User photo, name, email, account info etc.

Slide 74

Slide 74 text

- Generally used for user profile - Avoid large amounts of text - Great place for account switching - User photo, name, email, account info etc. - Declare layout using app:headerLayout

Slide 75

Slide 75 text

NavigationView - Menu

Slide 76

Slide 76 text

No content

Slide 77

Slide 77 text

Slide 78

Slide 78 text

Slide 79

Slide 79 text

Slide 80

Slide 80 text

Slide 81

Slide 81 text

Slide 82

Slide 82 text

No content

Slide 83

Slide 83 text

Slide 84

Slide 84 text

Slide 85

Slide 85 text

- Use iconography :)

Slide 86

Slide 86 text

- Use iconography :) - e.g Dominant screens and ‘Generic’ sections - Group related menu items

Slide 87

Slide 87 text

- Use iconography :) - e.g Dominant screens and Generic sections - Group related menu items - Declare menu using app:menu

Slide 88

Slide 88 text

EditText Floating Labels

Slide 89

Slide 89 text

No content

Slide 90

Slide 90 text

Slide 91

Slide 91 text

Slide 92

Slide 92 text

Slide 93

Slide 93 text

enable error at time of error enable error during initialisation

Slide 94

Slide 94 text

setErrorEnabled(true); setError(getString(R.string.text_error_message));

Slide 95

Slide 95 text

- Less disruptive than toasts, snackers and dialogs

Slide 96

Slide 96 text

- Enable error during screen creation - Less disruptive than toasts, snackers and dialogs - Enable error during screen creation

Slide 97

Slide 97 text

- Enable error during screen creation - Keep error to a single line - Less disruptive than toasts, snackers and dialogs

Slide 98

Slide 98 text

- Enable error during screen creation - Clear the error programatically - Keep error to a single line - Less disruptive than toasts, snackers and dialogs

Slide 99

Slide 99 text

TabLayout

Slide 100

Slide 100 text

tabGravity=“fill” tabGravity=“center”

Slide 101

Slide 101 text

No content

Slide 102

Slide 102 text

tabMode=“fixed” tabMode=“scrollable”

Slide 103

Slide 103 text

Slide 104

Slide 104 text

Slide 105

Slide 105 text

Slide 106

Slide 106 text

ViewPager pager = (ViewPager) rootView.findViewById(R.id.viewPager); pager.setAdapter(new MyPagerAdapter(getActivity().getSupportFragmentManager())); ————————————————— or———————————————————— TabLayout tabLayout = (TabLayout) rootView.findViewById(R.id.sliding_tabs); tabLayout.addTab(tabLayout.newTab().setText("Tab One")); tabLayout.addTab(tabLayout.newTab().setText("Tab Two")); tabLayout.addTab(tabLayout.newTab().setText("Tab Three")); tabLayout.setupWithViewPager(pager);

Slide 107

Slide 107 text

- Single row above the displayed content

Slide 108

Slide 108 text

- Single row above the displayed content - Keep titles short

Slide 109

Slide 109 text

- Single row above the displayed content - Don’t nest swipes! - Keep titles short - Don’t nest swipes!

Slide 110

Slide 110 text

- Single row above the displayed content - Don’t nest swipes! - Keep titles short - Used to switch between relatable categories

Slide 111

Slide 111 text

- Single row above the displayed content - Don’t nest swipes! - Keep titles short - Used to switch between relatable categories - Collapse the toolbar when scrolling with tabs

Slide 112

Slide 112 text

Coordinator Layout

Slide 113

Slide 113 text

enterAlways exitUntilCollapsed

Slide 114

Slide 114 text

Slide 115

Slide 115 text

Slide 116

Slide 116 text

- CoordinatorLayout helps save screen estate - More space for content!

Slide 117

Slide 117 text

- CoordinatorLayout helps save screen estate - More space for content! - Collapse the toolbar when scrolling with tabs

Slide 118

Slide 118 text

- CoordinatorLayout helps save screen estate - Simple to add! - More space for content! - Collapse the toolbar when scrolling with tabs

Slide 119

Slide 119 text

- CoordinatorLayout helps save screen estate - Simple to add! - More space for content! - Use the app:layout_behavior=“@string/ appbar_scrolling_view_behavior" flag - Collapse the toolbar when scrolling with tabs

Slide 120

Slide 120 text

Collapsing Toolbar Layout

Slide 121

Slide 121 text

pin parallax

Slide 122

Slide 122 text

Slide 123

Slide 123 text

Slide 124

Slide 124 text

- Use Parallax to fade out images - Use Parallax multiplier attribute

Slide 125

Slide 125 text

- Use Parallax to fade out images - or if you don’t want to fade out images! - Use Pin for solid colours - Use Parallax multiplier attribute

Slide 126

Slide 126 text

Toolbar

Slide 127

Slide 127 text

No content

Slide 128

Slide 128 text

Slide 129

Slide 129 text

Slide 130

Slide 130 text

- More flexible than ActionBar - It’s a part of our view hierarchy!

Slide 131

Slide 131 text

- More flexible than ActionBar - It’s a part of our view hierarchy! - Dominant, above all app content - All content scrolls underneath! - Dominant, above all app content - All content scrolls underneath!

Slide 132

Slide 132 text

- More flexible than ActionBar - It’s a part of our view hierarchy! - Dominant, above all app content - All content scrolls underneath! - Use with Coordinator layout to collapse

Slide 133

Slide 133 text

- More flexible than ActionBar - It’s a part of our view hierarchy! - Dominant, above all app content - All content scrolls underneath! - Use with Coordinator layout to collapse - Set using setSupportActionBar(toolbar); - Disable action bar in theme

Slide 134

Slide 134 text

Bottom Sheets

Slide 135

Slide 135 text

Modal Persistent

Slide 136

Slide 136 text

- Modal for menus, Persistent for content

Slide 137

Slide 137 text

- Modal for menus, Persistent for content - Display after user-initiated action

Slide 138

Slide 138 text

- Modal for menus, Persistent for content - Only size them using the space they need - Display after user-initiated action

Slide 139

Slide 139 text

- Modal for menus, Persistent for content - Only size them using the space they need - Display after user-initiated action - Declare view with bottom sheet behaviour - app:layout_behavior=“@string/bottom_sheet_behavior"

Slide 140

Slide 140 text

DayNight Theme

Slide 141

Slide 141 text

day night

Slide 142

Slide 142 text

- Extend from the theme

Slide 143

Slide 143 text

Slide 144

Slide 144 text

- Extend from the theme - Enable the feature in Application class

Slide 145

Slide 145 text

AppCompatDelegate.setDefaultNightMode( AppCompatDelegate.MODE_NIGHT_...);

Slide 146

Slide 146 text

- Extend from the theme - Update in component as required - Enable the feature in Application class

Slide 147

Slide 147 text

getDelegate().setLocalNightMode( AppCompatDelegate.MODE_NIGHT_...); // Now recreate for it to take effect recreate();

Slide 148

Slide 148 text

- Extend from the theme - Update in component as required - Enable the feature in Application class - Check mode and act accordingly!

Slide 149

Slide 149 text

int currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; switch (currentNightMode) { case Configuration.UI_MODE_NIGHT_NO: // Night mode is not active, we're in day time case Configuration.UI_MODE_NIGHT_YES: // Night mode is active, we're at night! case Configuration.UI_MODE_NIGHT_UNDEFINED: // We don't know what mode we're in, assume not night }

Slide 150

Slide 150 text

Palette

Slide 151

Slide 151 text

// Synchronous Palette p = Palette.from(mBitmap).generate(); // Asynchronous Palette.from(mBitmap).generate(new PaletteAsyncListener() { public void onGenerated(Palette p) { // Use generated instance } });

Slide 152

Slide 152 text

int vibrant = palette.getVibrantColor(default); int vibrantLight = palette.getLightVibrantColor(default); int vibrantDark = palette.getDarkVibrantColor(default); int muted = palette.getMutedColor(default); int mutedLight = palette.getLightMutedColor(default); int mutedDark = palette.getDarkMutedColor(default);

Slide 153

Slide 153 text

Constraint Layout

Slide 154

Slide 154 text

No content

Slide 155

Slide 155 text

app:layout_constraintBottom_toBottomOf=”@+id/constraintLayout” app:layout_constraintEnd_toEndOf=”@+id/constraintLayout” app:layout_constraintStart_toStartOf=”@+id/constraintLayout” app:layout_constraintTop_toTopOf=”@+id/constraintLayout” app:layout_constraintEnd_toStartOf=”@+id/imageView”

Slide 156

Slide 156 text

- Currently in alpha

Slide 157

Slide 157 text

- Currently in alpha - Relative-Layout on steroids! - Reduce nested containers - Simplify view layout relationships

Slide 158

Slide 158 text

Custom Tabs

Slide 159

Slide 159 text

No content

Slide 160

Slide 160 text

int color = ContextCompat.getColor(context, R.color.primary); Bitmap icon = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_arrow_back); CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder() .setToolbarColor(color) .setShowTitle(true) .setCloseButtonIcon(icon) .addDefaultShareMenuItem() .build(); Intent intent = customTabsIntent.intent; intent.setData(uri); startActivity(intent);

Slide 161

Slide 161 text

- Help provide a seamless experience

Slide 162

Slide 162 text

- Help provide a seamless experience - Built-in share functionality

Slide 163

Slide 163 text

- Help provide a seamless experience - Built-in share functionality - Theme browser bar to application color

Slide 164

Slide 164 text

- Help provide a seamless experience - Built-in share functionality - Theme browser bar to application color - Performance enhancements

Slide 165

Slide 165 text

Touch States

Slide 166

Slide 166 text

?android:attr/selectableItemBackground

Slide 167

Slide 167 text

?android:attr/selectableItemBackgroundBorderless

Slide 168

Slide 168 text

View Property Animator

Slide 169

Slide 169 text

alpha() scaleX() & scaleY() translationZ() setDuration() setStartDelay() setInterpolator() setListener()

Slide 170

Slide 170 text

Interpolators

Slide 171

Slide 171 text

Fast-Out-Linear-In Accelerate-Decelerate Linear-Out-Slow-In

Slide 172

Slide 172 text

Object Animator

Slide 173

Slide 173 text

No content

Slide 174

Slide 174 text

private void animateForegroundColor(@ColorInt final int targetColor) { ObjectAnimator animator = ObjectAnimator.ofInt(YOUR_VIEW, FOREGROUND_COLOR, Color.TRANSPARENT, targetColor); animator.setEvaluator(new ArgbEvaluator()); animator.setStartDelay(DELAY_COLOR_CHANGE); animator.start(); }

Slide 175

Slide 175 text

private void resizeView() { final float widthHeightRatio = (float) getHeight() / (float) getWidth(); resizeViewProperty(View.SCALE_X, .5f, 200); resizeViewProperty(View.SCALE_Y, .5f / widthHeightRatio, 250); } private void resizeViewProperty(Property property, float targetScale, int durationOffset) { ObjectAnimator animator = ObjectAnimator.ofFloat(this, property, 1f, targetScale); animator.setInterpolator(new LinearOutSlowInInterpolator()); animator.setStartDelay(DELAY_COLOR_CHANGE + durationOffset); animator.start(); }

Slide 176

Slide 176 text

Window Transitions

Slide 177

Slide 177 text

enter - Determines how an activity’s views enter the scene exit - Determines how an activity’s views exit the scene reenter - Determines how an activity reenters after previously exiting shared elements - Determines how shared views transition between activities

Slide 178

Slide 178 text

enter - Determines how an activity’s views enter the scene exit - Determines how an activity’s views exit the scene reenter - Determines how an activity reenters after previously exiting shared elements - Determines how shared views transition between activities

Slide 179

Slide 179 text

enter - Determines how an activity’s views enter the scene exit - Determines how an activity’s views exit the scene reenter - Determines how an activity reenters after previously exiting shared elements - Determines how shared views transition between activities

Slide 180

Slide 180 text

enter - Determines how an activity’s views enter the scene exit - Determines how an activity’s views exit the scene reenter - Determines how an activity reenters after previously exiting shared elements - Determines how shared views transition between activities

Slide 181

Slide 181 text

Explode Transition

Slide 182

Slide 182 text

No content

Slide 183

Slide 183 text

Slide 184

Slide 184 text

Slide 185

Slide 185 text

<item name="android:windowExitTransition">@transition/explode</item> <item name="android:windowReenterTransition">@android:transition/slide_top</item>

Slide 186

Slide 186 text

Slide Transition

Slide 187

Slide 187 text

No content

Slide 188

Slide 188 text

Slide 189

Slide 189 text

<item name="android:windowExitTransition">@transition/slide</item> <item name="android:windowReenterTransition">@android:transition/slide_top</item>

Slide 190

Slide 190 text

Fade Transition

Slide 191

Slide 191 text

No content

Slide 192

Slide 192 text

Slide 193

Slide 193 text

<item name="android:windowExitTransition">@transition/fade</item> <item name="android:windowReenterTransition">@android:transition/slide_top</item>

Slide 194

Slide 194 text

Excluding Views

Slide 195

Slide 195 text

Slide 196

Slide 196 text

Shared Element Transitions

Slide 197

Slide 197 text

No content

Slide 198

Slide 198 text

Screen One Screen Two

Slide 199

Slide 199 text

Pair participants = new Pair<>(mSquareView, ViewCompat.getTransitionName(mSquareView)); ActivityOptionsCompat transitionActivityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation( SharedTransitionsActivity.this, participants); ActivityCompat.startActivity(SharedTransitionsActivity.this, intent, transitionActivityOptions.toBundle());

Slide 200

Slide 200 text

Keep up-to-date

Slide 201

Slide 201 text

Resources

Slide 202

Slide 202 text

Resources Official Android Documentation G+ Android Developer Community github.com/hitherejoe medium.com/@hitherejoe G+ Android Design Community Official Google Design Documentation