Monday, January 6, 2020

PYDroid Party

Happy new year, happy new PYDroid!

PYDroid 20.7.0 has been released over the holidays! This finishes the deprecation of the old override API for the PYDroid-Arch component, and brings the new hooks API as well as the ability to nest BaseUiView<S, V> objects within each other as long as the state and event types are the same!

This also brings a shiny new convenience API called the StateSaver

  private var stateSaver: StateSaver? = null

  override fun onCreate(savedInstanceState: Bundle?) {
    ...
    stateSaver = createComponent(savedInstanceState, this, viewModel, view1, view2, view3) {
      ...
    }
  }

  override fun onSaveInstanceState(outState: Bundle) {
    stateSaver?.saveState(outState)
  }


StateSaver helps to reduce the boilerplate of saving instance state on all your views and viewmodel by wrapping them all up into a single object and calling save state on everything all at once. By marking the object as nullable and creating it in the onCreate function, you can guarantee cases where onSaveInstanceState is called before construction (like in Fragments when you rely on onViewCreated) will never crash your application - as the StateSaver will only be available once the view is created.

Oh, and speaking of saving state, UiViewModel now has a doOnSaveState { state -> ... } hook which will let you hook directly into the Android state saving lifecycle. If you are correctly modelling state in your application using UiViewModel and UiViewStates, in many cases you can remove code out of your UiView save state hooks and into the view model hook. The UiViewModel init hooks also gained support for accessing the save state bundle in the doOnInit { savedInstanceState -> ... } hook.

BaseUiView<S,V> (one's which inflate and use an Android View or ViewGroup as the parent object) have gained support for nested UiViews. You can use the top level UiView layoutRoot as the container for nested BaseUiView objects - as long as the layoutRoot is a ViewGroup. This is a bit of an API hack - as we override the parent argument passed in with the constructor and use the containing UiView instead. In a major release like 21.X.X, this API will probably be re-worked to be less clunky - but I wanted to make nesting available without breaking existing UiView setups.

Each UiView and all of its nested UiViews will be subscribed to the same UiViewModel and will receive all events in parent -> child order. This includes inflate, teardown, and save state hooks. You can nest BaseUiView objects using the nest(vararg view: BaseUiView<S,V>) function from within a BaseUiView object - usually within the init block. Though you theoretically could dynamically nest views on the fly - it is generally recommended that you always nest views in the correct relations - and then dynamically show or hide the needed views on the fly. Save your sanity.

The PYDroid UI reference library gains some new function as well - all animators in the view animator utility package will now return the animator object instead of throwing it away. This will allow you to pause and cancel animations in progress if needed. The listener override API will now append your listener to the default animation listener instead of overwriting it entirely - this change should not be noticed for too many people - but be aware as it is a minor behavior change.

List level ViewHolder performance has been improved thanks to the new ViewBinder<S> API. The ViewBinder is a lightweight component-like API, which groups a lifecycle owner with an abitrary set of UiView<S> which all share the same UiViewState. Instead of responding to UiController events, the Binder will respond to UiView events - this is because the view holder itself is mainly a light weight display container. All events would generally be forwarded to the list component which will be a part of the controller. The ViewBinder has a single method, bind(state: S) which will fire a render on all of the held view components. This helps to simplify playing with the RecyclerView ViewHolder framework - layouts are inflated, click listeners are set, components are injected or constructed - and the the ViewBinder is created via the top level bindViews(vararg views UiView<S,V>) function inside of the onCreateViewHolder function. The ViewBinder is used in the onBindViewHolder function to bind a model mapped to UiState to the list item. Generally the lifecycle owner is the container lifecycle owner passed into to the onCreate for the view holder - this allows all items to be bound and unbound with the container state.

class ViewHolder(parentLifecycleOwner: LifecycleOwner, itemView: View) : RecyclerView.ViewHolder(itemView) {

  private val viewBinder: ViewBinder<MyListItemState>

  init {
    ...
    viewBinder = bindViews(parentLifecycleOwner, view1, view2, view3) {
      ...
    }
  }

  fun bind(state: MyListItemState) {
    viewBinder.bind(state)
  }
}


You should notice these performance changes in the Open Source Licenses list page in pyamsoft applications.

Finally, though not documented, UiView initialization is now broken up into init(savedInstanceState: Bundle?) and inflate(savedInstanceState: Bundle?) steps. This is used for the new view model APIs, and breaks up the roiot view inflate step and the later inflation hooks into two seperate processes. If you, for some reason, do not use the standard UiViewModel component API, you will need to update your UiView classes to handle this view. It can be a no-op.

All of these big API changes are in prep for FridgeFriend to finally - finally ... finally release ........ someday soon! The holidays were a super lazy time for me - and as a result - I ate and drank and generally did zero work. So, continue to be patient as I continue to work hard and at some point I will deem the app good enough for a first version and that will be that.

But until then,

Stay tuned.

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================