[Bada] Creating Custom Lists

The Bada API provides a few different types of lists. The basic list (provided by the List class) provides a fairly basic format and supports up to two columns. The CustomList and Slidable provide greater control over the format of the list’s contents. However, these lists require more work to use it, than the basic List. This tutorial looks at how to create such a list in Bada.

Creating and Configuring the List

When using a CustomList or SlidableList, the data going into the list are represented by a series of CustomListItems. Each CustomListItem would usually be an individual row. The format or layout of each row is controlled by setting the item’s CustomListItemFormat. This separation provides allows the format to be changed quite easily. The ICustomListElement is an interface that allows custom elements, such as an icon or text with customised formatting, to be added to each row. The interface basically allows the implementation to specify what is drawn in a set area.

Essentially, the steps for creating the list are:

  1. Define the format for the row by creating and setting up a CustomListItemFormat. The format defines where each element or cell for the row will be located and the size of each cell. For textual cells, it can also define the size and colour of the text.
  2. Create the CustomListItem object that will be added to the list and set it to use the CustomListItemFormat object, by calling the item’s SetItemFormat method.
  3. The CustomListItem needs to define the elements or cells on the row. This is done using SetElement.
  4. Add the CustomListItem to the list.

An Example Custom List

As an example, I have written some example code for creating the following lists:

The following code listing shows how the list is initialised:

result
ListForm::OnInitializing(void)
{
    SlidableList* list = static_cast<SlidableList*>(GetControl(L"DataList", false));
    _pItemFactory->Initialise(list->GetWidth());
	list->AddItem(*(_pItemFactory->CreateHeading()));
    list->AddItem(*(_pItemFactory->CreateItem(L"Apple", 1, 0.50)));
    list->AddItem(*(_pItemFactory->CreateItem(L"Banana", 2, 1.50)));
    list->AddItem(*(_pItemFactory->CreateItem(L"Pear", 5, 1.25)));

	return E_SUCCESS;
}

Using Factories to Create List Items

Although the example uses a SlidableList, it will also work just as easily with a CustomList. The factory _pItemFactory simply creates the nodes that are added to the list, including the headings. In order to provide the two different looking lists, the factory need to configure the items slightly differently and its structure is illustrated below:

The TableItemFactory is used to produce a the list on the left hand side of the screen shot. The DescriptiveItemFactory produces the table on the right hand side. This abstraction is being used for demonstration only and will allows the look of the list to be easily changed by changing the factory that is used. In most cases, you probably will be using only one format at a time and you would not need to use this setup.

If you decide to download and run the example, you will notice that only one type of list is displayed at a time (it currently does not provide an option to swap between the lists from within the application). Instead, the factory is actually set in the ListForm‘s constructor in ListForm.cpp:

ListForm::ListForm() : _pItemFactory(new DescriptiveItemFactory())
{
    // Nothing to do
}

In order to change factory, all you have to do is, change the type of factory that is set in the constructor (line 1 in the above listing).

The Initialise method is common to both factories and is defined in ItemFactory:

void
ItemFactory::Initialise(const int listWidth)
{
    _pFormat = CreateFormat(listWidth);
    _pDefaultFont->Construct(Osp::Graphics::FONT_STYLE_PLAIN, 30);
}

Setting up the CustomListItemFormat

The method CreateFormat (used at line 4) will provide the CustomListItemFormat object that the CustomListItem will be using. If you think of each CustomnListItem as a "row" in the list, then the CustomListItemFormat specifies the location of each of the "cells" in a "row". The green boxes in the diagram below shows the location of the "cells" and the red rectangles shows the "rows" used in the example:

The elements to make a CustomList. The green boxes are the elements or "cells" that are specified in a CustomListItemFormat, whereas the red boxes representing the grouping into a CustomListItem or "rows".

The listing below shows how the CustomListItemFormat is created and configured for the list on the left:

CustomListItemFormat*
TableItemFactory::CreateFormat(int listWidth)
{
    CustomListItemFormat *format = new 
        CustomListItemFormat();
    format->Construct();
    int cellWidth = (listWidth / 3) - 1;
    format->AddElement(ID_NAME, Rectangle(0, 0, 
        cellWidth, 40));
    format->AddElement(ID_QUANTITY, Rectangle(
        cellWidth + 1, 0, cellWidth, 40));
    format->AddElement(ID_PRICE, Rectangle(
        (cellWidth * 2) + 2, 0, cellWidth, 40));
    return format;
}

The method AddElement is used to define the location of elements (or cells) for each row. Note that the CustomListItemFormat class have a couple of other variations of the method that are not used. Depending on how you to setup your list, the other variations may be appropriate. However, all of the available AddElement methods require a numeric integer and a rectangular region to be specified (as used in the above listing). The numeric integer (ID_NAME, ID_QUANTITY and ID_PRICE in the above listing) will later be specified to specify which “cell” a piece of content belongs, where as the rectangular region specifies where the cell will be located within the row.

Similary, the same method in DescriptiveItemFormat:

CustomListItemFormat*
TableItemFactory::CreateFormat(int listWidth)
{
    CustomListItemFormat *format = new 
        CustomListItemFormat();
    format->Construct();
    int cellWidth = (listWidth / 3) - 1;
    format->AddElement(ID_NAME, 
        Rectangle(0, 0, cellWidth, 40));
    format->AddElement(ID_QUANTITY, 
        Rectangle(cellWidth + 1, 0, 
        cellWidth, 40));
    format->AddElement(ID_PRICE, 
        Rectangle((cellWidth * 2) +
        2, 0, cellWidth, 40));
    return format;
}

Creating the List Items

The method for creating the item:

CustomListItem*
ItemFactory::CreateRow(const String& name,
        const String& quantity, const String& price)
{
    CustomListItem* item = new CustomListItem();
    item->Construct(GetRowHeight());
    item->SetItemFormat(*_pFormat);
    item->SetElement(ID_NAME,
            *(CreateTextItem(name, ID_NAME)));
    item->SetElement(ID_QUANTITY,
            *(CreateTextItem(quantity, ID_QUANTITY)));
    item->SetElement(ID_PRICE,
            *(CreateTextItem(price, ID_PRICE)));
    return item;
}

The CustomListItem‘s Construct method takes in the height of each row as its only parameter. Since the height of the item (or “row” is dependent) on the format, GetRowHeight (not listed in this tutorial, but you can find it in the available source code) is an abstract method that returns a constant. The _pFormat is the CustomListItemFormat created by one of the implementations of the CreateFormat methods, listed earlier.

Once the format is set (with SetItemFormat), the content in each of the cells can be placed (with SetElement). There are a few variations of the SetElement method:

result SetElement (int elementId, 
    const ICustomListElement &element) 

result SetElement (int elementId, 
    const Osp::Graphics::Bitmap &normalBitmap, 
    const Osp::Graphics::Bitmap *pFocusedBitmap) 
    
result SetElement (int elementId, 
    const Osp::Base::String &text) 

The elementId corresponds to one of the elements or cells specified by the item’s CustomListItemFormat object. The first variant provides the most control over the contents of the cell, whereas the second and third can be used when the contents are simply a picture or some text. For most lists containing text, the third variant would be sufficient. When using the third variant, the font size and colour are determined by the CustomListItemFormat object. In order to control the alignment of the text within the cells (for the TableItemFactory), the example creates a customised element and uses the first variant:

CustomListItem*
DescriptiveItemFactory::CreateItem(
        const String& name,
        int quantity,
        double price)
{
    String quantityText = String("Qty: ");
    quantityText.Append(Integer::ToString(quantity));

    String priceText;
    priceText.Format(30, L"Price: %.2f", price);
    return CreateRow(name, quantityText, priceText);
}

CustomListItem*
ItemFactory::CreateRow(const String& name,
        const String& quantity, const String& price)
{
    CustomListItem* item = new CustomListItem();
    item->Construct(GetRowHeight());
    item->SetItemFormat(*_pFormat);
    item->SetElement(ID_NAME,
            *(CreateTextItem(name, ID_NAME)));
    item->SetElement(ID_QUANTITY,
            *(CreateTextItem(quantity, ID_QUANTITY)));
    item->SetElement(ID_PRICE,
            *(CreateTextItem(price, ID_PRICE)));
    return item;
}

TextItem*
ItemFactory::CreateTextItem(const String& text, int id)
{
    return new TextItem(text, GetFont(id),
            GetAlignment(id));
}

The methods GetFont and GetAlignment are also not listed, but you can also find their definitions in the example’s source. They only provide a font or alignment based on the cell being created.

The Customised Element

The TextItem class provides an implementation of the ICustomListElement interface. DrawElement is the only method required by the interface and is the method that will determine the contents of the cell.

result
TextItem::DrawElement  (
        const Canvas& canvas,
        const Rectangle& rect,
        CustomListItemStatus itemStatus)
{
    Canvas *canvasPtr = const_cast<Canvas*>(&canvas);
    canvasPtr->SetFont(*(font));

    Dimension textDim;
    font->GetTextExtent(content, content.GetLength(), 
        textDim);

    int xOffset = 0;
    if (alignment != LEFT)
    {
        xOffset = rect.width - textDim.width;
        if (alignment == CENTER)
        {
            // Middle position.
            xOffset = xOffset / 2;
        }
    }

    int yOffset = (rect.height - textDim.height) / 2;
    return canvasPtr->DrawText(Point(rect.x + xOffset,
        rect.y + yOffset), content);
}

The fonts, contents and alignments used in the above listing are parameter’s to the object’s constructors and are remembered as the class’ fields. I have omitted listing the constructor for the sake of brevity. The methods GetFont and GetAlignment have not been listed for brevity, but you can find their definitions in the example’s source. They only provide a font or alignment based on the cell being created.

The TextItem class provides an implementation of the ICustomListElement interface. DrawElement is the only method required by the interface and is the method that will determine the contents of the cell.

result
TextItem::DrawElement  (
        const Canvas& canvas,
        const Rectangle& rect,
        CustomListItemStatus itemStatus)
{
    Canvas *canvasPtr = const_cast<Canvas*>(&canvas);
    canvasPtr->SetFont(*(font));

    Dimension textDim;
    font->GetTextExtent(content, content.GetLength(), 
        textDim);

    int xOffset = 0;
    if (alignment != LEFT)
    {
        xOffset = rect.width - textDim.width;
        if (alignment == CENTER)
        {
            // Middle position.
            xOffset = xOffset / 2;
        }
    }

    int yOffset = (rect.height - textDim.height) / 2;
    return canvasPtr->DrawText(Point(rect.x + xOffset,
        rect.y + yOffset), content);
}

The fonts, contents and alignments used in the above listing are parameter’s to the object’s constructors and are remembered as the class’ fields. Once again, the constructor for the list has omitted from the above listing.

Advertisements

6 Responses to [Bada] Creating Custom Lists

  1. Zoli says:

    Hi,
    thanks, this article helped me, I was suffering with Owner-draw list in Bada. Now it’s fine.

    A question…don’t you know how can I scroll a CustomList from code?

    • kahgoh says:

      Try placing your CustomList in a ScrollPanel. ScrollPanel has methods for scrolling (ScrollToTop, ScrollToBottom and SetScrollPosition). Check its documentation for details.

  2. dylan says:

    I think you missed code that deletes objects created by CreateXXX methods.

    • kahgoh says:

      Hi,

      In short, the CustomListItem is being deleted in the destructor of the ItemFactory. I had omitted listing the destructor from the post, but should be present in the downloadable example code. The CustomListItems are deleted automatically. I might have missed the TextItems though. Sometimes, you need to check the documentation to determine when objects should be deleted.

      When deleting objects that are intended to be displayed on the UI, you need to be aware that there may be some objects that you should NOT be deleting (at least according to the documentation). The documentation for CustomListItemFormat states:

      Note that CustomListItemFormat needs to be created on a heap and it should be deleted explicitly after use. However, CustomListItem should be manually deleted because items are removed from memory by the List when it is destroyed.

      Because I the same format is used each time, I use a single CustomListItemFormat for each row or item instead of having to recreate it each time. However, I do not know what it meant by “CustomListItem should be manually deleted”. Initially, I thought it meant that we have to delete the CustomListITem, but the documentation for CustomListItem says:

      CustomListItems will be deleted automatically when the CustomList is destroyed.

      I once tried deleting the CustomListItems straight after adding them to the CustomList. It seemed to cause a crash on the simulator. The documentation for CustomList agrees with this:

      Note that CustomListItem and CustomListItemFormat need to be created on a heap. CustomListItems will be deleted automatically when the CustomList is destroyed.

      The TextItems is only used for setting up the CustomListItem. Documentation for CustomListItem::SetElement:

      Do not delete element before the associated CustomListItem is removed from CustomList,
      as ICustomListElement is constantly used while CustomListItem is added to CustomList.

      Admittedly, I did not delete the TextItems. Then again, I did not have code to remove the items from the CustomList after they are added. But still, maybe they should be removed once the CustomList deletes the CustomListItems (when the CustomList is destroyed).

  3. gasolio says:

    How can i retrive a text from an item?

    • kahgoh says:

      Hi,

      If you are referring to TextItem, from the above example, it already has a content attribute. The text in the item is already this attribute, so to get the text from the item, just add a getText method that returns this.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: