Skip to main content

UI: Custom ListView – Lista

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.

markac

Full-stack Web Developer

3 thoughts to “UI: Custom ListView – Lista”

  1. Akurat teraz tworzę listy i zastanawiałem się jak w prosty sposób dodać dodatkowe informacje, a tu proszę…. kompletne rozwiązanie. Świetny tutorial.

    1. 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

  2. Marek Mariusz 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.

Komentarze są zamknięte.