Clone wiki

List View Framework / Core Classes

Core Classes

  • ListViewController.cs
    • ListViewController
      • This class sets up a vanilla component which can be used in a scene. It has the same name as the file so it can be added in the UI.
    • ListViewControllerBase
      • Contains all public/protected variables and methods which are agnostic to the type of data being viewed. In this way we can use a ListViewControllerBase reference to scroll the list, and avoid needing to use the generic type
    • ListViewController<DataType, ItemType>
      • Defines the remainder of the component, which deals with the data. This is separate from the base class for cases when we don’t need to know what type of data the list is working with and we use variables of type ListViewControllerBase
        Figure2.png
  • ListViewInputHandler.cs
    • ListViewInputHandler
      • Wraps the Update function and provides a ListViewControllerBase variable
    • ListViewScroller
      • Contains methods and variables for scrolling, which is a very common task.
  • ListViewItem.cs

    • ListViewItem
      • Like ListViewController, this is a vanilla Item component which can be dragged onto objects for the most generic case, as in the early examples.
    • ListViewItem<DataType>

      • At a minimum, the component that goes on scene objects for rows in the list will need a reference to the data object which drives it and a setup function to update the information it displays

      Figure3.png

    • ListViewItemData

      • Base class for the information contained in the data model. Each row in the list needs to know what template will be used to generate it, and a reference to the scene object which currently represents it.
    • ListViewItemNested<ChildType>
      • Nested lists may have children at each row, and any row may be expanded or collapsed. The data model stores the expanded state, and the view logic decides whether to display the children
    • ListViewItemInspectorData
      • This is a vanilla version of the class which is tagged as Serializable so that it can be set up and viewed in the inspector. This is helpful for debugging, e.g. toggle the expanded state of a nested list right in the inspector without in-game UI. Note that in the case of nested data, Unity will give you warnings if you tag it as Serialized.
    • ListViewItemTemplate

      • The ListViewController will create an instance of this class for each template it uses in order to keep track of the pool of instantiated copies of the specified prefab.

      Figure5.png

Abstract Methods

The main goal with these core classes is to set up a basic working list view, where practically every function is virtual so that it can be extended or overridden by developers who want to create their own behavior. Below are descriptions of these virtual functions (note we interchangeably use ListViewControllerBase and ListViewController<DataType, ItemType> since they essentially form a single class)

  • Setup()
    • Called from Start, and provides place where programmers can do one-time setup procedures.
  • ViewUpdate()
    • Called from Update. It calls ComputeConditions and UpdateItems. The only reason to override ViewUpdate is to stop it from calling those functions.
  • ComputeConditions()
    • If you wanted to save some CPU time, you could put most of this into Setup. The reason that we compute things like the range and item size on every frame is in case some external action changes these states. The list will know how to adjust its display if these states change.
  • GetObjectSize(Gameobject g)
    • Called by ComputeConditions on the first template in the list to establish sizing information in order to evenly space the list items. It currently uses render bounds, but different implementations could override this function to use a collider or some other basis for this spacing parameter.
  • UpdateItems()
    • This is where most of the processing happens. It is responsible for iterating over the data list, collecting scene objects which should no longer be visible, and creating and/or positioning the visible objects based on the current scroll offset.
  • ExtremeLeft()/ExtremeRight()
    • Called by UpdateItems, these methods will call RecycleItem on objects that should no longer be displayed in the list. Most of the time they will do nothing, but in the case of an item which has just gone out of range this frame, they pool the item.
  • ListMiddle()
    • Called for all list items in the visible range. If the datum does not have a reference to a scene object, GetItem is called to get one, and then Positioning is called on the new (or existing) scene object.
  • Positioning()
    • Uses the parameters from ComputeConditions as well as an offset parameter to set the position of all visible scene objects every frame. This function can take care of updating animations or interpolated positions, but in the generic case it just sets the item’s position directly.
  • GetItem(DataType data)
    • Returns a scene object based on the data provided. If there is a pooled object that was created from the requested template, it will remove the object from the pool. If no such object exists, it will instantiate a new one. GetItem will also call Setup() on the item right before it is returned.
  • RecycleItem(string template, MonoBehaviour item)
    • Deactivates a scene object and adds it to the pool for the given template. After calling RecycleItem, the caller should set the data object’s item variable to null to indicate that it has no view representation. This detaches the scene object from the data, getting it ready to serve a different row.
  • ScrollNext()/ScrollPrev()
    • Convenience functions to scroll by one whole object’s width at a time.
  • ScrollTo()
    • This will scroll the list to a particular data index. It makes less sense in the case of a nested list, since the index of a particular item in the list will change based on whether previous items are expanded or collapsed.

Overview

Whew! That was a lot! But on the bright side, that’s pretty much everything!

The most important functions to consider are ComputeConditions and UpdateItems. UpdateItems is responsible for evaluating the current state of the data and updating the view to reflect that. This includes:

  • cleaning up scene objects which should no longer be visible
  • activating or creating new visible objects and setting them up to display the correct data
  • laying out the scene objects based on the current scrolling state.

The procedure will change slightly between implementations, but generally displaying the list involves iterating over the entire local data and either showing or hiding rows depending on whether they are in the visible range. It may seem inefficient to touch every row, but for really large data sets we employ a caching strategy. To determine which objects are visible, we keep a data offset variable in ComputeConditions to store the start index for visible list items. Any items in the list up until that index (or count, in the case of a nested list) will be cleaned up. After we hit the data offset, we start displaying and positioning rows until we count up to the number of items which fit in the visible range, after which we clean up the remaining rows. In the case of large or non-local data sets, ComputeConditions is also responsible for fetching the next batch of data.

Figure4.png

Updated