ListView – kontrolka dostępna od SDK 2.0 w przestrzeni nazw Osp::Ui::Controls, która wyświetla opcje wyboru w postaci listy.
Artykuł jest kontynuacją wcześniejszego artykułu pod tytułem: UI: ListView (Lista) i zaznajomienie się z nim jest obowiązkowe.
Wszystkie informacje w nim zawarte, jak również kod źródłowy są aktualne i będą wykorzystane także w tym artykule.
Naszym celem będzie stworzenie listy o następującym wyglądzie:
Jest to pewnego rodzaju lista kontaktów 🙂 Po zaznaczeniu opcji pojawia się wiek wybranego kontaktu i podświetlenie opcji – to tylko taki przykład.
Wiek będzie jakąś liczbą losową, aby nie komplikować naszej aplikacji o dodatkowe struktury danych przechowujące kontakty.
My skorzystamy ze zwykłej kolekcji Stringów z poprzedniego przykładu inicjalizując ją po prostu innymi danymi.
Musimy się zastanowić jak pociąć nasz obrazek na mniejsze kawałki i za ich pomocą rysować listę o dowolnych rozmiarach na dowolnych urządzeniach (z rożnymi rozdzielczościami).
W przypadku aplikacji J2ME, gdzie urządzenia miały dość ograniczone zasoby, z opcji listy zaznaczonej szarym gradientem wyciąłbym po prostu fragment o szerokości 1px i wysokości 100% opcji.
Przy rysowaniu zastosowałbym pętlę, która wstawiałaby ten fragment obrazka tyle razy, ile wynosi szerokość ekranu.
Zaletą tego sposobu jest na pewno mniejsze użycie pamięci (dyskowej), ale o wiele razy dłuższe renderowanie grafiki.
Jest to temat rzeka, dlatego ograniczę się do zoptymalizowania aplikacji pod telefony z rozdzielczością WVGA.
W związku z tym, że aplikację piszemy tylko dla trybu portretowego, maksymalna szerokość listy dla WVGA to 480px.
Tło opcji listy po wycięciu posiada rozmiary: 480px x 41px.
ListItem.png – tło bez podświetlenia:
ListItemHover.png – tło z podświetleniem:
Czas otworzyć projekt z poprzedniego artykułu – zaczynami programować 🙂
Dodajemy nasze obrazki (oba) do projektu:
Po dodaniu naszych obrazków, środowisko stworzyło odpowiednie foldery z obrazkami:
Aby zaimplementować rysowanie opcji listy, należy stworzyć nową klasę implementującą interfejs: Osp::Ui::Controls::ICustomElement.
Interfejs wymaga implementacji tylko jednej metody:
virtual bool OnDraw(Osp::Graphics::Canvas &canvas, const Osp::Graphics::Rectangle &rect, Osp::Ui::Controls::ListItemDrawingStatus status);
Po stworzeniu klasy przenosimy plik nagłówkowy MyCustomListItem.h z folderu src do inc. Wystarczy w oknie Project Explorer przeciągnąć go myszką.
Następne modyfikujemy plik nagłówkowy MyCustomListItem.h wg. poniższego wzorca. Możesz skopiować cały kod i wkleić w miejsce starego.
/*
* MyCustomListItem.h
*
* Created on: 03-10-2011
* Author: markac
*/
#ifndef MYCUSTOMLISTITEM_H_
#define MYCUSTOMLISTITEM_H_
#include <FApp.h>
#include <FBase.h>
#include <FUi.h>
#include <FGraphics.h>
using namespace Osp::Base;
using namespace Osp::Graphics;
class MyCustomListItem :
public Osp::Ui::Controls::ICustomElement
{
public:
MyCustomListItem(Osp::Base::String& person, int age);
virtual bool OnDraw(Osp::Graphics::Canvas &canvas, const Osp::Graphics::Rectangle &rect, Osp::Ui::Controls::ListItemDrawingStatus status);
protected:
static Osp::Graphics::Bitmap* __pItemNormalBitmap;
static Osp::Graphics::Bitmap* __pItemHoverBitmap;
String* __person;
int __age;
};
#endif /* MYCUSTOMLISTITEM_H_ */
Implementacja metody rysującej w pliku MyCustomListItem.cpp może wyglądać jak poniżej.
/*
* MyCustomListItem.cpp
*
* Created on: 03-10-2011
* Author: markac
*/
#include "MyCustomListItem.h"
Osp::Graphics::Bitmap* MyCustomListItem::__pItemNormalBitmap = null;
Osp::Graphics::Bitmap* MyCustomListItem::__pItemHoverBitmap = null;
MyCustomListItem::MyCustomListItem(Osp::Base::String& person, int age)
{
__person = &person;
__age = age;
if (__pItemNormalBitmap == null)
{
// Pierwsze tworzenie obrazka
__pItemNormalBitmap = Osp::App::AppResource::GetInstance()->GetBitmapN("ListItem.png");
}
if (__pItemHoverBitmap == null)
{
// Pierwsze tworzenie obrazka
__pItemHoverBitmap = Osp::App::AppResource::GetInstance()->GetBitmapN("ListItemHover.png");
}
}
bool MyCustomListItem::OnDraw(Osp::Graphics::Canvas &canvas, const Osp::Graphics::Rectangle &rect, Osp::Ui::Controls::ListItemDrawingStatus status)
{
Osp::Graphics::Font font;
font.Construct(FONT_STYLE_PLAIN, 28);
canvas.SetFont(font);
if (status == Osp::Ui::Controls::LIST_ITEM_DRAWING_STATUS_NORMAL)
{
canvas.SetForegroundColor(Color(77, 77, 77));
canvas.DrawBitmap(Point(0, 0), *__pItemNormalBitmap);
canvas.DrawText(Point(20, (rect.height / 2) - font.GetMaxHeight() / 2), *__person);
}
else if (status == Osp::Ui::Controls::LIST_ITEM_DRAWING_STATUS_PRESSED || status == Osp::Ui::Controls::LIST_ITEM_DRAWING_STATUS_HIGHLIGHTED)
{
canvas.SetForegroundColor(Color::COLOR_WHITE);
canvas.DrawBitmap(Point(0, 0), *__pItemHoverBitmap);
canvas.DrawText(Point(20, (rect.height / 2) - font.GetMaxHeight() / 2), *__person);
/**
* Pozycjonowanie wieku w prostokącie i centrowanie
*/
Osp::Graphics::Font ageFont;
ageFont.Construct(FONT_STYLE_PLAIN, 18);
canvas.SetFont(ageFont);
String age;
age.Append(__age);
Dimension ageDimension;
ageFont.GetTextExtent(age, age.GetLength(), ageDimension);
int x = rect.width - 48 - (ageDimension.width / 2);
int y = (rect.height / 2) - (ageDimension.height / 2);
canvas.DrawText(Point(x, y), age);
}
return true;
}
Pozostało nam jeszcze dołączyć plik nagłówkowy MyCustomListItem.h w pliku Form1.h:
#include "MyCustomListItem.h"
i lekko zmodyfikować metodę CreateItem() z pliku Form1.cpp zaimplementowaną w poprzednim artykule. Oto cały kod nowej metody:
Osp::Ui::Controls::ListItemBase* Form1::CreateItem(int index, int itemWidth)
{
ListAnnexStyle style = LIST_ANNEX_STYLE_NORMAL;
CustomItem* pItem = new CustomItem();
pItem->Construct(Osp::Graphics::Dimension(itemWidth, 41), style);
String* text;
__strings.GetAt(index, text);
int age = Utility::Math::Rand() % 99; // 0-99
MyCustomListItem* myItem = new MyCustomListItem(*text, age);
int elementId = index;
pItem->AddElement(Osp::Graphics::Rectangle(0, 0, itemWidth, 41), elementId, *myItem);
return pItem;
}
W związku z tym, że operujemy na obrazkach, powinniśmy dodać odpowiednie uprawnienie: IMAGE do pliku manifest.xml.
Powiadomienie o tym fakcie ukaże się w dymku przed niektórymi funkcjami.
Uprawnienie jest wymagane na urządzeniu docelowym. W przypadku emulatora, jesteśmy tylko o tym informowani.
Dociekliwi programiści zapewne zauważą, że nigdzie nie są zwalniane zasoby tworzone w konstruktorze klasy MyCustomListItem. Chodzi tutaj o funkcję GetBitmapN().
W projekcie do pobrania (ukaże się wkrótce) zostanie dodana nowa metoda statyczna zwalniająca te zasoby podczas zamykania aplikacji.
A o to efekt końcowy:
Działanie aplikacji demonstruje także poniższy film:
[yframe url=’http://www.youtube.com/watch?v=KQntRfOM7d0′]
Komentarze mile widziane.








Akurat teraz tworzę listy i zastanawiałem się jak w prosty sposób dodać dodatkowe informacje, a tu proszę…. kompletne rozwiązanie. Świetny tutorial.
Trzeba pamiętać, żeby zwolnić pamięć zajmowaną przez myItem. Na tamten czas o tym zapomniałem i jeszcze nie zmodyfikowałem artykułu.
Myślę, że jest to dość jasne ;D
MarekMariusz jak zwykle mnie nie zawiódł, a co do konkretów to uważam ten typ tworzenia listy jest lepszy od poprzedniego nie tylko z powodu lepszego wyglądy, ale i jak autor pisze jest mniejsze użycie pamięci co jest naprawdę dużym plusem. Widzę też, że muszę przyspieszyć prace nad animacją, bo przydałaby się do demonstracji.