viernes, 10 de diciembre de 2010

TUTORIAL: Desarrollo de aplicaciones para bada (IX)

BuddyFix: Rellenar la lista principal

Para completar la UI básica de BuddyFix necesitaremos:

- Implementar la softkey "Add"
- Implementar el menú de opciones
- Rellenar la lista

Añadir un OptionMenu

El menú de opciones (OptionMenu) aparece cuando el usuario presiona la softkey "Option" en un Form que tiene la propiedad "FORM_STYLE_OPTIONKEY" establecida como "True".
Para cada Form se puede definir su propio Option Menu, y decidir si mostrarlo o no.

La guía de creación de UIs especifica algunas pautas a seguir a la hora de diseñar un Option Menu:
- Las funciones de la softkey no deberían estar duplicadas en el menú de opciones
- El número máximo de elementos es 12, pero si ponemos como mucho entre 4 y 6 elementos mejor
- Los elementos del menú pueden tener elementos secundarios, para los cuales el límite preferible es 4 subelementos

La clase "OptionMenu" deriva de "Control" vía "Container". Los principios de construcción y utilización de un menú de opciones son los siguientes:

- Construir un objeto OptionMenu dinámicamente, típicamente cuando se inicia el Form que mostrará el menú
- Especificar un identificador de evento y un listener para la acción de presionar la softkey
- Crear los elementos del menú y añadir un identificador de evento para cada uno
- Implementar un "action event handler" (gestor de eventos) que responda a los eventos de la softkey y de los elementos del menú
- Eliminar el objeto OptionMenu, típicamente cuando se cierra el Form que mostrará el menú

Los identificadores de eventos son simplemente números enteros. Se suele asignar 100 al evento de tecla, 101 al primer elemento de la raíz del menú de opciones, 201 al primer subelemento, y así sucesivamente:


Una vez que especificados los identificadores de evento, el framework devolverá los identificadores correspondientes a las acciones que el usuario efectúe sobre el menú.

Para BuddyFix, implementaremos un menú de cuatro elementos y dos subelementos:

"Amigos"
- "Añadir amigo" (subelemento, añade un amigo a la lista)
- "Eliminar amigo" (subelemento, elimina un amigo de la lista)
"Pausar intercambio" si está activo o "Reanudar intercambio" si está en pausa, (pausa o reanuda el intercambio de ubicación de todos los amigos)
"Preferencias" (lanza un menú emergente de preferencias)
"Acerca de ..." (lanza un menú emergente con información sobre la aplicación)

Manos a la obra...
Primero, en "MainListForm.h", añade un puntero a OptionMenu llamado "pOptMenu", y define los identificadores de evento como "protected":

// Implementation
protected:
    Osp::Ui::Controls::OptionMenu*
    pOptMenu; static const int ID_OPTIONKEY = 100;
    static const int ID_OPTIONMENU_ITEM_1 = 101;
    static const int ID_OPTIONMENU_ITEM_2 = 102;
    static const int ID_OPTIONMENU_ITEM_3 = 103;
    static const int ID_OPTIONMENU_ITEM_4 = 104;
    static const int ID_OPTIONMENU_ITEM_5 = 105;
    static const int ID_OPTIONMENU_SUBITEM_1 = 201;
    static const int ID_OPTIONMENU_SUBITEM_2 = 202;

Ahora, en "MainListForm.cpp", añade el código siguiente en el método "Initialize()", que indica que queremos establecer "ID_OPTIONKEY" como identificador del botón del menú de opciones (Optionkey), y que queremos añadir al Form un Action Listener para ese botón :


    // Invoked on the Form, not the menu
    SetOptionkeyActionId(ID_OPTIONKEY);
    AddOptionkeyActionListener(*this);

Finalmente añade las líneas de código para crear el OptionMenu (el menú de opciones):

    // Create the OptionMenu
    // Member in MainListForm Osp::Ui::Controls::OptionMenu* pOptMenu
    pOptMenu = new OptionMenu;
    pOptMenu->Construct();
    pOptMenu->AddItem("Amigos",ID_OPTIONMENU_ITEM_1);
    pOptMenu->AddSubItem(0, "Añadir amigo",ID_OPTIONMENU_SUBITEM_1);
    pOptMenu->AddSubItem(0, "Eliminar amigo",ID_OPTIONMENU_SUBITEM_2);
    pOptMenu->AddItem("Pausar intercambio",ID_OPTIONMENU_ITEM_2);
    pOptMenu->AddItem("Reanudar intercambio",ID_OPTIONMENU_ITEM_3);
    pOptMenu->AddItem("Preferencias",ID_OPTIONMENU_ITEM_4);
    pOptMenu->AddItem("Acerca de BuddyFix",ID_OPTIONMENU_ITEM_5);


Ahora el método Initialize() debe tener el siguiente aspecto (fíjate en el uso de la construcción en dos pasos para reservar memoria e inicializar el objeto OptionMenu):


Como hemos reservado memoria para el objeto menú, nos tendremos que ocupar de liberar el espacio ocupado, en el método "OnTerminating" de "MainListForm.cpp", mediante la orden "delete":

result
MainListForm::OnTerminating(void)
{
    result r = E_SUCCESS;
    delete pOptMenu;
    return r;
}


Finalmente, necesitamos gestionar los eventos que las acciones sobre el menú generan. En "MainListForm.cpp", añade el siguiente código de gestión de eventos en el método "OnActionPerformed()":

void
MainListForm::OnActionPerformed(const Osp::Ui::Control &source, int actionId)
{
    switch (actionId)
    {
        case ID_OPTIONKEY:
            ShowOptionMenu();
            break;
            // Menu items
        case ID_OPTIONMENU_ITEM_1:
            // Todo: insert your code here break;
            // ... etc
            // Menu sub-items
        case ID_OPTIONMENU_SUBITEM_1:
            // Todo: insert your code here
            break;
        case ID_OPTIONMENU_SUBITEM_2:
            // Todo: insert your code here
            break;
        default: break;
    }

}


Nuestra implementación casi está completa. El último paso es añadir métodos para mostrar y esconder el menú de forma dinámica. De la ocultación del menú se encarga el framework, pero mostrarlo cuando se presione la softkey "Option" es nuestra responsabilidad. Añade, en "MainListForm.cpp":

void MainListForm::ShowOptionMenu(void)
{
    pOptMenu->SetShowState(true);
    pOptMenu->Show();
}

void MainListForm::HideOptionMenu(void)
{
    pOptMenu->SetShowState(false);
    Draw();
    Show();
}


Fíjate que en "ShowOptionMenu()", "Show()" se invoca sobre el objeto del menú de opciones. 
No olvides añadir las declaraciones en "MainListForm.h":

// Construction
public:
    MainListForm(void);
    virtual ~MainListForm(void);
    bool Initialize();
    result OnInitializing(void);
    result OnTerminating(void);
    void ShowOptionMenu(void);
    void HideOptionMenu(void);



Añadir softkeys

Añadir una softkey es sencillo. La softkey se comporta exactamente como el botón del menú de opciones. En la definición de "MainListForm" en "MainListForm.h", añadimos un identificador de softkey, como hicimos para el menú de opciones:

// Implementation
protected:
    Osp::Ui::Controls::OptionMenu* pOptMenu; 

    static const int ID_OPTIONKEY = 100;
    static const int ID_OPTIONMENU_ITEM_1 = 101;
    static const int ID_OPTIONMENU_ITEM_2 = 102;
    static const int ID_OPTIONMENU_ITEM_3 = 103;
    static const int ID_OPTIONMENU_ITEM_4 = 104;
    static const int ID_OPTIONMENU_ITEM_5 = 105;
    static const int ID_OPTIONMENU_SUBITEM_1 = 201;
    static const int ID_OPTIONMENU_SUBITEM_2 = 202;
    static const int ID_LEFT_SOFTKEY = 900;


En el método "Initialize()" de "MainListForm.cpp" añade estas dos líneas para definir el identificador de acción (action ID) de la softkey y su listener, como hicimos con la Optionkey:


    SetSoftkeyActionId(SOFTKEY_0,ID_LEFT_SOFTKEY);
    AddSoftkeyActionListener(SOFTKEY_0, *this);


Para capturar y gestionar el evento de la softkey, en "MainListForm.cpp" añade un "case" en la parte superior de la sentencia "switch" del método "OnActionPerformed()":

void
MainListForm::OnActionPerformed(const Osp::Ui::Control &source, int actionId)
{
    switch (actionId)
    {
        case ID_LEFT_SOFTKEY:
            ShowOptionMenu();
            break;

        case ID_OPTIONKEY:
            ShowOptionMenu();
            break;

        ...

Rellenar la lista

No podemos ver el aspecto de la lista de amigos hasta que la relenemos con algo.
Como de momento solo nos interesa validar el correcto funcionamiento de la UI, simplemente escribiremos un método de test para llenar la lista con datos de prueba.
Para añadir un elemento, crearemos el método "TestAdditem" y lo definiremos como método "protected" de MainListForm. Recuerda que elegimos el formato "LIST_ITEM_DOUBLE_IMAGE_TEXT_FULLTEXT" para mostrar una primera línea de imagen y texto y una segunda línea de texto.
Los parámetros que tendremos que pasarle al método serán aquellos que necesita "List.AddItem()":

- pText1 – la primera línea de texto, el nombre del amigo
- pText2 – la segunda línea de texto, la marca de localización del amigo
- pBitmap1 – la primera imagen, la foto del amigo
- pBitmap2 – la segunda imagen, no utilizada
- itemId – el identificador entero de este elemento de la lista

Este es el prototipo a añadir a "MainListForm.h":

// Implementation
protected:
    Osp::Ui::Controls::OptionMenu* pOptMenu; 

    static const int ID_OPTIONKEY = 100;
    static const int ID_OPTIONMENU_ITEM_1 = 101;
    static const int ID_OPTIONMENU_ITEM_2 = 102;
    static const int ID_OPTIONMENU_ITEM_3 = 103;
    static const int ID_OPTIONMENU_ITEM_4 = 104;
    static const int ID_OPTIONMENU_ITEM_5 = 105;
    static const int ID_OPTIONMENU_SUBITEM_1 = 201;
    static const int ID_OPTIONMENU_SUBITEM_2 = 202;
    static const int ID_LEFT_SOFTKEY = 900;

    //Test method, add an item to the list
    result TestAdditem( const Osp::Base::String * aBuddyName,
                 const Osp::Base::String * aLocationStamp,
                 const Osp::Graphics::Bitmap * aBuddyPic );

Y esta es la implementación inicial del método a añadir en "MainListForm.cpp":

/* Test method, add an item to the main buddy list */
result
MainListForm::TestAdditem(const Osp::Base::String * aBuddyName,
                          const Osp::Base::String * aLocationStamp,
                          const Osp::Graphics::Bitmap * aBuddyPic)
{
    AppLog("TestAdditem()\n");
    return E_SUCCESS;
}

Podemos asumir que, en la implementación final, la imagen (aBuddyPic) y el nombre del amigo  (aBuddyName) se cargarán de la aplicación "Contactos" del móvil.
De momento, tendremos que proporcionar la imagen nosotros. Crearemos una sola imagen de prueba para todos los elementos que creemos.
Para utilizar imágenes, en "manifest.xml" añade el siguiente privilegio:


NOTA: puedes modificar "manifest.xml" con el "Application Manager" que hay en la web developer.bada.com.

Para utilizar imágenes, en "MainListForm.h" añade lo siguiente:


Y en "MainListForm.cpp" añade lo siguiente:

using namespace Osp::Graphics;
using namespace Osp::Media;

¡ Esto ya compila ! 

Como creamos la imagen de prueba dentro del método de test, dejamos el parámetro "aBuddyPic" comentado (coméntalo también en "MainListForm.h"). Así es como debe quedar el método "TestAddItem()": 

/* Test method, add an item to the main buddy list */
result
MainListForm::TestAdditem(const Osp::Base::String *aBuddyName,
                          const Osp::Base::String *aLocationStamp)
                        //, const Osp::Graphics::Bitmap *aBuddyPic)
{
    // First, get a pointer to the main list, by name
    if (pMainlist)
        AppLog("Not null list");
    else
        AppLog("Null list!");

    // Create a dummy bitmap to put into buddy list entries
    result r = E_SUCCESS;
    Image* dummyimage = new Image;
    Bitmap* bitmap = null;
    r = dummyimage -> Construct();
    if (IsFailed(r))
    {
        // Handle error condition
    }

    bitmap = dummyimage->DecodeN(L"/Home/img.jpg", BITMAP_PIXEL_FORMAT_RGB565,    52, 52);

    if (null == bitmap)
    {
        // Handle error condition
    }

    //Delete the image instance
    delete dummyimage;

    AppLog("TestAdditem() %s %s", aBuddyName, aLocationStamp );

    // Okay, now make a list item
    // r = pMainlist->AddItem(*aBuddyName, *aLocationStamp, bitmap, bitmap, 500);
    r = pMainlist->AddItem(aBuddyName, aLocationStamp, bitmap, null, 500 );

    return r;
}


Por cierto, pon una imagen en "Home" llamada por ejemplo "img.jpg" (/Home/img.jpg).
Finalmente, invoca el método añadiendo las siguientes líneas en el método "OnInitializing()":

result
MainListForm::OnInitializing(void)
{
    result r = E_SUCCESS;

    pMainlist = static_cast(GetControl("IDC_MAINLIST"));
    if (pMainlist)
    {
        pMainlist->AddItemEventListener(*this);
        pMainlist->AddFocusEventListener(*this);
        pMainlist->AddKeyEventListener(*this);
        pMainlist->AddTouchEventListener(*this);
    }

    // Now initialise the list
    // Create the strings
    String dummyname = L"Elvis";
    String dummylocstamp = L"Ha salido del escenario";
    r = TestAdditem(&dummyname, &dummylocstamp);
    // Log it!
    AppLog("result = %s", GetErrorMessage(r));


    return r;
}


Por último añade la declaración de pMainList en "MainListForm.h": 

protected:
    Osp::Ui::Controls::List* pMainlist;
    Osp::Ui::Controls::OptionMenu* pOptMenu;
    static const int ID_OPTIONKEY = 100;

    ...

Compila y ejecuta el proyecto y verás algo como esto:


Aún no es la UI final, pero por lo menos ya podemos ver algún elemento del la lista.



Vía: Introduction to bada - A developer's guide

4 comentarios:

Unknown dijo...

hola, tango un problemilla y es que no me aparece el MainListForm::OnActionPerformed(). Me aparecen OnItemStateChanged(), onKeyPressed()... pero no ese.
Tengo el bada ide 1.2.1 y no se si es por eso, o que he hecho algo mal en los pasos anteriores.

Byron dijo...

Buenas Iñaki,

Bueno, supongo que te habrás saltado algún paso, pero no pasa nada. Puedes añadir el método "manualmente".
Si no sabes cómo hacerlo, sigue los pasos de los posts anteriores (http://viva-moore.blogspot.com/2010/11/desarrollo-de-aplicaciones-para-bada.html).

De todas maneras te puedes bajar mi proyecto e importarlo en el bada SDK si ves que no te sale.

¡Suerte!

Unknown dijo...

Hola! Lo primero muchas gracias por estos tutoriales que te curras, porque nos ayudan o al menos a mi me está ayudando mucho para empezar a cacharrear en bada xD.
Tengo una duda, ya que en la parte que comentas que hay que modificar el "manifest.xml" con el "Application Manager" de la web, no se cómo hacerlo. En la página no encuentro esa aplicación por ningún lado. Si pudieras echarme una mano te lo agradecería. Muchas gracias por todo!!!

Byron dijo...

Hola!
El "manifest.xml" es un fichero que contiene el perfil de la aplicación, y se obtiene en la web de bada Developers, haciendo clic en "My Applications > Applicaton Manager > Generate Application Profile" y rellenando los datos que piden. Cuando acabes verás un botón grande que pone "Download Manifest File". Te lo bajas, le cambias el nombre por "manifest.xml" y lo mueves a la raíz del proyecto para el que hayas generado ese perfil, reemplazando el "manifest.xml" anterior.

En el post siguiente lo explico en más detalle:
http://viva-moore.blogspot.com/2011/02/desarrollo-de-aplicaciones-para-bada_11.html

Espero que te ayude.

Publicar un comentario