Feathers UI
  • Docs
  • API
  • Showcase
  • Blog
  • Community

›Custom components

Getting started

  • Installation
  • Hello World
  • Create a new project in…

    • Adobe Flash Builder
    • IntelliJ IDEA
    • FlashDevelop
    • Adobe Animate
  • Features

Components

  • Alert
  • AutoComplete
  • Button
  • ButtonGroup
  • Callout
  • Check
  • DataGrid
  • DateTimeSpinner
  • Default item renderers
  • Drawers
  • GroupedList
  • Header
  • ImageLoader
  • Label
  • LayoutGroup
  • List
  • NumericStepper
  • PageIndicator
  • Panel
  • PanelScreen
  • PickerList
  • ProgressBar
  • Radio
  • Screen
  • ScreenNavigator
  • ScrollBar
  • ScrollContainer
  • ScrollScreen
  • ScrollText
  • SimpleScrollBar
  • Slider
  • SpinnerList
  • StackScreenNavigator
  • TabBar
  • TabNavigator
  • TextArea
  • TextCallout
  • TextInput
  • Toast
  • ToggleButton
  • ToggleSwitch
  • Tree
  • WebView

Media components

  • SoundPlayer
  • VideoPlayer
  • More media controls

Text

  • Text renderers
  • Text editors
  • TextBlockTextRenderer
  • BitmapFontTextRenderer
  • TextFieldTextRenderer
  • StageTextTextEditor
  • TextBlockTextEditor
  • BitmapFontTextEditor
  • TextFieldTextEditor

Layouts

  • AnchorLayout
  • FlowLayout
  • HorizontalLayout
  • HorizontalSpinnerLayout
  • SlideShowLayout
  • TiledColumnsLayout
  • TiledRowsLayout
  • VerticalLayout
  • VerticalSpinnerLayout
  • WaterfallLayout
  • Custom layouts
  • ILayoutDisplayObject and ILayoutData (Starling version)
  • Custom layouts with virtualization

Skinning and themes

  • Skinning Feathers components
  • Introduction to themes
  • Extending the example themes
  • Creating custom themes
  • Style providers in-depth
  • Managing assets in themes
  • Original design sources for example themes

Custom components

  • Component lifecycle
  • Component anatomy
  • Validating with draw()
  • Custom item renderers
  • Custom item renderers with LayoutGroup
  • Custom item renderers with FeathersControl

Animation

  • Effects and animations
  • Navigator animated transitions

Miscellaneous

  • Displaying pop-ups
  • Focus management
  • Drag and drop
  • Tool-tips
  • Feathers SDK and MXML
  • Cookbook
  • FAQ
  • Prerelease
  • Deprecation policy
  • Beta policy

Contributing

  • Build Feathers from source
  • Coding conventions

Migration guides

  • 4.0 Migration Guide
  • 3.3 Migration Guide
  • 3.1 Migration Guide
  • 3.0 Migration Guide
  • 2.0 Migration Guide
Edit

Creating custom item renderers with the LayoutGroup container (Starling version)

The LayoutGroup container is a simple Feathers component that holds children and provides the ability to specify a layout. This makes an ideal base for any custom component, but it's especially useful for custom item renderers in the List, DataGrid, Tree and GroupedList components.

Feathers includes several subclasses of LayoutGroup that are designed specifically to help you get started creating custom item renderers (or "cell" renderers in a DataGrid).

  • LayoutGroupListItemRenderer can be used as an item renderer in a List.

  • LayoutGroupDataGridCellRenderer can be used as a cell renderer in a DataGrid.

  • LayoutGroupTreeItemRenderer can be used as an item renderer in a Tree.

  • LayoutGroupGroupedListItemRenderer can be used as an item renderer in a GroupedList.

  • LayoutGroupGroupedListHeaderOrFooterRenderer can be used as either a header renderer or a footer renderer in a GroupedList.

All of these classes implement the required functions from their respective interfaces, to save you time on bootstrapping code, and they provide a couple of useful functions that you can override the update the layout and the parse the data without worrying about the lowest level parts of the Feathers component architecture.

Below, we will look at how to create a simple custom item renderer by extending one of these classes. At the very end, the complete source code for a simple custom item renderer will be provided to offer a starting point for other custom item renderers.

These base classes for item renderers based on LayoutGroup provide the easiest way to build custom item renderers in Feathers. However, they have the risk of slightly lower performance. For a more advanced approach at a lower level in the Feathers architecture, please see Creating custom Item Renderers with FeathersControl and IListItemRenderer instead.

The Simplest Item Renderer

Let's implement a very simple item renderer. It will contain a Label component to display some text and it will be possible to customize some padding around the edges.

When it's finished, we'll want to use it like this:

var list:List = new List();
list.itemRendererFactory = function():IListItemRenderer
{
    var itemRenderer:CustomLayoutGroupItemRenderer = new CustomLayoutGroupItemRenderer();
    itemRenderer.padding = 10;
    return itemRenderer;
};
list.dataProvider = new ArrayCollection(
[
    { label: "One" },
    { label: "Two" },
    { label: "Three" },
    { label: "Four" },
    { label: "Five" },
]);
this.addChild(list);

Notice that we set a padding property to adjust the layout. The item renderer will get the text for its Label sub-component from the label property of an item in the data provider.

We could go crazy and add background skins, icons, the ability to customize the which field from the item that the label text comes from, and many more things. We're going to keep it simple for now because this is just a basic example.

For this example, we're creating an item renderer for a List component, but it will be virtually the exact same process to create an item renderer for a Tree or GroupedList component, or a header renderer or footer renderer for a GroupedList component. You simply need to change the class that you extend.

Implementation Details

Let's start out with the basic framework for our custom item renderer. We want to subclass feathers.controls.renderers.LayoutGroupListItemRenderer:

package
{
    import feathers.controls.renderers.LayoutGroupListItemRenderer;
 
    public class CustomLayoutGroupItemRenderer extends LayoutGroupListItemRenderer
    {
        public function CustomLayoutGroupItemRenderer()
        {
        }
    }
}

This base class implements the IListItemRenderer interface, so the data, index, and owner properties that the List component require on an item renderer are already there for us.

Adding Children

We want to display a Label component, so let's add a member variable for it:

protected var _label:Label;

Next, we want to create a new instance and add it as a child. We need to override initialize() function:

override protected function initialize():void
{
    this._label = new Label();
    this.addChild(this._label);
}

The initialize() function is called once the very first time that the component is added to the stage. It's a good place to create sub-components and other children and possibly to do things like add event listeners that you don't intend to remove until the component is disposed. In general, it is better to use initialize() for this sort of thing instead of the constructor.

For more information about the initialize() function and other parts of the Feathers architecture, see Anatomy of a Feathers Component.

Parsing the data

Next, we want to access the item renderer's data property and display something in our Label component. Override the convenient commitData() function to do this:

override protected function commitData():void
{
    if(this._data)
    {
        this._label.text = this._data.label;
    }
    else
    {
        this._label.text = null;
    }
}

For this particular item renderer, we're requiring all items in the data provider to have a label property that holds the text to display in the Label component. If we were building a generic item renderer, ideally, we might like to make that field name customizable, like the labelField property in DefaultListItemRenderer. However, let's keep it simple for this example.

Don't forget to handle the case where the data property is null. You don't want any runtime errors causing you trouble.

Adjusting the layout

Let's handle how the Label sub-component will be positioned and sized within the item renderer. We generally want to use a fluid layout that can handle changes in the dimensions of the item renderer (which are ultimately controlled by the parent List component). AnchorLayout is ideal for this situation.

At the beginning of the initialize() function, let's create our AnchorLayout instance:

override protected function initialize():void
{
    this.layout = new AnchorLayout();

Now, in order to have the AnchorLayout control the Label component's positioning and dimensions, we need to pass an AnchorLayoutData instance to the Label component. Let's do that next:

override protected function initialize():void
{
    this.layout = new AnchorLayout();
 
    var labelLayoutData:AnchorLayoutData = new AnchorLayoutData();
    labelLayoutData.top = 0;
    labelLayoutData.right = 0;
    labelLayoutData.bottom = 0;
    labelLayoutData.left = 0;
 
    this._label = new Label();
    this._label.layoutData = this._labelLayoutData;

We've constrained the Label component to all four edges of the item renderer. If the item renderer's width grows or shrinks, the Label component will be resized appropriately.

With that finished, we now have a fully working item renderer. However, we probably don't want the Label component to fill the item renderer right up to the edges. We probably want a little space around the edge to allow the labels to breathe when they appear next to each other in the list. Let's add a padding property to customize this spacing around the edges:

protected var _padding:Number = 0;
 
public function get padding():Number
{
    return this._padding;
}
 
public function set padding(value:Number):void
{
    if(this._padding == value)
    {
        return;
    }
    this._padding = value;
    this.invalidate(INVALIDATION_FLAG_LAYOUT);
}

When we change a property that requires the component to change something about its appearance, we need to call the invalidate() function. This will tell the component that it needs to update its appearance before the next time that Starling renders to the screen. We'll explain that constant, INVALIDATION_FLAG_LAYOUT, in a moment.

For more information about the invalidate() function and other parts of the Feathers architecture, see Anatomy of a Feathers Component.

The base class offers a preLayout() function that you can override to update layout properties on children before the layout code is run. We're going to update the Label component's AnchorLayoutData in this function:

override protected function preLayout():void
{
    var labelLayoutData:AnchorLayoutData = AnchorLayoutData(this._label.layoutData);
    labelLayoutData.top = this._padding;
    labelLayoutData.right = this._padding;
    labelLayoutData.bottom = this._padding;
    labelLayoutData.left = this._padding;
}

If we had additional children in the item renderer, we'd adjust their layoutData properties in this function too. With AnchorLayoutData, we're not limited to constraining children to the edges of their parent container. We can also position those children relative to the center of the container, both horizontally and vertical. We can even position children relative to each other too! For complete details, see How to use AnchorLayout in Feathers containers.

The base class also offers a postLayout() function that can be overridden to update anything after the layout code has run. We won't be using that one here, though.

Source Code

The complete source code for the CustomLayoutGroupItemRenderer class is included below:

package
{
    import feathers.controls.Label;
    import feathers.controls.renderers.LayoutGroupListItemRenderer;
    import feathers.layout.AnchorLayout;
    import feathers.layout.AnchorLayoutData;
 
    public class CustomLayoutGroupItemRenderer extends LayoutGroupListItemRenderer
    {
        public function CustomLayoutGroupItemRenderer()
        {
        }
 
        protected var _label:Label;
 
        protected var _padding:Number = 0;
 
        public function get padding():Number
        {
            return this._padding;
        }
 
        public function set padding(value:Number):void
        {
            if(this._padding == value)
            {
                return;
            }
            this._padding = value;
            this.invalidate(INVALIDATION_FLAG_LAYOUT);
        }
 
        override protected function initialize():void
        {
            this.layout = new AnchorLayout();
 
            var labelLayoutData:AnchorLayoutData = new AnchorLayoutData();
            labelLayoutData.top = 0;
            labelLayoutData.right = 0;
            labelLayoutData.bottom = 0;
            labelLayoutData.left = 0;
 
            this._label = new Label();
            this._label.layoutData = labelLayoutData;
            this.addChild(this._label);
        }
 
        override protected function commitData():void
        {
            if(this._data && this._owner)
            {
                this._label.text = this._data.label;
            }
            else
            {
                this._label.text = null;
            }
        }
 
        override protected function preLayout():void
        {
            var labelLayoutData:AnchorLayoutData = AnchorLayoutData(this._label.layoutData);
            labelLayoutData.top = this._padding;
            labelLayoutData.right = this._padding;
            labelLayoutData.bottom = this._padding;
            labelLayoutData.left = this._padding;
        }
    }
}

Next Steps

Looking to do more with your custom item renderer? Check out the Feathers Cookbook for "recipes" that show you how to implement typical features in custom item renderers and in other Feathers UI components.

Related Links

  • Introduction to Custom Item Renderers

  • Feathers Cookbook: Recipes for Custom Item Renderers

  • feathers.controls.renderers.LayoutGroupListItemRenderer API Documentation

  • feathers.controls.renderers.LayoutGroupDataGridCellRenderer API Documentation

  • feathers.controls.renderers.LayoutGroupTreeItemRenderer API Documentation

  • feathers.controls.renderers.LayoutGroupGroupedListItemRenderer API Documentation

  • feathers.controls.renderers.LayoutGroupGroupedListHeaderOrFooterRenderer API Documentation

Last updated on 7/30/2019
← Custom item renderersCustom item renderers with FeathersControl →
  • The Simplest Item Renderer
  • Implementation Details
    • Adding Children
    • Parsing the data
    • Adjusting the layout
  • Source Code
  • Next Steps
  • Related Links
Feathers UI
Feathers UI
  • Downloads
  • Showcase
  • Testimonials
  • Premium Support
Documentation
  • Getting Started
  • API Reference
  • Samples
    Github
  • Source Code
  • Issue Tracker
Community
  • Forum
  • Discord
  • Stack Overflow
News & Updates
  • Blog (RSS, Atom)
  • Twitter
  • Mastodon
Make a Donation
  • Join Github Sponsors
  • Donate with PayPal
  • Buy a T-Shirt
Copyright © 2023 Bowler Hat LLC — Illustrations by unDraw.