Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building Beautiful Apps with the Design Support...

Joe Birch
August 02, 2016

Building Beautiful Apps with the Design Support Library

Since Material Design has been introduced to the world of Android, many applications are still not making the most out of what's provided to us out-of-the-box to help us create material styled apps. In this class, we'll take a look at the capabilities of the design support library and how we can implement the numerous components that it provides to help us create beautiful and polished material influenced applications. As well as taking a deep dive into these components and their various attributes, we'll also take a look at how you can add subtle animations to compliment your material apps even further!

Joe Birch

August 02, 2016
Tweet

More Decks by Joe Birch

Other Decks in Programming

Transcript

  1. Layout Manager - How to layout views Item Animator -

    How to animate views Adapter - Provides views
  2. public class YourAdapter extends RecyclerView.Adapter<YourAdapter.YourViewHolder> { @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) { } } }
  3. public class YourAdapter extends RecyclerView.Adapter<YourAdapter.YourViewHolder> { @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 } } }
  4. public class YourAdapter extends RecyclerView.Adapter<YourAdapter.YourViewHolder> { @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 } } }
  5. public class YourAdapter extends RecyclerView.Adapter<YourAdapter.YourViewHolder> { @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 } } }
  6. - More powerful and flexible than list view - Decouples

    list from container, easily add items at runtime
  7. - 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
  8. - 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
  9. - 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
  10. LinearLayoutManager lm = new LinearLayoutManager(this); GridLayoutManager lm = new GridLayoutManager(this,

    4); StaggeredGridLayoutManager lm = new StaggeredGridLayoutManager(3, VERTICAL); mRecyclerView.setLayoutManager(lm);
  11. - 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
  12. - Allow the user to easily refresh content - Whilst

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

    the colour of the PTR circle - Whilst also showing them it’s taking place
  14. - 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
  15. - One per screen, dominant action - Avoid minor or

    destructive actions - Even on tabbed content
  16. Do:

  17. - One per screen, dominant action - Avoid minor or

    destructive actions - Even on tabbed content - Not a replacement for an overflow menu
  18. - 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
  19. 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);
  20. 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();
  21. - Short informative messages - Avoid adding iconography - Can

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

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

    replace toast notifications in most places - Only display a single action - Don’t obstruct any displayed FAB
  24. - Display underneath status bar - Use to display navigation

    for app ‘sections’ - Set transparent status bar in theme
  25. - 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
  26. - Generally used for user profile - Avoid large amounts

    of text - Great place for account switching - User photo, name, email, account info etc.
  27. - 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
  28. - Use iconography :) - e.g Dominant screens and Generic

    sections - Group related menu items - Declare menu using app:menu
  29. - Enable error during screen creation - Less disruptive than

    toasts, snackers and dialogs - Enable error during screen creation
  30. - Enable error during screen creation - Keep error to

    a single line - Less disruptive than toasts, snackers and dialogs
  31. - Enable error during screen creation - Clear the error

    programatically - Keep error to a single line - Less disruptive than toasts, snackers and dialogs
  32. 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);
  33. - Single row above the displayed content - Don’t nest

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

    swipes! - Keep titles short - Used to switch between relatable categories
  35. - 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
  36. - CoordinatorLayout helps save screen estate - More space for

    content! - Collapse the toolbar when scrolling with tabs
  37. - CoordinatorLayout helps save screen estate - Simple to add!

    - More space for content! - Collapse the toolbar when scrolling with tabs
  38. - 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
  39. - 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
  40. - 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!
  41. - 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
  42. - 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
  43. - Modal for menus, Persistent for content - Only size

    them using the space they need - Display after user-initiated action
  44. - 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"
  45. - Extend from the theme - Update in component as

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

    required - Enable the feature in Application class - Check mode and act accordingly!
  47. 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 }
  48. 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);
  49. - Currently in alpha - Relative-Layout on steroids! - Reduce

    nested containers - Simplify view layout relationships
  50. 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);
  51. - Help provide a seamless experience - Built-in share functionality

    - Theme browser bar to application color - Performance enhancements
  52. 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(); }
  53. 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<View, Float> 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(); }
  54. 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
  55. 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
  56. 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
  57. 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
  58. Pair participants = new Pair<>(mSquareView, ViewCompat.getTransitionName(mSquareView)); ActivityOptionsCompat transitionActivityOptions = ActivityOptionsCompat.makeSceneTransitionAnimation(

    SharedTransitionsActivity.this, participants); ActivityCompat.startActivity(SharedTransitionsActivity.this, intent, transitionActivityOptions.toBundle());