Pro Yearly is on sale from $80 to $50! »

Simplify Android UI

Simplify Android UI

The introduction of Fragments in Android gave developers a tool to create more modular Android user interfaces. With the added functionality, however, a host of complexities and bugs were introduced that have still not been resolved years later. In this talk, you’ll learn the reasons to use a view-based architecture, how to construct a view only interface using Mortar and Flow, and lessons learned when moving away from a fragment­-oriented UI.

5e3e6f0c073e2d18f6c4716d83c6cea3?s=128

Steve Zeidner

August 25, 2016
Tweet

Transcript

  1. SIMPLIFY ANDROID UI Steve Zeidner @stevezeidner

  2. None
  3. None
  4. None
  5. None
  6. APPLICATION COMPONENTS • Activities • Services • Content Providers •

    Broadcast Receivers
  7. ACTIVITY “An activity represents a single screen with a user

    interface. For example, an email app might have one activity that shows a list of new emails, another activity to compose an email, and another activity for reading emails. Although the activities work together to form a cohesive user experience in the email app, each one is independent of the others.”
  8. None
  9. None
  10. FRAGMENT PROBLEMS • Lifecycle • Fragment Backstack • Tightly coupled

    View / Controller logic • Default constructor • Memory management + Fragment bugs
  11. public static MyFragment newInstance(int index) { MyFragment f = new

    MyFragment(); Bundle args = new Bundle(); args.putInt("index", index); f.setArguments(args); return f; }
  12. None
  13. None
  14. None
  15. None
  16. FLOW

  17. public final class TrackScreen { public final String albumId; public

    final String trackId; public TrackScreen(String albumId, String trackId) { this.albumId = albumId; this.trackId = trackId; } }
  18. flow.set(new TrackScreen(albumId, trackId))

  19. flow.goBack();

  20. FLOW • Navigate between screens • Manages history and persists

    state • Subflows - nested flows • Dispatcher - executes state changes
  21. MORTAR

  22. @dagger.Component(dependencies = MainActivity.Component.class) @DaggerScope(Component.class) public interface Component extends AppDependencies {

    void inject(TrackView view); } @DaggerScope(Component.class) public static class Presenter extends ViewPresenter<TrackView> { @Override protected void onLoad(Bundle savedInstanceState) { getView().albumTitle.setText("Chalk Dust Torture"); } @Override protected void onSave(Bundle outState) { super.onSave(outState); } }
  23. public class TrackView extends FrameLayout { @Bind(R.id.album_title) public TextView albumTitle;

    @Inject protected TrackScreen.Presenter presenter; public TrackView(Context context, AttributeSet attrs) { super(context, attrs); DaggerService.<TrackScreen.Component>getDaggerComponent(context).inject(this); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); presenter.takeView(this); } @Override protected void onDetachedFromWindow() { presenter.dropView(this); super.onDetachedFromWindow(); } @Override protected void onFinishInflate() { super.onFinishInflate(); ButterKnife.bind(this); } }
  24. onEnterScope(MortarScope scope) onLoad(Bundle savedInstanceState) dropView() onSave(Bundle outState) onExitScope()

  25. SCOPING AND DEPENDENCY INJECTION

  26. public class TrackView extends FrameLayout { @Bind(R.id.album_title) public TextView albumTitle;

    @Inject protected TrackScreen.Presenter presenter; public TrackView(Context context, AttributeSet attrs) { super(context, attrs); DaggerService.<TrackScreen.Component>getDaggerComponent(context).inject(this); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); presenter.takeView(this); } @Override protected void onDetachedFromWindow() { presenter.dropView(this); super.onDetachedFromWindow(); } @Override protected void onFinishInflate() { super.onFinishInflate(); ButterKnife.bind(this); } }
  27. "Don't call us, we'll call you."

  28. None
  29. @Layout(R.layout.screen_track) public class TrackScreen extends Path implements ScreenComponentFactory<MainActivity.Component> { public

    final String albumId; public final String trackId; public TrackScreen(String albumId, String trackId) { this.albumId = albumId; this.trackId = trackId; } @Override public Object createComponent(MainActivity.Component parent) { return DaggerPostScreen_Component.builder() .component(parent) .build(); } @dagger.Component(dependencies = MainActivity.Component.class) @DaggerScope(Component.class) public interface Component extends AppDependencies { void inject(TrackView view); } @DaggerScope(Component.class) public static class Presenter extends ViewPresenter<TrackView> { @Override protected void onLoad(Bundle savedInstanceState) { getView().albumTitle.setText("Chalk Dust Torture"); } @Override protected void onSave(Bundle outState) { super.onSave(outState); } } }
  30. Flow tells the app where to go while Mortar informs

    it of what to build and how long to live.
  31. None
  32. None
  33. CABINLIFE DEMO

  34. MORTAR & FLOW ADVANTAGES • MVP pattern • Memory efficient

    views • No Fragment issues! • Easier Dependency Injection
  35. MORTAR & FLOW DOWNSIDES • Steep learning curve • No

    Material style animations • It's not the Google way
  36. Should you convert an existing app?

  37. It depends.

  38. RESOURCES • [Lukas Piliszczuk] (https://github.com/lukaspili) • Flow Navigation • Mortar

    Architect
 • [CabinLife] (https://github.com/szeidner/cabinlife-android)
  39. QUESTIONS?