Category: User Interface
December 30th, 2010
Últimamente hay mucho revuelo y movimiento relacionado a lo que es mobile, pero de todo ese ruido muy pocas cosas son realmente originales o proponen un cambio de paradigma, una de esas excepciones es la nueva UI de Windows Phone 7 y aunque posiblemente nunca compre, use o desarrolle para WP7 hay un par de conceptos interesantes que me gustaría tomar prestados, como por ejemplo los controles Panorama (ver imagen abajo) y Pivot para poder usarlos en mis aplicaciones QML.
Vamos a comenzar con el control Panorama, implementarlo no es para nada complicado, como verán hay algunas propiedades para configurar el look and feel, el contenido o el elemento que hará de contenedor de los PanoramaItems, también tenemos un control Flickable que utilizaremos para mover horizontalmente los PanoramaItems hijos y para finalizar tenemos un elemento Text que usaremos para mostrar el titulo del control Panorama y una Grid en donde se irán acomodando los PanoramaItems.
Panorama.qml
import Qt 4.7
Rectangle {
id: container
width:parent.width
height:parent.height
default property alias content: content.children
property string title: "panorama"
property int titleSize:48
property string titleFont: ""
property color titleColor:"#fff"
property color background:"#333"
property string backgroundImage:""
property string backgroundFillMode:"Tile"
color: container.background
Flickable {
width:parent.width
height:parent.height
contentWidth: content.width;
contentHeight: container.height
boundsBehavior: "StopAtBounds"
Image{
source: container.backgroundImage
fillMode: container.backgroundFillMode
Grid{
width: content.width
height: content.height
rows:2
Text {
id: title
text: container.title
font.pointSize: container.titleSize
font.family: container.titleFont
color: container.titleColor
}
Grid {
id: content
spacing:10
}
}
}
}
}
El control PanoramaItem es aun mas sencillo como se ve en el código a continuación.
PanoramaItem.qml
import Qt 4.7
Item {
id: container
default property alias content: content.children
property string title: "panoramaItem"
property int titleSize: 21
property color titleColor: "#fff"
height: wrapper.height
Grid{
id: wrapper
rows:2
Text {
id: title
width: container.width
text: container.title
smooth: true
wrapMode: "Wrap"
font.pointSize: container.titleSize
color: container.titleColor
}
Rectangle {
id: content
width: container.width
height: container.height - title.height
}
}
}
Bien, ahora que ya tenemos nuestros controles, es momento de usarlos, para ello solo debemos crear un control Panorama, establecer el titulo, el tipo y tamaño del font, una imagen de fondo si es que vamos a usar una, para luego agregar los PanoramaItems que deseemos con su respectivos contenidos.
PanoramaDemo.qml
import Qt 4.7
Rectangle {
id: screen
width: 360
height: 640
Panorama{
id:myPanorama
title: "my Panorama"
backgroundImage:"images/bg.jpg"
PanoramaItem{
width: screen.width -30
title:"first item"
titleSize: 21
// Content here
}
PanoramaItem{
width: screen.width
title:"second Item"
titleSize: 21
// Content here
}
PanoramaItem{
width: screen.width
title:"third Item"
titleSize: 21
// Content here
}
}
}
En la imagen a continuación pueden ver el resultado de el ejemplo y en el vídeo su funcionamiento.
Como verán duplicar el comportamiento básico de estos controles no fue para nada difícil, aunque esto es solo un simple ejemplo, con muchas cosas aun por pulir y funcionalidades por implementar para lograr una emulación completa de las funcionalidades de los controles originales, aun sirve para demostrar la capacidades de QML y como con poco código se pueden lograr resultados interesantes.
December 9th, 2010
Sencha Touch es un framework para el desarrollo de aplicaciones mobile centrado en WebKit, Sencha Touch hace uso extensivo de CSS3 para todo lo referente a las animaciones y los styles que por suerte son fácilmente personalizables y mas aun si usan Sass, ya que cambiando el valor de unas cuantas variables podemos modificar totalmente la apariencia de una aplicación, estas características y funcionalidades han sido tremendamente optimizadas para lograr que todo quepa en aproximadamente 80k.
Sencha Touch esta disponible tanto en un versión con licencia comercial como con una licencia Open Source, mas precisamente la GPL v3.
Componentes
Una de las principales ventajas de Sencha Touch es la cantidad de controles que incluye, todos ellos muy fáciles de usar y personalizar.
- Lists
- Carousel
- Picker
- Overlay
- Toolbar & Buttons
- Audio
- Video
- GeoLocation
Touch Events
Sencha Touch incluye los eventos que comúnmente mas se usan en dispositivos touch:
- Tap
- Double Tap
- Tap and Hold
- Swipe
- Rotate
- Drag and Drop
Layouts
Existe dos conceptos utilizados por Sencha Touch, uno es el de panel, el cual actúa de contenedor de componentes, el otro es el de layout el cual se aplica a un panel y especifica como deben mostrarse lo objetos que se encuentran dentro de el.
Los layouts que incluye Sencha Touch son:
Models, Stores & DataViews
Quizás las características mas poderosas de Sencha Touch son todas aquellas relacionadas con el manejo de datos, por un lado tenemos los Models que nos permiten representar un objeto, las validaciones que se deben aplicar cuando se usa en un formulario y las relaciones que este tiene con otros objetos.
Los Stores son una colección de registros los cuales utilizan un proxy para leer y escribir data, por ultimo los DataView nos permite poblar de data una template con los datos de un Store.
Manos a la obra
En el siguiente ejemplo pueden ver como utilizamos los diferentes elementos antes detallados para crear una simple liste anidada.
index.js
Ext.setup({
onReady: function() {
Ext.regModel('Player', {
fields: [
{name : "name"},
{name : "position"},
{name : "number"},
{name : "caps"},
{name : "age"},
{name : "picture"}
]
});
var itemTemplate = new Ext.XTemplate(
'<tpl for=".">',
'{number}',' {name}',
'</tpl>');
var detailTemplate = new Ext.XTemplate(
'<div class="detail">',
'<img src="{picture}"/><br/>',
'{number}', ' {name} <br/>',
'Position: {position} <br/>',
'Age: {age} ',
'Caps: {caps} ',
'</div>'
);
var playersStore = new Ext.data.Store({
model: "Player",
proxy: {
type: 'ajax',
url: 'http://localhost:3000/players.json',
reader: {
type: 'json',
record: 'player'
}
},
autoLoad:true
});
var navBar = new Ext.Toolbar({
itemId: 'navBar',
ui: 'light',
dock: 'top',
items: [
{
text: 'Back',
ui: 'back',
handler: function(){
panel.layout.prev({type: 'slide', direction: 'right'});
}
}
]
});
var detailPanel = new Ext.Panel({
tpl: detailTemplate,
dockedItems: [ navBar ]
});
var showDetail = function(record, btn, index) {
navBar.setTitle(record.get('name'));
detailPanel.update(record.data);
panel.setActiveItem(1);
}
var listPanel = {
dockedItems: [
{
title: 'Players',
xtype: 'toolbar',
ui: 'light',
dock: 'top'
}
],
layout: 'fit',
scroll: 'vertical',
items: [
{
xtype: 'list',
store: playersStore,
itemTpl:itemTemplate,
singleSelect: true,
onItemDisclosure: showDetail
}
]
};
panel = new Ext.Panel({
fullscreen: true,
layout: 'card',
cardSwitchAnimation: 'slide',
items: [ listPanel, detailPanel ]
});
}
});
Al correr el ejemplo, veremos algo como esto:
Sencha Touch, viene con un style por default, que es el que usamos en el ejemplo anterior, pero ademas viene con dos styles mas, uno que se ajusta mejor al look and feel de Android y otro para iOS, cambiar entro uno y otro es solo cuestión de reemplazar la stylesheet.
Only for WebKit
Sencha Touch tiene muchas cosas a favor, es fácil de usar, es flexible y cuenta con muchas características y funciones interesantes, pero solo funciona con WebKit, y es algo que para mi es molesto, porque muchas de estas funciones y características, podrían funcionar en Firefox, Fennec y en menor medida Opera si ademas de usar las extensiones de WebKit usaran las de los demás fabricantes, o podrían simularse usando Javascript, así que tengan en cuenta esta restricción ante de lanzarse de lleno a desarrollar con el. Quizás en un futuro esto cambie, pero por ahora, si usamos un browser que no se base en WebKit, solo obtendremos este resultado.
Recursos
November 22nd, 2010
Lo bueno de QML es que permite que casos de uso como una simple vista Maestro Detalle pueda ser mejorada agregando animaciones y layouts personalizados con muy poco esfuerzo.
El ejemplo a implementar es muy simple, vamos a usar un archivo XML como data source y para presentar los datos vamos a usar un GridView en donde seleccionaremos el item a mostrar en en el ListView que hace las veces de details view.
La acción central ocurren en el delegate usado para poblar el GridView el cual espera a que se haga click en alguno de los items, para pasar el index al ListView y disparar la animación que remueve el GridView de la escena para cambiar la opacidad del ListView de 0 a 1 para poder hacerlo visible, por ultimo hay en elemento Text que hará de botón para volver a la vista original.
import Qt 4.7
Rectangle {
width: 360
height: 640
id:container
XmlListModel {
id: oranjeModel
source: "http://localhost:3002/players.xml"
query: "/players/player"
XmlRole {
name: "name"
query: "name/string()"
}
XmlRole {
name: "number"
query: "number/string()"
}
XmlRole {
name: "position"
query: "position/string()"
}
XmlRole {
name: "picture"
query: "picture/string()"
}
XmlRole {
name: "bio"
query: "bio/string()"
}
}
Component {
id: oranjeDelegate
Item {
width: 80
height: 110
Image {
source: picture
width: parent.width
height: parent.height
fillMode: "Stretch"
opacity: 0.5
}
MouseArea {
anchors.fill: parent
focus: true
onClicked: {
oranjeDetails.currentIndex = index
container.state = "detailsView"
}
}
}
}
Component {
id: oranjeDetailsDelegate
Grid {
rows: 3
spacing: 6
Row {
Image {
source: picture
width: 160
height: 230
fillMode: "Stretch"
}
Column {
Text {
text: number
font.pointSize: 24
}
Text {
text: name
font.pointSize: 18
}
Text {
text: position
font.pointSize: 14
}
}
}
Row {
Text {
text: bio
font.pointSize: 8
width: container.width
wrapMode: "Wrap"
}
}
Row {
Text {
text: "Back"
font.pointSize: 12
color: "#ed1e24"
width: container.width
MouseArea {
anchors.fill: parent
onClicked: container.state = ""
}
}
}
}
}
GridView {
id: oranjeList
model: oranjeModel
delegate: oranjeDelegate
width: container.width
height: container.height
cellWidth:90
cellHeight: 120
}
ListView{
id: oranjeDetails
model: oranjeModel
delegate: oranjeDetailsDelegate
opacity: 0
}
states: [
State {
name: "detailsView"
PropertyChanges {
target: oranjeList
x: -container.width
}
}
]
transitions: [
Transition {
from: ""
to: "detailsView"
NumberAnimation {
properties: "x"
easing.type: "OutBounce"
}
PropertyAnimation {
target: oranjeDetails
property: "opacity"
to: 1
}
},
Transition {
from: "detailsView"
to: ""
NumberAnimation {
properties: "x"
easing.type: "OutSine"
duration: 250
}
PropertyAnimation {
target: oranjeDetails
property: "opacity"
to: 0
}
}
]
}
November 11th, 2010
Una de las características interesantes de QML es la posibilidad de usar Javascript para agregar funcionalidades extras de forma rápida y fácil, básicamente contamos con un entorno igual al que se encuentra en cualquier browser moderno con algunas pequeñas adiciones.
Vamos a comenzar con un ejemplo simple, la idea de este ejemplo es crear una app que muestre numero aleatorios cuando se presione la barra espaciadora o cuando se haga click sobre el elemento Text que mostrara los numeros.
Javascript Inline
Para comenzar vamos a mostrar como integrar el código Javascript de forma inline, esta practica se aconseja siempre y cuando el código a añadir no sea muy extenso, ya que sino la legibilidad y mantenibilidad del código se resiente.
Pasemos ahora al código, en un nuevo proyecto pegamos el siguiente código, como verán luego de declarar un Rectangle y dentro del mismo, agregue un función que genera números aleatorios en este caso hasta el numero 100, luego viene el elemento Text encargado de mostrar el numero aleatorio, el cual contiene un MouseArea que cuando se dispara el evento onClicked toma un numero generado por la función Javascript y se lo asigna al elemento Text, lo mismo pasa a continuación con la función Keys.onPressed seteada para dispararse cuando se presiona la barra espaciadora.
Numerator.qml
import Qt 4.7
Rectangle {
width: 640
height: 360
id:container
function rand (n){
return (Math.floor(Math.random() * n + 1));
}
Text{
id: start
text: "click!"
font.pointSize: 128
font.family: "Arial"
font.bold: true
color: "#cc0000"
focus: true
anchors.horizontalCenter: container.horizontalCenter
anchors.verticalCenter: container.verticalCenter
MouseArea {
id: mouseRegion
anchors.fill: start
onClicked: {
start.text = RandomFunctions.rand (100);
}
}
Keys.onPressed: {
if (event.key == Qt.Key_Space) {
start.text = rand(100);
}
}
}
}
Javascript Standalone
Cuando el código Javascript crece en tamaño o si se quiere mantener todo mas ordenado la opción es poner el código Javascript en uno o mas archivos separados, si vamos al caso de nuestro ejemplo podemos mover la función que genera los numero aleatorios a un archivo aparte, en este caso yo lo llame generator.qml, un tip a tener en cuenta es que por convención los nombres de los archivos .js deben comenzar con minúscula.
generator.js
function rand (n){
return (Math.floor(Math.random() * n + 1));
}
Para poder hacer uso de las funciones ubicadas en un archivo separado debemos hacer un import con la ruta del archivo .js y setear un “alias” el cual debe ser único para cada archivo que importemos, este “alias” lo usaremos para poder invocar las funciones que albergue el archvio .js, un ejemplo de ello pueden verlo en el MouseArea o en la Keys.onPressed del código a continuación.
Numerator.qml
import Qt 4.7
import "generator.js" as RandomFunctions
Rectangle {
width: 640
height: 360
id:container
Text{
id: start
text: "click!"
font.pointSize: 128
font.family: "Arial"
font.bold: true
color: "#cc0000"
focus: true
anchors.horizontalCenter: container.horizontalCenter
anchors.verticalCenter: container.verticalCenter
MouseArea {
id: mouseRegion
anchors.fill: start
onClicked: {
start.text = RandomFunctions.rand (100);
}
}
Keys.onPressed: {
if (event.key == Qt.Key_Space) {
start.text = RandomFunctions.ran (100);
}
}
}
}
Como verán el integrar Javascript con QML es muy fácil y nos permite potenciar las posibilidades de QML, en la documentación oficial pueden encontrar mas información, ejemplos y buenas practicas para aplicar a nuestros desarrollos.
November 6th, 2010
Para quienes hayan o estén trabajado con Flash, Flex, WPF o Silverlight el tema de los States es algo conocido y de uso diario, en QML un State nos permite cambiar propiedades de un elemento, disparar un script usando StateChangeScript, modificar el parent utilizando ParentChanges, cambiar la alineación usando AnchorChanges o sobrescribir un signal handler utilizando PropertyChanges, todo ello cuando ocurra un evento determinado ya sea por nosotros en código o por el usuario cuando interactúa con la aplicación.
Vamos a comenzar con un ejemplo simple, la idea es crear un botón, mas que nada porque es un elemento de uso común el cual cuenta con varios estados con los que podemos jugar y porque QML no trae un elemento Button definido así que si necesitamos hacer uso de uno vamos a tener que crearlo nosotros, esto puede parecer un poco molesto, pero por suerte es un proceso fácil y cuyo código puede ser reutilizado posteriormente.
Vamos a empezar creando un proyecto con Qt Creator, luego de crearlo vamos a añadir un archivo QML llamado Button.qml, una cosa a tener en cuenta es que el nombre que usemos para el archivo sera el que usaremos para invocar el elemento que creemos con el, en este caso Button.
El próximo paso sera pegar el siguiente código en el archivo Button.qml
Button.qml
import Qt 4.7
Item {
id: container
signal clicked
property string text
Rectangle {
id: button
color: "#00aeef"
border.width: 1
radius: 4
width: container.width
height: container.height
}
MouseArea {
id: mouseRegion
anchors.fill: button
onClicked: {
container.clicked();
}
}
Text {
color: "#fff"
anchors.centerIn: button
font.pixelSize: 12
text: container.text
}
states: [
State {
name: "Pressed"
when: mouseRegion.pressed == true
PropertyChanges {
target: button
color: "#333"
}
}
]
}
El código comienza con el import de rigor y sigue con un Item, el cual es el mas básico de los visual items en QML, continua con el signal que no es mas que un handler usado por el MouseArea y que detecta las acciones hechas por el mouse dentro de si, y por ultimo en esta primera parte del código tenemos la declaración de la propiedad text la cual usaremos para setear el texto a mostrar por el botón.
A continuación tenemos un elemento Rectangle que sera nuestro botón en si, sigue el MouseArea que esta configurado para que ocupe toda la superficie del Rectangle y que contiene el handler onClicked el cual se dispara cuando se hace click con el botón izquierdo del mouse, por ultimo tenemos un elemento Text que hace las veces de label del botón.
Y para finalizar llegamos a la sección de states donde tenemos un State cuyo nombre es Pressed y se dispara cuando el MouseArea ha recibido un click el cual a través de la PropertyChanges que apunta al botón cambien la propiedad color del mismo.
Bien, ya tenemos nuestro botón listo, utilizarlo es tan simple como invocar un elemento Button, en el código que sigue pueden ver un ejemplo de como hacerlo.
Main.qml
import Qt 4.7
Rectangle {
width: 200
height: 200
id: container
Button {
width: 100;
height: 25;
text: 'Hello World';
anchors.horizontalCenter: container.horizontalCenter;
anchors.verticalCenter: container.verticalCenter;
}
}
Animación
Bien como se ve en el ejemplo anterior el uso de states es bastante simple e intuitivo, con states se pueden hacer muchas mas cosas y pueden aprender mas de ellos viendo la documentación, pero para ir cerrando este post y como adelanto de los que vienen vamos a agregarle una pequeña animación al State utilizado por nuestro botón para hacer mas suave la transición entre el estado por default y Pressed, para ello solamente hay que agregar el siguiente código luego de la sección de states.
Button.qml
transitions: [
Transition {
PropertyAnimation { properties: "color"; duration: 300 }
}
]
La animación que usamos es bastante simple lo único que hacemos es crear una Transition y setear las propiedades que serán animadas por la PropertyAnimation y su duración.
El resultado de todo esto debería de verse de la siguiente manera:
Recuerden darse una vuelta por los ejemplos y por la documentación de Qt Quick, en la cual hay mas información acerca de states.