O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.
Conductor vs. Fragments
Fedorets Konstantin
Senior Android developer
What is Conductor?
• Small, yet full-featured framework that allows building
View-based Android applications.
• Was made t...
Main features
• Simpler lifecycle than fragments have
• Application can be made using only one Activity
• Immediately exec...
Under the hood
• Conductor uses Fragment in retain instance mode to
handle backstack state
• All Controllers survive when ...
Main components
• Controller
• Router
• ControllerChangeHandler
• ControllerTransaction
Controller
• The Controller is the View wrapper.
• Listen Activity’s lifecycle events like onActivityStarted(),
onActivity...
Router
• Implements navigation and backstack handling for
Controllers.
• Sends onActivityResult() only to requested Contro...
ControllerChangeHandler
• Responsible for swapping Controllers
• Performs animations and transitions between Controllers.
ChangeHandlers
ControllerChangeHanlder
SimpleSwapChangeHandler AnimatorChangeHandler
FadeChangeHandler HorizontalChangeHan...
ControllerTransaction
• Backstack entry
• Used to define data about adding Controllers.
Controller/Activity
Components
Router
Backstack
ControllerTransaction
ChangeHandlers
Child Controller
Official Lifecycle
Actual lifecycle
https://distillery.com/blog/innovating-android-development-introduction-conductor/
Kirill Akhmetov ©
Example
class MainActivity : AppCompatActivity() {
private lateinit var router: Router
override fun onCreate(savedInstance...
Example
class MainActivity : AppCompatActivity() {
private lateinit var router: Router
override fun onCreate(savedInstance...
Example
class MainActivity : AppCompatActivity() {
private lateinit var router: Router
override fun onCreate(savedInstance...
Example
override fun onBackPressed() {
if (!router.handleBack()) {
super.onBackPressed()
}
}
Example
class ParentController : Controller() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup):...
Child Routers & Controllers
public final Router getChildRouter(@NonNull ViewGroup container)
• Creates router to push chil...
Target Controllers
• setTargetController() creates and saves link to the target
controller.
• Link to the target controlle...
ChangeHandlerFrameLayout
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android...
ChangeHandlerFrameLayout
public class ChangeHandlerFrameLayout extends FrameLayout implements ControllerChangeListener {
p...
Lifecycle Listener
• LifecycleListener listens all contoller’s lifecycle events.
• It is very useful to integrate other li...
public static abstract class LifecycleListener {
public void onChangeStart(@NonNull Controller controller, @NonNull Contro...
Retain View Modes
• RELEASE_DETACH
• RETAIN_DETACH
Router Pager Adapter
• Subclass of PagerAdapter
• Used to show pages on screen with saving and restoring
states of all chi...
DialogController
• Right now it’s not a part of Conductor framework.
• To show dialog we need add Controller to view hiera...
Constructors
class BaseController : Controller {
constructor() : super()
constructor(args: Bundle?) : super(args)
}
Constructors
class ContactDetailsController : BaseController {
companion object {
private const val KEY_CONTACT_ID = "Cont...
Constructors
class ContactDetailsController : BaseController {
constructor() : super()
constructor(args: Bundle?) : super(...
Integration other libs
• Integration with
• Mosby
• Android Architecture Components(ver. 0.1.1)
• RxJava1, 2
Kotlin Android extensions
object ViewBinder {
fun setup(target: Any, view: View)
fun tearDown(target: Any)
}
Kotlin Android extensions
fun <V : View> Controller.bindView(id: Int)
: ReadOnlyProperty<Controller, V> = required(id, vie...
Kotlin Android extensions
// Usage
private val drawerLayout by bindView<DrawerLayout>(R.id.drawer_layout)
private val tool...
Sample App
• Written on Kotlin
• Uses base ChangeHandlers
• Uses Drawer
Base Controller
abstract class BaseController : Controller {
constructor() : super()
constructor(args: Bundle?) : super(ar...
Base Controller
abstract class BaseController : Controller {
constructor() : super()
constructor(args: Bundle?) : super(ar...
Parent Controller
class ParentController : BaseController {
interface ToolbarController {
val title: String?
}
constructor...
Parent Controller
class ParentController : BaseController {
private val controllerContainer by bindView<ViewGroup>(R.id.co...
Parent Controller
private var currentItemId: Int? = null
private fun handleMenuClick(item: MenuItem): Boolean {
if (curren...
Parent Controller
private var currentItemId: Int? = null
private fun handleMenuClick(item: MenuItem): Boolean {
if (curren...
Contacts Controllers
Contacts Controllers
class ContactsController : BaseController,
ParentController.ToolbarController {
private val recyclerV...
Contacts Controllers
class ContactsController : BaseController,
ParentController.ToolbarController {
private val recyclerV...
Contact Details Controller
Contact Details Controller
class ContactDetailsController : BaseController {
constructor() : super()
constructor(args: Bun...
Contact Details Controller
override fun onAttach(view: View) {
super.onAttach(view)
ContactsManager.find(contactId)?.apply...
Contact Details Controller
override fun onBindView(view: View) {
super.onBindView(view)
initToolbar()
}
private fun initTo...
Contact Details Controller
override fun onBindView(view: View) {
super.onBindView(view)
initToolbar()
}
private fun initTo...
Pros and Cons
• Less code
• Simple lifecycle
management
• Synchronous transactions
• Easy to create support for
popular fr...
https://www.appbrain.com/stats/libraries/details/conductor/conductor
Thanks
Conductor vs Fragments
Conductor vs Fragments
Conductor vs Fragments
Conductor vs Fragments
Próximos SlideShares
Carregando em…5
×
Próximos SlideShares
What to Upload to SlideShare
Avançar
Transfira para ler offline e ver em ecrã inteiro.

1

Compartilhar

Baixar para ler offline

Conductor vs Fragments

Baixar para ler offline

Talk about framework Conductor, its main features, difference from the Fragments.

Audiolivros relacionados

Gratuito durante 30 dias do Scribd

Ver tudo

Conductor vs Fragments

  1. 1. Conductor vs. Fragments Fedorets Konstantin Senior Android developer
  2. 2. What is Conductor? • Small, yet full-featured framework that allows building View-based Android applications. • Was made to simplify creating android applications and handling screens backstack. • Conductor is architecture-agnostic and does not try to force any design decisions on the developer.
  3. 3. Main features • Simpler lifecycle than fragments have • Application can be made using only one Activity • Immediately executed transactions • Persistent state • Easy navigation handling • Support standard animations • Simple integration MVP, MVVM, MVC
  4. 4. Under the hood • Conductor uses Fragment in retain instance mode to handle backstack state • All Controllers survive when change configuration happened
  5. 5. Main components • Controller • Router • ControllerChangeHandler • ControllerTransaction
  6. 6. Controller • The Controller is the View wrapper. • Listen Activity’s lifecycle events like onActivityStarted(), onActivityResumed() etc. • Has integration with onActivityResult(), onRequestPermissionsResult() etc.
  7. 7. Router • Implements navigation and backstack handling for Controllers. • Sends onActivityResult() only to requested Controller • Sends Activity’s events all parent and child controllers
  8. 8. ControllerChangeHandler • Responsible for swapping Controllers • Performs animations and transitions between Controllers.
  9. 9. ChangeHandlers ControllerChangeHanlder SimpleSwapChangeHandler AnimatorChangeHandler FadeChangeHandler HorizontalChangeHandler VerticalChangeHandler TransitionChangeHandler AutoTransitionChangeHandler
  10. 10. ControllerTransaction • Backstack entry • Used to define data about adding Controllers.
  11. 11. Controller/Activity Components Router Backstack ControllerTransaction ChangeHandlers Child Controller
  12. 12. Official Lifecycle
  13. 13. Actual lifecycle https://distillery.com/blog/innovating-android-development-introduction-conductor/ Kirill Akhmetov ©
  14. 14. Example class MainActivity : AppCompatActivity() { private lateinit var router: Router override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) router = Conductor.attachRouter(this, controllerContainer, savedInstanceState) if (!router.hasRootController()) { router.setRoot(RouterTransaction.with(ParentController())) } } override fun onBackPressed() { if (!router.handleBack()) { super.onBackPressed() } } } class ParentController : Controller() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View { return inflater.inflate(R.layout.controller_parent, container, false) } }
  15. 15. Example class MainActivity : AppCompatActivity() { private lateinit var router: Router override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) router = Conductor.attachRouter(this, controllerContainer, savedInstanceState) if (!router.hasRootController()) { router.setRoot(RouterTransaction.with(ParentController())) } } }
  16. 16. Example class MainActivity : AppCompatActivity() { private lateinit var router: Router override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) router = Conductor.attachRouter(this, controllerContainer, savedInstanceState) if (!router.hasRootController()) { router.setRoot(RouterTransaction.with(ParentController())) } } }
  17. 17. Example override fun onBackPressed() { if (!router.handleBack()) { super.onBackPressed() } }
  18. 18. Example class ParentController : Controller() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View { return inflater.inflate(R.layout.controller_parent, container, false) } }
  19. 19. Child Routers & Controllers public final Router getChildRouter(@NonNull ViewGroup container) • Creates router to push child controllers • Useful to create advanced layouts, such as Master/Detail. • Each child controller have reference to parent via getParent() method
  20. 20. Target Controllers • setTargetController() creates and saves link to the target controller. • Link to the target controller will be saved and restored when controller will be recreated
  21. 21. ChangeHandlerFrameLayout <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/drawer_layout"> <LinearLayout> <android.support.design.widget.AppBarLayout android:id="@+id/appbar_view"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar_view"/> <FrameLayout android:id="@+id/toolbar_content_view"/> </android.support.design.widget.AppBarLayout> <com.bluelinelabs.conductor.ChangeHandlerFrameLayout android:id="@+id/controller_container" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout> <android.support.design.widget.NavigationView android:id="@+id/navigation_drawer_view"/> </android.support.v4.widget.DrawerLayout>
  22. 22. ChangeHandlerFrameLayout public class ChangeHandlerFrameLayout extends FrameLayout implements ControllerChangeListener { private int inProgressTransactionCount; @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return (inProgressTransactionCount > 0) || super.onInterceptTouchEvent(ev); } @Override public void onChangeStarted(@Nullable Controller to, @Nullable Controller from, boolean isPush, @NonNull ViewGroup container, @NonNull ControllerChangeHandler handler) { inProgressTransactionCount++; } @Override public void onChangeCompleted(@Nullable Controller to, @Nullable Controller from, boolean isPush, @NonNull ViewGroup container, @NonNull ControllerChangeHandler handler) { inProgressTransactionCount--; } }
  23. 23. Lifecycle Listener • LifecycleListener listens all contoller’s lifecycle events. • It is very useful to integrate other libraries like Mosby(MVP, MVI), Android Architecture Components and others.
  24. 24. public static abstract class LifecycleListener { public void onChangeStart(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) { } public void onChangeEnd(@NonNull Controller controller, @NonNull ControllerChangeHandler changeHandler, @NonNull ControllerChangeType changeType) { } public void preCreateView(@NonNull Controller controller) { } public void postCreateView(@NonNull Controller controller, @NonNull View view) { } public void preAttach(@NonNull Controller controller, @NonNull View view) { } public void postAttach(@NonNull Controller controller, @NonNull View view) { } public void preDetach(@NonNull Controller controller, @NonNull View view) { } public void postDetach(@NonNull Controller controller, @NonNull View view) { } public void preDestroyView(@NonNull Controller controller, @NonNull View view) { } public void postDestroyView(@NonNull Controller controller) { } public void preDestroy(@NonNull Controller controller) { } public void postDestroy(@NonNull Controller controller) { } public void preContextAvailable(@NonNull Controller controller) { } public void postContextAvailable(@NonNull Controller controller, @NonNull Context context) { } public void preContextUnavailable(@NonNull Controller controller, @NonNull Context context) { } public void postContextUnavailable(@NonNull Controller controller) { } public void onSaveInstanceState(@NonNull Controller controller, @NonNull Bundle outState) { } public void onRestoreInstanceState(@NonNull Controller controller, @NonNull Bundle savedInstanceState) { } public void onSaveViewState(@NonNull Controller controller, @NonNull Bundle outState) { } public void onRestoreViewState(@NonNull Controller controller, @NonNull Bundle savedViewState) { } }
  25. 25. Retain View Modes • RELEASE_DETACH • RETAIN_DETACH
  26. 26. Router Pager Adapter • Subclass of PagerAdapter • Used to show pages on screen with saving and restoring states of all child backstacks for each pages.
  27. 27. DialogController • Right now it’s not a part of Conductor framework. • To show dialog we need add Controller to view hierarchy. • Adds an empty view to hierarchy and shows dialog in onAttach() method.
  28. 28. Constructors class BaseController : Controller { constructor() : super() constructor(args: Bundle?) : super(args) }
  29. 29. Constructors class ContactDetailsController : BaseController { companion object { private const val KEY_CONTACT_ID = "ContactDetailsController.contact_id" fun newInstance(contactId: Int): ContactDetailsController { return ContactDetailsController(Bundle().apply { putInt(KEY_CONTACT_ID, contactId) }) } } }
  30. 30. Constructors class ContactDetailsController : BaseController { constructor() : super() constructor(args: Bundle?) : super(args) private val contactId: Int by lazy { args.getInt(KEY_CONTACT_ID) } }
  31. 31. Integration other libs • Integration with • Mosby • Android Architecture Components(ver. 0.1.1) • RxJava1, 2
  32. 32. Kotlin Android extensions object ViewBinder { fun setup(target: Any, view: View) fun tearDown(target: Any) }
  33. 33. Kotlin Android extensions fun <V : View> Controller.bindView(id: Int) : ReadOnlyProperty<Controller, V> = required(id, viewFinder) fun <V : View> Controller.bindOptionalView(id: Int) : ReadOnlyProperty<Controller, V?> = optional(id, viewFinder) fun <V : View> Controller.bindViews(vararg ids: Int) : ReadOnlyProperty<Controller, List<V>> = required(ids, viewFinder) fun <V : View> Controller.bindOptionalViews(vararg ids: Int) : ReadOnlyProperty<Controller, List<V>> = optional(ids, viewFinder)
  34. 34. Kotlin Android extensions // Usage private val drawerLayout by bindView<DrawerLayout>(R.id.drawer_layout) private val toolbarView by bindView<Toolbar>(R.id.toolbar_view) private val navigationView by bindView<NavigationView>(R.id.navigation_drawer_view) private val controllerContainer by bindView<ViewGroup>(R.id.controller_container)
  35. 35. Sample App • Written on Kotlin • Uses base ChangeHandlers • Uses Drawer
  36. 36. Base Controller abstract class BaseController : Controller { constructor() : super() constructor(args: Bundle?) : super(args) }
  37. 37. Base Controller abstract class BaseController : Controller { constructor() : super() constructor(args: Bundle?) : super(args) override fun onCreateView(inflater: LayoutInflater, container: ViewGroup): View { val view = inflateView(inflater, container) ViewBinder.setup(this, view) onBindView(view) return view } abstract fun inflateView(inflater: LayoutInflater, container: ViewGroup): View override fun onDestroyView(view: View) { ViewBinder.tearDown(this) super.onDestroyView(view) } open fun onBindView(view: View) { } }
  38. 38. Parent Controller class ParentController : BaseController { interface ToolbarController { val title: String? } constructor() : super() constructor(args: Bundle?) : super(args) }
  39. 39. Parent Controller class ParentController : BaseController { private val controllerContainer by bindView<ViewGroup>(R.id.controller_container) private lateinit var controllersRouter: Router override fun onBindView(view: View) { super.onBindView(view) setHasOptionsMenu(true) controllersRouter = getChildRouter(controllerContainer) } }
  40. 40. Parent Controller private var currentItemId: Int? = null private fun handleMenuClick(item: MenuItem): Boolean { if (currentItemId == item.itemId) { return true } return when (item.itemId) { R.id.menu_contacts -> { startController(ContactsController(), item.itemId) true } R.id.menu_profile -> { startController(ProfileController(), item.itemId) true } else -> false } } private fun <T> startController(controller: T, itemId: Int) where T : Controller, T : ToolbarController { controllersRouter.setRoot(RouterTransaction.with(controller)) toolbarView.title = controller.title currentItemId = itemId }
  41. 41. Parent Controller private var currentItemId: Int? = null private fun handleMenuClick(item: MenuItem): Boolean { if (currentItemId == item.itemId) { return true } return when (item.itemId) { R.id.menu_contacts -> { startController(ContactsController(), item.itemId) true } R.id.menu_profile -> { startController(ProfileController(), item.itemId) true } else -> false } } private fun <T> startController(controller: T, itemId: Int) where T : Controller, T : ToolbarController { controllersRouter.setRoot(RouterTransaction.with(controller)) toolbarView.title = controller.title currentItemId = itemId }
  42. 42. Contacts Controllers
  43. 43. Contacts Controllers class ContactsController : BaseController, ParentController.ToolbarController { private val recyclerView by bindView<RecyclerView>(R.id.contacts_list) override fun onBindView(view: View) { super.onBindView(view) recyclerView.adapter = ContactsAdapter(object : ContactsAdapter.Listener { override fun onContactSelected(contactData: ContactData) { rootRouter()?.pushController( RouterTransaction.with( ContactDetailsController.newInstance(contactData.id) ) .pushChangeHandler(HorizontalChangeHandler()) .popChangeHandler(HorizontalChangeHandler()) ) } }) }
  44. 44. Contacts Controllers class ContactsController : BaseController, ParentController.ToolbarController { private val recyclerView by bindView<RecyclerView>(R.id.contacts_list) override fun onBindView(view: View) { super.onBindView(view) recyclerView.adapter = ContactsAdapter(object : ContactsAdapter.Listener { override fun onContactSelected(contactData: ContactData) { rootRouter()?.pushController( RouterTransaction.with( ContactDetailsController.newInstance(contactData.id) ) .pushChangeHandler(HorizontalChangeHandler()) .popChangeHandler(HorizontalChangeHandler()) ) } }) }
  45. 45. Contact Details Controller
  46. 46. Contact Details Controller class ContactDetailsController : BaseController { constructor() : super() constructor(args: Bundle?) : super(args) private val contactId: Int by lazy { args.getInt(KEY_CONTACT_ID) } companion object { private const val KEY_CONTACT_ID = "ContactDetailsController.contact_id" fun newInstance(contactId: Int): ContactDetailsController { return ContactDetailsController(Bundle().apply { putInt(KEY_CONTACT_ID, contactId) }) } } }
  47. 47. Contact Details Controller override fun onAttach(view: View) { super.onAttach(view) ContactsManager.find(contactId)?.apply { nameTextView.text = name phoneTextView.text = phone addressTextView.text = address emailTextView.text = email } }
  48. 48. Contact Details Controller override fun onBindView(view: View) { super.onBindView(view) initToolbar() } private fun initToolbar() { toolbarView.setNavigationOnClickListener { router.popCurrentController() } toolbarView.inflateMenu(R.menu.contact_details_menu) toolbarView.setOnMenuItemClickListener { item -> return@setOnMenuItemClickListener when (item.itemId) { R.id.edit -> { router.pushController( RouterTransaction.with( EditContactController.newInstance(contactId) ) .pushChangeHandler(FadeChangeHandler()) .popChangeHandler(FadeChangeHandler()) ) true } else -> false } } }
  49. 49. Contact Details Controller override fun onBindView(view: View) { super.onBindView(view) initToolbar() } private fun initToolbar() { toolbarView.setNavigationOnClickListener { router.popCurrentController() } toolbarView.inflateMenu(R.menu.contact_details_menu) toolbarView.setOnMenuItemClickListener { item -> return@setOnMenuItemClickListener when (item.itemId) { R.id.edit -> { router.pushController( RouterTransaction.with( EditContactController.newInstance(contactId) ) .pushChangeHandler(FadeChangeHandler()) .popChangeHandler(FadeChangeHandler()) ) true } else -> false } } }
  50. 50. Pros and Cons • Less code • Simple lifecycle management • Synchronous transactions • Easy to create support for popular frameworks (Mosby, Rx, Architecture components) • No documentation • Bad compatibility with some libraries(e.g. Easy Permission) • Maintenance (last commit was in October)
  51. 51. https://www.appbrain.com/stats/libraries/details/conductor/conductor
  52. 52. Thanks
  • PKJeff

    Nov. 6, 2018

Talk about framework Conductor, its main features, difference from the Fragments.

Vistos

Vistos totais

1.137

No Slideshare

0

De incorporações

0

Número de incorporações

10

Ações

Baixados

5

Compartilhados

0

Comentários

0

Curtir

1

×