Slide 1

Slide 1 text

View based apps with Conductor

Slide 2

Slide 2 text

Your host Simon Vergauwen Android developer [email protected] @vergauwen_simon github.com/nomisRev

Slide 3

Slide 3 text

AppFoundry

Slide 4

Slide 4 text

Topics Lifecycle Master-detail Conductor sample Animations Anko

Slide 5

Slide 5 text

Lifecycle

Slide 6

Slide 6 text

Activity Lifecycle

Slide 7

Slide 7 text

Fragment Lifecycle

Slide 8

Slide 8 text

Coding Example java.lang.NullPointerException at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:960) at android.support.v4.app.FragmentManagerImpl.performPendingDeferredStart(FragmentManager.java:768) at android.support.v4.app.FragmentManagerImpl.startPendingDeferredFragments(FragmentManager.java:1104) at android.support.v4.app.LoaderManagerImpl$LoaderInfo.onLoadComplete(LoaderManager.java:410) at android.support.v4.content.Loader.deliverResult(Loader.java:103) at android.support.v4.content.CursorLoader.deliverResult(CursorLoader.java:81) at android.support.v4.content.CursorLoader.onStartLoading(CursorLoader.java:126) at android.support.v4.content.Loader.startLoading(Loader.java:197) at android.support.v4.app.LoaderManagerImpl$LoaderInfo.start(LoaderManager.java:262) at android.support.v4.app.LoaderManagerImpl.doStart(LoaderManager.java:710) at android.support.v4.app.Fragment.onStart(Fragment.java:981) at android.support.v4.app.Fragment.performStart(Fragment.java:1332) at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:906) at android.support.v4.app.FragmentManagerImpl.attachFragment(FragmentManager.java:1240) at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:612) ...

Slide 9

Slide 9 text

Coding Example View Lifecycle package android.view; /**
 * Interface definition for a callback to be invoked when this view is attached
 * or detached from its window.
 */
 public interface OnApplyWindowInsetsListener { 
 public void onViewAttachedToWindow(View v);
 
 public void onViewDetachedFromWindow(View v);
 }

Slide 10

Slide 10 text

Conductor lifecycle

Slide 11

Slide 11 text

Android N - Multi-window • Does not affect lifecycle events • One topmost “activity” • Continue even while paused • Configuration change

Slide 12

Slide 12 text

Master-Detail

Slide 13

Slide 13 text

Master-detail

Slide 14

Slide 14 text

Android way

Slide 15

Slide 15 text

Coding Example Layout - Main 
 
 
 
 
 
 
 
 
 
 
 


Slide 16

Slide 16 text

Coding Example Layout - Master res/layout/item_list

Slide 17

Slide 17 text

Coding Example Layout - Master res/layout-w900dp/item_list 
 
 
 
 
 


Slide 18

Slide 18 text

Conductor

Slide 19

Slide 19 text

Conductor github.com/bluelinelabs/Conductor bluelinelabs.com

Slide 20

Slide 20 text

Conductor A small, yet full-featured framework that allows building View- based Android applications. Conductor provides a light-weight wrapper around standard Android Views that does just about everything you'd want.

Slide 21

Slide 21 text

Conductor

Slide 22

Slide 22 text

Dependencies • compile ‘com.bluelinelabs:conductor:2.0.3' • compile ‘com.bluelinelabs:conductor-support:2.0.3' • compile 'com.bluelinelabs:conductor-rxlifecycle:2.0.3'

Slide 23

Slide 23 text

Conductor Components • Controller (View) • Router (Backstack Handling) • ControllerChangeHandler (Animations) • RouterTransaction (Backstack Entry)

Slide 24

Slide 24 text

Coding Example Layout - Main 
 
 
 
 
 


Slide 25

Slide 25 text

Coding Example ChangeHandlerFrameLayout public class ChangeHandlerFrameLayout extends FrameLayout implements ControllerChangeListener {
 
 private int inProgressTransactionCount;
 
 @Override
 public void onChangeStarted(Controller to, Controller from, boolean isPush, ViewGroup container, ControllerChangeHandler handler) {
 inProgressTransactionCount++;
 }
 
 @Override
 public void onChangeCompleted(Controller to, Controller from, boolean isPush, ViewGroup container, ControllerChangeHandler handler) {
 inProgressTransactionCount--;
 }
 
 }

Slide 26

Slide 26 text

Coding Example ChangeHandlerFrameLayout 
 public class ChangeHandlerFrameLayout extends FrameLayout implements ControllerChangeListener {
 
 private int inProgressTransactionCount;
 
 @Override
 public boolean onInterceptTouchEvent(MotionEvent ev) {
 return (inProgressTransactionCount > 0) || super.onInterceptTouchEvent(ev);
 }
 
 public void onChangeStarted() { }
 public void onChangeCompleted() { } 
 }

Slide 27

Slide 27 text

Coding Example MainActivity public class MainActivity extends AppCompatActivity {
 @BindView(R.id.container) ChangeHandlerFrameLayout container;
 private Router router;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 router = Conductor.attachRouter(this, container, savedInstanceState);
 }
 }

Slide 28

Slide 28 text

Coding Example MainActivity public class MainActivity extends AppCompatActivity { @BindView(R.id.container) ChangeHandlerFrameLayout container;
 private Router router;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 router = Conductor.attachRouter(this, container, savedInstanceState); 
 if (!router.hasRootController()) {
 router.setRoot(RouterTransaction.with(new MasterViewController()));
 }
 }
 }

Slide 29

Slide 29 text

Coding Example MainActivity public class MainActivity extends AppCompatActivity { @BindView(R.id.container) ChangeHandlerFrameLayout container;
 private Router router;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 router = Conductor.attachRouter(this, container, savedInstanceState); 
 if (!router.hasRootController()) {
 router.setRoot(RouterTransaction.with(new MasterDetailController()));
 }
 }
 
 @Override
 public void onBackPressed() {
 if (router != null && !router.handleBack()) {
 super.onBackPressed();
 }
 }
 }

Slide 30

Slide 30 text

Coding Example MasterViewController public class MasterViewController extends Controller {
 public MasterViewController() {
 super();
 }
 
 public MasterViewController(Bundle args) {
 super(args);
 }
 }

Slide 31

Slide 31 text

Coding Example MasterViewController public class MasterViewController extends Controller {
 
 @Override
 protected View onCreateView(LayoutInflater inflater, ViewGroup container) {
 View view = inflater.inflate(R.layout.master_list_controller, container, false);
 unbinder = ButterKnife.bind(this, view);
 setUpRecyclerView();
 return view;
 } @Override
 protected void onDestroyView(View view) {
 unbinder.unbind();
 super.onDestroyView(view);
 } }

Slide 32

Slide 32 text

Coding Example MasterViewController public class MasterViewController extends Controller {
 @Override
 protected void onAttach(View view) {
 super.onAttach(view);
 dataProvider.getData().forEach(item -> itemAdapter.addItem(item));
 } }

Slide 33

Slide 33 text

Coding Example MasterViewController public class MasterViewController extends Controller { private void onRowSelected(int index) {
 Item item = itemAdapter.getItem(index);
 if (item != null) {
 showDetail(
 new DetailViewController(item.name(), item.colorId(), item.getDrawResId()),
 getCircularRevealChangeHandler(index)
 );
 }
 } }

Slide 34

Slide 34 text

Coding Example MasterViewController public class MasterViewController extends Controller {
 private void showDetail(Controller controller,ControllerChangeHandler changeHandler) { 
 if (detailContainer != null) {
 getChildRouter(detailContainer, TAG).setRoot(RouterTransaction.with(controller));
 } else {
 getRouter().pushController(
 RouterTransaction.with(controller) .tag(DETAIL_CONTROLLER)
 .pushChangeHandler(controllerChangeHandler)
 .popChangeHandler(controllerChangeHandler)
 );
 } } }

Slide 35

Slide 35 text

Animations

Slide 36

Slide 36 text

Animations • Meaningful Motion • Richer experience • Fluent • Natural

Slide 37

Slide 37 text

Coding Example Vanilla Android res/transition 
 
 


Slide 38

Slide 38 text

Coding Example Vanilla Android SecondFragment secondFragment = SecondFragment.newInstance();
 FragmentTransaction fragmentTransaction = getActivity()
 .getSupportFragmentManager()
 .beginTransaction(); 
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
 } else {
 }
 fragmentTransaction
 .replace(R.id.content_frame, secondFragment)
 .addToBackStack(SecondFragment.FRAGMENT_TAG)
 .commit();

Slide 39

Slide 39 text

Coding Example Vanilla Android SecondFragment secondFragment = SecondFragment.newInstance();
 FragmentTransaction fragmentTransaction = getActivity()
 .getSupportFragmentManager()
 .beginTransaction(); 
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
 Transition transition = TransitionInflater.from(getContext())
 .inflateTransition(R.transition.arc_shared); 
 secondFragment.setSharedElementEnterTransition(transition);
 secondFragment.setEnterTransition(new Fade()); 
 secondFragment.setReturnTransition(transition);
 setExitTransition(new Fade()); 
 fragmentTransaction.addSharedElement(floatingActionButton,
 getString(R.string.fab_transition_name));
 } else {
 
 } 
 fragmentTransaction
 .replace(R.id.content_frame, secondFragment)
 .addToBackStack(SecondFragment.FRAGMENT_TAG)
 .commit();

Slide 40

Slide 40 text

Coding Example Vanilla Android SecondFragment secondFragment = SecondFragment.newInstance();
 FragmentTransaction fragmentTransaction = getActivity()
 .getSupportFragmentManager()
 .beginTransaction(); 
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
 
 } else {
 fragmentTransaction
 .setCustomAnimations(android.R.anim.slide_in_left,
 android.R.anim.slide_out_right,
 android.R.anim.slide_in_left,
 android.R.anim.slide_out_right);
 } 
 fragmentTransaction
 .replace(R.id.content_frame, secondFragment)
 .addToBackStack(SecondFragment.FRAGMENT_TAG)
 .commit();

Slide 41

Slide 41 text

Conductor Animation

Slide 42

Slide 42 text

ControllerChangeHandler

Slide 43

Slide 43 text

ControllerChangeHandler

Slide 44

Slide 44 text

ControllerChangeHandler

Slide 45

Slide 45 text

ControllerChangeHandler

Slide 46

Slide 46 text

ControllerChangeHandler

Slide 47

Slide 47 text

ControllerChangeHandler

Slide 48

Slide 48 text

Coding Example ArcFadeMoveChangeHandler public class ArcFadeMoveChangeHandlerCompat extends TransitionChangeHandlerCompat {
 
 public ArcFadeMoveChangeHandlerCompat() {
 super(new ArcFadeMoveChangeHandler(), new HorizontalChangeHandler());
 }
 
 }

Slide 49

Slide 49 text

Coding Example HorizontalChangeHandler public class HorizontalChangeHandler extends AnimatorChangeHandler {
 @Override
 protected Animator getAnimator(@NonNull ViewGroup container, View from, View to, boolean isPush, boolean toAddedToContainer) { }
 
 @Override
 protected void resetFromView(@NonNull View from) { }
 
 }

Slide 50

Slide 50 text

Coding Example HorizontalChangeHandler public class HorizontalChangeHandler extends AnimatorChangeHandler {
 @Override protected Animator getAnimator(@NonNull ViewGroup container, View from, View to,
 boolean isPush, boolean toAddedToContainer) {
 AnimatorSet animSet = new AnimatorSet();
 
 if (isPush && from != null) {
 animSet.play(ObjectAnimator.ofFloat(from, View.TRANSLATION_X, -from.getWidth()));
 }
 if (isPush && to != null) {
 animSet.play(ObjectAnimator.ofFloat(to, View.TRANSLATION_X, to.getWidth(), 0));
 }
 
 return animatorSet;
 }
 protected void resetFromView(@NonNull View from) { }
 }

Slide 51

Slide 51 text

Coding Example ArcFadeMoveChangeHandler @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class ArcFadeMoveChangeHandler extends TransitionChangeHandler {
 
 @Override
 @NonNull
 protected Transition getTransition(@NonNull ViewGroup container, View from,
 View to, boolean isPush) {
 }
 
 }

Slide 52

Slide 52 text

Coding Example ArcFadeMoveChangeHandler @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class ArcFadeMoveChangeHandler extends TransitionChangeHandler {
 
 @Override
 @NonNull
 protected Transition getTransition(@NonNull ViewGroup container, View from,
 View to, boolean isPush) {
 TransitionSet transition = new TransitionSet() .setOrdering(TransitionSet.ORDERING_SEQUENTIAL) .addTransition(new Fade(Fade.OUT))
 .addTransition(new TransitionSet()
 .addTransition(new ChangeBounds()))
 .addTransition(new Fade(Fade.IN));
 
 transition.setPathMotion(new ArcMotion()); transition.setInterpolator(new LinearInterpolator());
 transition.setDuration(500); 
 return transition;
 }
 }

Slide 53

Slide 53 text

Coding Example ArcFadeMoveChangeHandler public class ArcFadeMoveChangeHandlerCompat extends TransitionChangeHandlerCompat {
 
 public ArcFadeMoveChangeHandlerCompat() {
 super(new ArcFadeMoveChangeHandler(), new HorizontalChangeHandler());
 }
 
 }

Slide 54

Slide 54 text

Anko

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

Advanced Layout

Slide 57

Slide 57 text

Advanced Layout

Slide 58

Slide 58 text

Advanced Layout

Slide 59

Slide 59 text

Anko performance test Specs ANKO XML Diff ALCATEL
 ONE TOUCH Mediatek MT6572
 Dual-core 1.3GHz Cortex-A7
 512MB RAM 169.33 ms 608.66 ms 359% HUAWEI Y300 Qualcomm MSM8225
 Dual-core 1.0 GHz Cortex-A5 512 MB RAM 593.66 ms 3435.33 ms 578% HUAWEI Y330 Mediatek MT6572 Dual-core 1.3 GHz Cortex-A7 512MB 162.33 ms 984 ms 606% Samsung Galaxy S2 Exynos 4210 Dual Dual-core 1.2 GHz Cortex-A9 1 GB RAM 207.33 ms 753.66 ms 363%

Slide 60

Slide 60 text

Coding Example Anko 
 
 
 
 
 
 
 
 
 


Slide 61

Slide 61 text

Coding Example MainActivity class MainActivity : AppCompatActivity() {
 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState) 
 } }

Slide 62

Slide 62 text

Coding Example MainActivity class MainActivity : AppCompatActivity() {
 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 
 coordinatorLayout {
 fitsSystemWindows = true
 
 
 } }
 }

Slide 63

Slide 63 text

Coding Example MainActivity class MainActivity : AppCompatActivity() {
 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 
 coordinatorLayout {
 fitsSystemWindows = true
 
 appBarLayout { }.lparams(width = matchParent)
 
 } }
 }

Slide 64

Slide 64 text

Coding Example MainActivity class MainActivity : AppCompatActivity() {
 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 
 coordinatorLayout {
 fitsSystemWindows = true
 
 appBarLayout { 
 toolbar {
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) elevation = 4f
 }.lparams(width = matchParent, height = actionBarSize()) }.lparams(width = matchParent)
 
 } }
 }

Slide 65

Slide 65 text

Coding Example MainActivity class MainActivity : AppCompatActivity() {
 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 
 coordinatorLayout {
 fitsSystemWindows = true
 
 appBarLayout { 
 toolbar {
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) elevation = 4f
 }.lparams(width = matchParent, height = actionBarSize()) }.lparams(width = matchParent)
 
 changeHandlerFrameLayout()
 .lparams(width = matchParent, height = matchParent) {
 behavior = AppBarLayout.ScrollingViewBehavior()
 }
 } }
 }

Slide 66

Slide 66 text

Coding Example MainActivity class MainActivity : AppCompatActivity() { private var toolBar: Toolbar? = null
 private var container: ViewGroup? = null 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 
 coordinatorLayout {
 fitsSystemWindows = true
 
 appBarLayout { 
 toolBar = toolbar {
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) elevation = 4f
 }.lparams(width = matchParent, height = actionBarSize()) }.lparams(width = matchParent)
 
 container = hangeHandlerFrameLayout()
 .lparams(width = matchParent, height = matchParent) {
 behavior = AppBarLayout.ScrollingViewBehavior()
 }
 } }
 }

Slide 67

Slide 67 text

Coding Example 
 
 
 
 
 
 
 
 
 
 coordinatorLayout {
 fitsSystemWindows = true
 
 appBarLayout { 
 toolBar = toolbar {
 if (SDK_INT >= LOLLIPOP) elevation = 4f
 }.lparams(width = matchParent, height = actionBarSize()) }.lparams(width = matchParent)
 
 changeHandlerFrameLayout()
 .lparams(width = matchParent, height = matchParent) {
 behavior = ScrollingBehavior()
 } 
 }

Slide 68

Slide 68 text

Coding Example View injection class MasterViewLayout { 
 fun bindLayout(masterView: MasterViewController): View =
 masterView.activity.UI { 
 }.view
 } fun unbind(masterView: MasterViewController) {
 container = null
 recyclerView = null
 detailContainer = null
 } 
 }

Slide 69

Slide 69 text

Coding Example View injection class MasterViewLayout : LayoutInjector { 
 fun bindLayout(masterView: MasterViewController): View =
 masterView.activity.UI {
 linearLayout { 
 configuration(orientation = Orientation.PORTRAIT) {
 
 }
 
 configuration(orientation = Orientation.LANDSCAPE) {
 
 } 
 }
 }.view 
 }

Slide 70

Slide 70 text

Coding Example View injection class MasterViewLayout : LayoutInjector { 
 fun bindLayout(masterView: MasterViewController): View {
 return masterView.activity.UI {
 masterView.background = linearLayout {
 configuration(orientation = Orientation.PORTRAIT) {
 recyclerView {
 init()
 }.lparams(width = matchParent, height = matchParent)
 }
 
 configuration(orientation = Orientation.LANDSCAPE) { 
 }
 }
 }.view
 } 
 }

Slide 71

Slide 71 text

Coding Example View injection class MasterViewLayout : LayoutInjector { 
 fun bindLayout(masterView: MasterViewController): View {
 return masterView.activity.UI {
 linearLayout {
 configuration(orientation = Orientation.PORTRAIT) {
 recyclerView {
 init()
 }.lparams(width = matchParent, height = matchParent)
 }
 
 configuration(orientation = Orientation.LANDSCAPE) {
 recyclerView {
 init()
 }.lparams(width = dip(275), height = matchParent)
 
 frameLayout {
 }.lparams(width = matchParent, height = matchParent)
 }
 }
 }.view
 } 
 }

Slide 72

Slide 72 text

Coding Example View injection class MasterViewLayout : LayoutInjector { 
 fun bindLayout(masterView: MasterViewController): View {
 return masterView.activity.UI {
 masterView.background = linearLayout {
 configuration(orientation = Orientation.PORTRAIT) {
 masterView.recyclerView = recyclerView {
 init()
 }.lparams(width = matchParent, height = matchParent)
 }
 
 configuration(orientation = Orientation.LANDSCAPE) {
 masterView.recyclerView = recyclerView {
 init()
 }.lparams(width = dip(275), height = matchParent)
 
 masterView.detailContainer = frameLayout {
 }.lparams(width = matchParent, height = matchParent)
 }
 }
 }.view
 } 
 }

Slide 73

Slide 73 text

Coding Example Runtime layouts configuration(orientation = Orientation.LANDSCAPE, smallestWidth = 700) {
 recyclerView {
 init()
 }.lparams(width = widthProcent(50), height = matchParent)
 
 frameLayout {
 
 }.lparams(width = matchParent, height = matchParent)
 } fun T.widthProcent(procent: Int): Int =
 getAppUseableScreenSize().x.toFloat()
 .times(procent.toFloat() / 100).toInt()

Slide 74

Slide 74 text

Questions? Simon Vergauwen Android Developer [email protected] @twitternaam github.com/nomisRev

Slide 75

Slide 75 text

Thank you!