martes, 31 de mayo de 2011

Lo que probablemente no sabías de PHP

Utilice ip2long () y long2ip () para almacenar las direcciones IP como enteros en lugar de cadenas en una base de datos. Esto reducirá el espacio de almacenamiento en casi un factor de cuatro (15 bytes para char (15) frente a 4 bytes para el número entero), que sea más fácil para calcular si una determinada dirección cae dentro de un rango, y las búsquedas velocidad y tipo (a veces un poco).

Validar direcciones de correo electrónico mediante la comprobación de que el nombre de dominio existe con checkdnsrr () . Esta construido en función de los controles para garantizar que un nombre de dominio especificado resuelve a una dirección IP. Una función definida por el usuario simple que se basa en checkdnsrr () para direcciones de correo electrónico válida en parte se puede encontrar en los comentarios de los usuarios en la sección de documentación de PHP. Esto es útil para la captura de esas personas ocasionales que piensan que su dirección de correo electrónico es 'usuario@wwwphp.net' en lugar de 'usuario@php.net.

Aprende a amar el operador ternario .

Prevenir los mensajes de error potencialmente sensibles de ser mostrado a los usuarios con el error_reporting (0). Lo ideal sería que los informes de errores deben ser completamente desactivado en un servidor de producción desde el interior de php.ini . Sin embargo, si usted está en un servicio de hosting compartido y no le dan su propio php.ini , entonces su mejor apuesta es agregar error_reporting (0), como la primera línea en cada una de las secuencias de comandos (o utilice con require_once () .) Esto evitará que las consultas SQL potencialmente sensibles y los nombres de ruta que se muestren si las cosas salen mal.

Utilice gzcompress() y gzuncompress() para comprimir de forma transparente y descomprimir cadenas largas antes de guardarlos en una base de datos. Estas funciones integradas de utilizar el algoritmo de gzip y puede comprimir texto hasta un 90%. Puedo usar estas funciones casi cada vez que de lectura / escritura a un BLOB de campo dentro de PHP. La única excepción es cuando necesito todas las funciones de indexación de texto.

lunes, 30 de mayo de 2011

CSS3 efecto texto en 3D


h1 {
       text-shadow: 0 1px 0 #ccc,
       0 2px 0 #c9c9c9, 
       0 3px 0 #bbb,
       0 4px 0 #b9b9b9,
       0 5px 0 #aaa,
       0 6px 1px rgba(0,0,0,.1),
       0 0 5px rgba(0,0,0,.1),
       0 1px 3px rgba(0,0,0,.3),
       0 3px 5px rgba(0,0,0,.2),
       0 5px 10px rgba(0,0,0,.25),
       0 10px 10px rgba(0,0,0,.2),
       0 20px 20px rgba(0,0,0,.15);

domingo, 29 de mayo de 2011

Crear diapositivas de imágenes en HTML5 con SlideMyPics

SlideMyPics es un servicio que nos permite crear slideshows de imágenes en HTML5 de una manera muy sencilla que no nos tomará más de 3 minutos crear una diapositiva con nuestras fotos preferidas.

Su uso es muy simple, sólo tienen que elegir la red social de la cual quieren importar los álbumes y darles acceso con nuestros datos de login. Luego tienen que elegir los álbumes disponibles y seleccionar las fotos que van a agregar a la diapositiva.

Lo bueno es que tiene varias opciones de personalización del diseño, podemos elegir entre tres plantillas distintas, personalizar el fondo de la diapositiva, agregar música de fondo desde videos de YouTube, y obviamente la posibilidad de generar un código único para compartir en sitios web.
Visto en FeedMyApp

sábado, 28 de mayo de 2011

php detecta dispositivo/plataforma

Mobile_Detect es una sencilla clase de PHP que permite la detección de las plataformas para dispositivos móviles más conocidas: Android, Blackberry, Opera Mini, Palm, Windows Mobile, así como las genéricas.

Su uso es muy sencillo. Basta con incluir e instanciar la clase:
include("Mobile_Detect.php");
$detect = new Mobile_Detect();
y comprobar:
if ($detect->isAndroid()) {
    // código para una plataforma Google Android
}
Los métodos disponibles son isAndroid(), isBlackberry(), isOpera(), isPalm(), isWindows(), isGeneric(). Si sólo estamos interesados en saber si el usuario está usando un dispositivo móvil pero sin preocuparnos de qué plataforma:
if ($detect->isMobile()) {
    // cualquier plataforma móvil
}

viernes, 27 de mayo de 2011

Compartir código desde Codr no podría ser más fácil. La página es tan simple que prácticamente es como un editor normal de texto, a secas. Se trata únicamente de un sitio desde donde podremos mostrar a otros el código que vayamos escribiendo.


Codr genera un link único que utilizaremos para que, a quien se lo enviemos, pueda ver el código que deseamos compartir. Además, cabe señalar que todo esto es en vivo, cada que agreguemos una nueva línea o que modifiquemos algo, lo mismo sucederá en la página que visualizan nuestros contactos.

Y si querías más, para los más flojos, o para los que les gusta optimizar su tiempo, tiene la posibilidad de arrastrar un archivo que tengamos en nuestro equipo, y Codr automáticamente lo leerá y copiará su contenido por ti. Codr soporta el remarcado de los siguientes lenguajes: html, css, js, php, py, java, cpp.

Web: Codr

jueves, 26 de mayo de 2011

Generador CRUD online para PHP



PhpScaffold.com es una sencilla herramienta online que puede ser de interés para personas que desean gestionar las operaciones básicas CRUD tablas mediante una aplicación php sin importarle el aspecto gráfico. A partir de una consulta create table obtenida por ejemplo de una exportación mysql es capaz de crear los ficheros PHPs de listado, alta, baja y modificación de elementos de esa tabla.
Generador de código | phpscaffold.com

miércoles, 25 de mayo de 2011

herramientas para comprobar la propagación de los DNS

Cuando estamos empezando un nuevo proyecto que incluye un dominio recién registrado o cuando hemos cambiado de hosting y esperamos la propagación de los DNS es útil ver cómo se va llevando a cabo esta tarea.




Con estas dos herramientas pueden comprobar un dominio y ver cómo se están propagando los DNS en los distintos routers de internet.

martes, 24 de mayo de 2011

Alertas con jQuery



Con Clean jQuery AJAX Alert podremos lograrlo. Su implementación es muy sencilla, son pocas líneas de código las que utilizaremos, y como único parámetro le diremos la URL de la página a la que se redirigirá con una respuesta afirmativa del usuario, y claro, el ID del elemento con el que vayamos a interactuar, como se observa a continuación:
$(function() {
       $('#id_elemento').alertBox({
              href: 'pagina_de_resultado.html'
       });
});
Ver una Demo: Demo

Descargalo desde: thefinishedbox

lunes, 23 de mayo de 2011

GetMySite, para analizar el posicionamiento de tu sitio web

Como el título del post lo sugiere, GetMySite.info te permitirá conocer el lugar que tiene tu sitio web (O cualquier otro también), en los principales buscadores, como son Google, Bing y Yahoo. Además de que también nos brinda información sobre la posición en Alexa.

Y no sólo eso, también podremos encontrar el resumen de los países desde donde más visitan nuestro sitio, además de la posición que este tiene en algunos de ellos. etc, etc.


Visto en: wwwhatsnew

domingo, 22 de mayo de 2011

crea los estilos de tus capas

Layer Styles, herramienta online que te permite crear los estilos de tus layers.

sábado, 21 de mayo de 2011

Tamaño máximo de la contraseña Gmail

No te lo preguntes más, la contraseña mas larga 
que se permite utilizar en las cuentas Gmail 
es de 200 caracteres.

viernes, 20 de mayo de 2011

Cachear una página dinámica PHP

El sistema es bastante simple y en principio se podría aplicar a cualquier sitio. El código sería algo tal que este:

<?php
       $md5 = md5($_SERVER["REQUEST_URI"]); // convertimos la URL a único identificador
       $file = "cache/".$md5.".html"; // donde se guardará el fichero
       $hora = filemtime($file); // comprobamos la hora del fichero si ya existiera
       if(time() <= $hora+86400) { // asignamos un tiempo de cache de 86400 segundos
              include($file); // incluimos el contenido del fichero cacheado
              echo "<!-- ".date('YmdHis', $hora)." -->"; // (opcional) añadimos al pie de página la fecha-hora de la caché
              exit; // salimos
       }
       ob_start(); // abrimos la memoria
?>

AQUI VA LA WEB NORMAL

<?php
       $fp = fopen($file, 'w+'); // abrimos el fichero de caché
       fwrite($fp, ob_get_contents()); // guardamos el contenido de la página generada en el fichero
       fclose($fp); // cerramos el fichero
       ob_end_flush(); // devolvemos la página que se ha generado y cerramos la memoria
?>

Con este sistema podremos incrementar una página dinámica tranquilamente entre un 50% y un 1.000% la velocidad, dependiendo de la carga de base de datos o cálculo que tuviera anteriormente.

jueves, 19 de mayo de 2011

Filtrar elementos nulos en jQuery

Lo primero será tener un array con elementos nulos.
var a=[1,2,0,"a",null,NaN,undefined,7,"","palabra",{}];
Vemos que en el array están presentes elementos como null, NanN, undefined y el cero. Todos ellos elementos nulos.
Para filtrar los elementos volvemos a apoyarnos en la función .grep(), la cual recibía el array con los elementos y la función de filtro. Pero, ¿qué función jQuery nos filtra los elementos nulos de un array?
Pues en ese caso vamos a utilizar el constructor de Boolean. Y es que cuando intentamos instanciar el constructor de Boolean con un elemento nulo, dicho constructor nos devolverá false.
Boolean(NaN); //false
Boolean(undefined); //false
Boolean(null); //false
Boolean(1); //true
El código para filtrar elementos nulos de un array con jQuery utilizando .grep() nos quedará de la siguiente forma:
var b = a.filter(Boolean);
Ya solo nos queda volcar el contenido del array.
$("#resultado").text(b.join(", "));

miércoles, 18 de mayo de 2011

convertir una imagen a HTML/CSS

ImgToCSS es una aplicación curiosa, con un nombre un poquito mentiroso, que probablemente sea útil para aquellos que envían newsletters, permite convertir cualquier imagen con un tamaño que no supere los 250×250 pixeles a formato HTML/CSS.
El mecanismo para realizar este trabajo es muy simple e interesante, por un lado recibe la imagen que nosotros le enviamos en formato JPEG, GIF, BMP, TIFF e ICO. Luego la aplicación procesa esta imagen, determinando el color de cada pixel, el mismo se asigna como color de fondo de una tabla HTML con tantas celdas como pixeles contenga la imagen, obviamente cada celda va a tener 1 pixel x 1 pixel de tamaño. De esta forma vamos a obtener un clon prácticamente perfecto de nuestra imagen en formato HTML.

Por ejemplo yo convertí a este pequeño IronMan:



El resultado es muy pero muy bueno.

Cuando comento que el nombre de la aplicación es un poco mentirosa lo digo porque en si en ningún momento se encuentran implementadas las hojas de estilo, no obstante si deseamos hacerlo simplemente tendremos que tomarnos el trabajo chino de crear clases css por cada color y reemplazarlos en las celdas que lo utilicen.

Web: ImgToCSS

martes, 17 de mayo de 2011

OptimiConsejo para hoy

Está muy bien controlar la descarga asíncrona de nuestra aplicación web.
El uso excesivo de script para lograrlo no es una buena solución.
Piensa que no todos lo soportan.
Y piensa que en algún momento hay que cargar esa cantidad de código.

Buen día ;)

lunes, 16 de mayo de 2011

Mashup de logotipos


¿Qué pasaría si ponemos en una coctelera algunos de los logotipos más conocidos?
Seguramente… ¡algo así!



sábado, 14 de mayo de 2011

Reset CSS elige la adecuada

La utilidad de los reset, nos ayudarán a que independientemente del navegador que se utilice, el contenido se vea lo más parecido posible a lo que realmente planeamos, según como lo hayamos establecido en nuestro CSS. Y para facilitarnos eso de encontrar el reset adecuado, les de recomiendo cssresetr, un sitio desde donde podrán no sólo probar como se verían los distintos tipos de reset que ofrece en la comparativa, sino que si se van al link de la parte derecha que dice CSS: Reset Library, podrán escoger el que los haya convencido y les mostrará su código. El cual pueden copiar y utilizar sin preocupaciones en sus proyecto.
Web: http://cssresetr.com/

viernes, 13 de mayo de 2011

Animaciones Graciosas


Están muy bien... son muy cortas no duran más de medio minuto pero son muy divertidas, además todas tienen una moraleja al final.

Son hechas por Rex Martin A.K.A Ricky Martin, ver más | [rexmartin]



PANCAKES from Rex Martin on Vimeo.


LEMONS from Rex Martin on Vimeo.


CHAIN from Rex Martin on Vimeo.


HORSE from Rex Martin on Vimeo.


I.T. from Rex Martin on Vimeo.


LAPTOP from Rex Martin on Vimeo.

jueves, 12 de mayo de 2011

Grubeco te ayuda a recuperar el Grub

Menuda gracia supone el perder el Grub cuando has instalado otro sistema o se ha desconfigurado.
Pues de la mano de machgeek tenemos la posibilidad de recuperarlo sin problema.

Comenzamos iniciando nuestro PC desde un Live CD de Ubuntu. Una vez hecho esto, descargamos el scripty le damos permisos de ejecución:
$ sudo chmod +x grubreco
Y tras esto ejecutarlo:
$ ./grubreco
Si todo sale bien te saldrá esto:
Operacion completada, al reiniciar no vera el grub, pero si iniciara Ubuntu, luego de esto ejecute en un terminal sudo update-grub y listo!
Como verán nos da unas últimas instrucciones, así que para terminar el proceso, reiniciamos y ejecutamos:
$ sudo update-grub
Volvemos a reiniciar para aplicar los cambios y listo :)

miércoles, 11 de mayo de 2011

Consejos al diseñar para móviles

La navegación en móviles tienen sus propios principios. Los de Hongkiat muestran 10 consejos, para una mejor usabilidad.

Decidir sobre la resolución de la pantalla:

Dividir Páginas Web en pequeñas porciones:

Simplificar el diseño:

Opción Para ver la página completa:

Colocación de navegación. Puede ser en cualquier posición, solo que debe ser intuitiva:

Utilizar enlaces de texto:

Hacer distinción en el enlace seleccionado:


Planificar los hipervínculos en cuanto a su profundidad en el sitio:

Reducir al usuario la necesidad de escribir texto, mejor utilizar métodos de navegación como menús desplegables en las diferentes opciones:

No usar “pop-ups” (ventanas que se abren, aparte de la principal) o refrescamiento automático.


martes, 10 de mayo de 2011

ALERTA - Comic Sans No se usa!!

Why You Hate Comic Sans es una racionalización del odio existente hacia la tipografía Comics Sans, en forma de charla en vídeo y algunos apuntes acerca de las razones puramente técnicas tras el repelús que produce la abominable –aunque ubicua– tipografía.

(…) Llegó un punto a finales de los 90 en el que gracias a los Macintosh todo el mundo podía usar e imprimir con casi cualquier tipografía. Hacia finales de los 90 los tipógrafos nos dimos cuenta de que las palabras eran incapaces de describir la desgarradora experiencia de ver cómo se había abierto la caja de Pandora para revelar que el 95 por ciento de todo lo que se veía diseñado por el campus la universidad era feo: tipografías elegidas sin criterio, interletraje pobre, elección de colores descarriada… todo estaba terriblemente mal: letreros para guiar a la gente por el campus, carteles de fiestas toga y, sí: pósteres de gatitos perdidos (…) Pero todos quedamos cegados ante la increíble revolución que se estaba produciendo: nuestras abuelas podían componer e imprimir todos los pósteres de gatitos perdidos que quisieran (…)
visto en: microsiervos

domingo, 8 de mayo de 2011

Filtrar un array con jQuery

jQuery tiene la posibilidad de filtrar elementos de un array. Para ello contamos con la función .grep(). Comenzamos con un sencillo ejemplo:

1-Creamos un array con números del 1 al 10.
var a=[1,2,3,4,5,6,7,8,9,10];
Crearemos una función que devuelva true cuando cumpla la condición que queramos aplicar y false cuando no la cumpla. Siguiendo el ejemplo buscaremos los elementos mayores que 5:
function mayorQueCinco(elemento,indice){return (elemento <=5);}
A la función le pasamos dos parametros "elemento" que será el elemento del array evaluado y el "indice" indice del array que ocupa el elemento. Ahora aplicamos el método .grep(). Este método nos devuelve otro array con los elementos que cumplen el filtro. En el siguiente ejemplo reutilizaremos la variable. En cierto modo estaría mas optimizado.
var a=[1,2,3,4,5,6,7,8,9,10];
a= jQuery.grep(a,mayorQueCinco);
Echo esto solo quedaría visualizar la información del nuevo array:
$("#resultado").text(a.join(", "));

sábado, 7 de mayo de 2011

Copiar Base de datos de MySQL a MySQL

Si quereis copiar una base de datos de un servidor MySQL a otro servidor MySQL podeis hacerlo ejecutando la siguiente linea:
$ mysqldbcopy --source=login:pass@localhost:3306 --destination=login:pass@localhost:3306 dborigen:dbdestino
mysqldbcopy pertenece a las utilidades de la linea de comandos de MySQL Workbench 

viernes, 6 de mayo de 2011

ahorra tiempo de transferencia con el código 204 No Content

Cuando se hace una petición y se devuelve un 204, se acaba todo, porque básicamente cuando la petición vuelve no se manda “ningún contenido”. ¿Y para qué utilizar esto? Hay un caso en el que se ve todo muy claro.

Muchas veces los contadores de visitas o similares integran en el sitio un pequeño GIF de 1×1, habitualmente, al que se le mandan algunos parámetros. Esto básicamente se usa para recoger estadísticas. La imagen no es que sea una imagen, sino que suele ser algo “programado” que acaba devolviendo una imagen muy pequeña, de pocos bytes… pero ¿qué necesidad hay de devolver información si la imagen simplemente no se muestra?


Esto en vez de devolver el “nada.gif” ¿por qué simplemente no devuelve nada y te ahorras el tiempo que se tarda en descargar información para otras cosas?

Se podría hacer algo como:


Y que este PHP tenga algo similar a:

    <?php
        /*
        [...]
        fwrite();
        [...]
        */
        header("HTTP/1.0 204 No Content");
    ?>

De esta forma, se conseguiría el mismo efecto y se ahorrarían unas décimas de segundo en tiempo de transferencia, a parte de la transferencia en sí.

jueves, 5 de mayo de 2011

(8 métodos de jQuery que deberías conocer) + 4 que quizá ya conocías

jQuery la librería de javascript mas usada. Tiene una multitud de métodos, efectos, animaciones... Me he topado con este post 8-jquery-methods-you-need-to-know en el que explica de forma breve 8 métodos interesantes que se debería conocer si vas a usar esta librería. Yo he incluido algunos mas que me parecen interesantes.

.data() info
Este no lo conocía y me ha parecido muy interesante. Os mostrare un ejemplo:

<div data-type="page" data-hidden="true" data-options='{"name":"John"}'></div>
$("div").data("role") === "page";
$("div").data("hidden") === true;
$("div").data("options").name === "John";
Otra forma de hacer lo mismo
$("div").data("role", "page");
$("div").data("hidden", "true");
$("div").data("role", {name: "John"});
De está forma nos permite almacenar información sobre los elementos de la página.

.stop() info
Con esto podemos llamar a un elemento que se está ejecutando una animación y pararlo. Pudiendo concatenar otra acción cuando este termine, como por ejemplo .fadeOut() vemos un ejemplo de ello:
$(this).find('img').stop(true, true).fadeOut();

.toggleClass() info
Añade o elimina la clase/s en el elemento con el que interactua (es pulsado). Un ejemplo:
$('#opcion').toggleClass('active');
podemos hacer lo mismo realizando este proceso:
if ($('#home').hasClass('active')) {
    $('#home').removeClass('active');
}
else {
    $('#home').addClass('active');
}

.delay(duración, [nombreCola]) info
Sirve para crear un intervalo de espera entre la ejecución de funciones.
$('#content').slideUp(300).delay(800).fadeIn(400);

.each() info
Recorre un objeto ejecutando la función en todos los elementos coincidentes.

Lista: <span>(pulsa para cambiar)</span>
    <ul>
        <li>Platanos</li>
        <li>Manzanas</li>

        <li>Naranjas</li>
    </ul>
    <script>
        $("span").click(function () {
            $("li").each(function(){
                $(this).toggleClass("example");
            });
        });
    </script>

.size() info
devuelve el numero de elementos del objeto.
var size1 = $("li").size();
var size2 = $("li").length;
.length hace el mismo efecto.

.closest() info
Obtiene un grupo de elementos que contenga el elemento padre más cercano que cumpla con el selector especificado, incluyendo al elemento inicial. Comienza comprobando primero si el elemento actual cumple con la selección especificada. Si es así devuelve ese elemento. En caso contrario continúa buscando en el elemento, padre a padre, hasta que encuentra un elemento que cumpla la condición dada. Si no lo encuentra, no devuelve nada.

$('p').find('span')

.position() info
Como su nombre indica obtiene las coordenadas del elemento.

    var p = $("p:first");
    var position = p.position();
    $("p:last").text( "left: " + position.left + ", top: " + position.top );

.noConflict() info
Con esto podemos editar la referencia del " $ "
var j = jQuery.noConflict();
// Ahora, en vez de $, usamos j.
j('#someDiv').hide();
// La siguiente linea referenciará a otra librería con la función $
$('someDiv').style.display = 'none';
.ready() info
Ejecuta la función cuando está listo el elemento indicado.
$(document).ready(function() {
    // ejecuta la función cuando el documento está cargado
});
otra versión mas optimizada.
$(function() {
    // ejecuta la función cuando el documento está cargado
});

.load() info
El evento load se lanza tan pronto el elemento al que hemos asociado el evento ha terminado de cargarse por completo. Este evento puede asociarse a cualquier tipo de elemento que tenga una URL, imágenes, iframes, hojas de estilo o javascripts...
Hay que tener especial cuidado con las imágenes, porque si la imagen la carga desde la caché en ocasiones 
no lanza el evento de cargado. Un ejemplo de este evento podría ser;
<img alt="Book" id="book" src="book.png" />
$('#book').load(function() {
    // Hace algo cuando termina de cargar la imagen por completo
});

.one() info

Es igual que bind, pero en este caso el evento sólo se ejecuta una vez. Por ejemplo, puede interesarnos que cuando pinchemos un botón se muestre un mensaje, pero que la segunda vez que se pinche ya no se muestre, en ese caso podríamos hacer,
$('#boton').one('click', function() {
    alert('Este mensaje se muestra sólo una vez');
});

Principiantes con el teclado

Explicación básica sobre los atajos del teclado.
Fuente: GeekSareSexy

miércoles, 4 de mayo de 2011

Mi Cuaderno Android - controles personalizados

Si queremos dar un toque especial y original a nuestra aplicación, o simplemente si necesitamos cierta funcionalidad no presente en los componentes estándar de Android, nos vemos en la necesidad de crear nuestros propios controles personalizados, diseñados a medida de nuestros requisitos.

Android admite por supuesto crear controles personalizados, y permite hacerlo de diferentes formas:
  1. Extendiendo la funcionalidad de un control ya existente. 
  2. Combinando varios controles para formar un otro más complejo. 
  3. Diseñando desde cero un nuevo control. 
Vamos a ver cómo podemos crear un nuevo control partiendo de la base de un control ya existente. A modo de ejemplo, vamos a extender el control EditText (cuadro de texto) para que muestre en todo momento el número de caracteres que contiene a medida que se escribe en él. Intentaríamos emular algo así como el editor de mensajes SMS del propio sistema operativo, que nos avisa del número de carateres que contiene el mensaje. En nuestro caso, como resultado obtendremos un control como el que se muestra en la siguiente imagen:
Controles Personalizados Android: EditText Extendido
Como vemos, en la esquina superior derecha del cuadro de texto vamos a mostrar el número de caracteres del mensaje de texto introducido, que ira actualizándose a medida que modificamos el texto.

Para empezar, vamos a crear una nueva clase java que extienda del control que queremos utilizar como base, en este caso EditText.

public class ExtendedEditText extends EditText
{
//...
}

Tras esto, sobreescribiremos siempre los tres constructores heredados, donde por el momento nos limitaremos a llamar al mismo constructor de la clase padre.

public ExtendedEditText(Context context, AttributeSet attrs, int defStyle){
    super(context, attrs,defStyle);
}

public ExtendedEditText(Context context, AttributeSet attrs) {
    super(context, attrs);
}

public ExtendedEditText(Context context) {
    super(context);
}

Por último el paso más importante. Dado que queremos modificar el aspecto del control para añadir el contador de caracteres tendremos que sobrescribir el evento onDraw(), que es llamado por Android cada vez que hay que redibujar el control en pantalla. Este método recibe como parámetro un objeto Canvas, que no es más que el “lienzo” sobre el que podemos dibujar todos los elementos extra necesarios en el control. El objeto Canvas, proporciona una serie de métodos para dibujar cualquier tipo de elemento (lineas, rectángulos, elipses, texto, bitmaps, …) sobre el espacio ocupado por el control. En nuestro caso tan sólo vamos a necesitar dibujar sobre el control un rectángulo que sirva de fondo para el contador y el texto del contador con el número de caracteres actual del cuadro de texto. No vamos a entrar en muchos detalles sobre la forma de dibujar gráficos ya que ése será tema de otro artículo, pero vamos a ver al menos las acciones principales.
En primer lugar definiremos los “pinceles” (objetos Paint) que utilizaremos para dibujar, uno de ellos (p1) de color negro y relleno sólido para el fondo del contador, y otro (p2) de color blanco para el texto.

Paint p1 = new Paint(Paint.ANTI_ALIAS_FLAG);
p1.setColor(Color.BLACK);
p1.setStyle(Style.FILL);
Paint p2 = new Paint(Paint.ANTI_ALIAS_FLAG);
p2.setColor(Color.WHITE);

Dado que estos elementos tan sólo hará falta crearlos la primera vez que se dibuje el control, para evitar trabajo innecesario no incluiremos su definición en el método onDraw(), sino que los definiremos como atributos de la clase y los inicializaremos en el constructor del control (en los tres).

Una vez definidos los diferentes pinceles necesarios, dibujaremos el fondo y el texto del contador mediante los métodos drawRect() y drawText(), respectivamente, del objeto canvas recibido en el evento.

@Override
public void onDraw(Canvas canvas)
{
    //Llamamos al método de la clase base (EditText)
    super.onDraw(canvas);
    //Dibujamos el fondo negro del contador
    canvas.drawRect(this.getWidth()-30, 5,
    this.getWidth()-5, 20, p1);

    //Dibujamos el número de caracteres sobre el contador
    canvas.drawText("" + this.getText().toString().length(),
    this.getWidth()-28, 17, p2);
}
Como puede comprobarse, a estos métodos se les pasará como parámetro las coordenadas del elemento a dibujar relativas al espacio ocupado por el control y el pincel a utilizar en cada caso.

Hecho esto, ya tenemos finalizado nuestro cuadro de texto personalizado con contador de caracteres. Para añadirlo a la interfaz de nuestra aplicación lo incluiremos en el layout XML de la ventana tal como haríamos con cualquier otro control, teniendo en cuenta que deberemos hacer referencia a él con el nombre completo de la nueva clase creada (incluido el paquete java), que en mi caso particular sería net.sgoliver.ExtendedEditText.
<net.sgoliver.ExtendedEditText
android:id="@+id/TxtPrueba"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />

creación de controles compuestos
Es decir, controles personalizados construidos a partir de varios controles estándar, combinando la funcionalidad de todos ellos en un sólo control reutilizable en otras aplicaciones.

Como ejemplo ilustrativo vamos a crear un control de identificación (login) formado por varios controles estandar de Android. La idea es conseguir un control como el que se muestra la siguiente imagen esquemática:
Esquema control login android
A efectos didácticos, y para no complicar más el ejemplo, vamos a añadir también a la derecha del botón Login una etiqueta donde mostrar el resultado de la identificación del usuario (login correcto o incorrecto).

A este control añadiremos además eventos personalizados, veremos como añadirlo a nuestras aplicaciones, y haremos que se pueda personalizar su aspecto desde el layout XML de nuestra interfaz utilizando también atributos XML personalizados.

Empecemos por el principio. Lo primero que vamos a hacer es construir la interfaz de nuestro control a partir de controles sencillos: etiquetas, cuadros de texto y botones. Para ello vamos a crear un nuevo layout XML en la carpeta \res\layout con el nombre “control_login.xml“. En este fichero vamos a definir la estructura del control como ya hemos visto en muchos artículos anteriores, sin ninguna particularidad destacable. Para este caso quedaría como sigue:

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dip">

<TextView android:id="@+id/TextView01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Usuario:"
android:textStyle="bold" />

<EditText android:id="@+id/TxtUsuario"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />

<TextView android:id="@+id/TextView02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Contraseña:"
android:textStyle="bold" />

<EditText android:id="@+id/TxtPassword"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />

<LinearLayout android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/BtnAceptar"
android:text="Login"
android:paddingLeft="20dip"
android:paddingRight="20dip" />

<TextView android:id="@+id/LblMensaje"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dip"
android:textStyle="bold" />

</LinearLayout>

</LinearLayout>

Visualmente, nuestro control ya quedaría como se observa en la siguiente imagen:
Control de Login - Diseño principal

A continuación crearemos su clase java asociada donde definiremos toda la funcionalidad de nuestro control. Dado que nos hemos basado en un LinearLayout para construir el control, esta nueva clase deberá heredar también de la clase java LinearLayout de Android. Redefiniremos además los dos constructores básicos:
public class ControlLogin extends LinearLayout
{
    public ControlLogin(Context context) {
        super(context);
        inicializar();
        }
    public ControlLogin(Context context, AttributeSet attrs) {
        super(context, attrs);
        inicializar();
    }
}

Como se puede observar, todo el trabajo lo dejamos para el método inicializar(). En este método inflaremos el layout XML que hemos definido, obtendremos las referencias a todos los controles y asignaremos los eventos necesarios. Todo esto ya lo hemos hecho en otras ocasiones, por lo que tampoco nos vamos a detener mucho. Veamos como queda el método completo:

private void inicializar()
{
    //Utilizamos el layout 'control_login' como interfaz del control
    String infService = Context.LAYOUT_INFLATER_SERVICE;
    LayoutInflater li = (LayoutInflater)getContext().getSystemService(infService);
    li.inflate(R.layout.control_login, this, true);
    //Obtenemoslas referencias a los distintos control
    txtUsuario = (EditText)findViewById(R.id.TxtUsuario);
    txtPassword = (EditText)findViewById(R.id.TxtPassword);
    btnLogin = (Button)findViewById(R.id.BtnAceptar);
    lblMensaje = (TextView)findViewById(R.id.LblMensaje);

    //Asociamos los eventos necesarios
    asignarEventos();
}

Dejaremos por ahora a un lado el método asignarEventos(), volveremos sobre él más tarde.

Con esto ya tenemos definida la interfaz y la funcionalidad básica del nuevo control por lo que ya podemos utilizarlo desde otra actividad como si se tratase de cualquier otro control predefinido. Para ello haremos referencia a él utilizando la ruta completa del paquete java utilizado, en nuestro caso quedaría como net.sgoliver.ControlLogin. Vamos a insertar nuestro nuevo control en la actividad principal de la aplicación:

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dip" >

<net.sgoliver.ControlLogin android:id="@+id/CtlLogin"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#0000AA" />
</LinearLayout>

Dado que estamos heredando de un LinearLayout podemos utilizar en principio cualquier atributo permitido para dicho tipo de controles, en este caso hemos establecido por ejemplo los atributos layout_width, layout_height y background. Si ejecutamos ahora la aplicación veremos cómo ya hemos conseguido gran parte de nuestro objetivo:
Control de Login - Aplicacion principal 1

Vamos a añadir ahora algo más de funcionalidad. En primer lugar, podemos añadir algún método público exclusivo de nuestro control. Como ejemplo podemos añadir un método que permita modificar el texto de la etiqueta de resultado del login. Esto no tiene ninguna dificultad:

public void setMensaje(String msg)
{
    lblMensaje.setText(msg);
}

En segundo lugar, todo control que se precie debe tener algunos eventos que nos permitan responder a las acciones del usuario de la aplicación. Así por ejemplo, los botones tienen un evento OnClick, las listas un evento OnItemSelected, etc. Pues bien, nosotros vamos a dotar a nuestro control de un evento personalizado, llamado OnLogin, que se lance cuando el usuario introduce sus credenciales de identificación y pulsa el botón “Login”.
Para ello, lo primero que vamos a hacer es concretar los detalles de dicho evento, creando una interfaz java para definir su listener. Esta interfaz tan sólo tendrá un método llamado onLogin() que devolverá los dos datos introducidos por el usuario (usuario y contraseña). Vemos cómo queda:
package net.sgoliver;
public interface OnLoginListener
{
    void onLogin(String usuario, String password);
}

A continuación, deberemos añadir un nuevo miembro de tipo OnLoginListener a la clase ControlLogin, y un método público que permita suscribirse al nuevo evento.
public class ControlLogin extends LinearLayout
{
    private OnLoginListener listener;
    //...
    public void setOnLoginListener(OnLoginListener l)
    {
        listener = l;
    }
}

Con esto, la aplicación principal ya puede suscribirse al evento OnLogin y ejecutar su propio código cuando éste se genere. ¿Pero cuándo se genera exactamente? Dijimos antes que queremos lanzar el evento OnLogin cuando el usuario pulse el botón “Login” de nuestro control. Pues bien, para hacerlo, volvamos al método asignarEventos() que antes dejamos aparcado. En este método vamos a implementar el evento OnClick del botón de Login para lanzar el nuevo evento OnLogin del control. ¿Confundido?. Intento explicarlo de otra forma. Vamos a aprovechar el evento OnClick() del botón Login (que es un evento interno a nuestro control, no se verá desde fuera) para lanzar hacia el exterior el evento OnLogin() (que será el que debe capturar y tratar la aplicación que haga uso del control).
Control de Login - Eventos
Para ello, implementaremos el evento OnClick como ya hemos hecho en otras ocasiones y como acciones generaremos el evento OnLogin de nuestro listener pasándole los datos que ha introducido el usuario en los cuadros de texto “Usuario” y “Contraseña”:

private void asignarEventos()
{
    btnLogin.setOnClickListener(new OnClickListener()
    {
        @Override
        public void onClick(View v) {
            listener.onLogin(txtUsuario.getText().toString(),
            txtPassword.getText().toString());
        }
    });
}

Con todo esto, la aplicación principal ya puede implementar el evento OnLogin de nuestro control, haciendo por ejemplo la validación de las credenciales del usuario y modificando convenientemente el texto de la etiqueta de resultado:

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    ctlLogin = (ControlLogin)findViewById(R.id.CtlLogin);
    ctlLogin.setOnLoginListener(new OnLoginListener()
    {
        @Override
        public void onLogin(String usuario, String password)
        {
            //Validamos el usuario y la contraseña
            if (usuario.equals("demo") && password.equals("demo"))
                ctlLogin.setMensaje("Login correcto!");
            else
                ctlLogin.setMensaje("Vuelva a intentarlo.");
        }
    });
}

Veamos lo que ocurre al ejecutar ahora la aplicación principal e introducir las credenciales correctas:
Control de Login - Aplicación principal 2
Nuestro control está ya completo, en aspecto y funcionalidad. Hemos personalizado su interfaz y hemos añadido métodos y eventos propios. ¿Podemos hacer algo más? Pues sí.

Cuando vimos cómo añadir el control de login al layout de la aplicación principal dijimos que podíamos utilizar cualquier atributo xml permitido para el contenedor LinearLayout, ya que nuestro control derivaba de éste. Pero vamos a ir más allá y vamos a definir también atributos xml exclusivos para nuestro control. Como ejemplo, vamos a definir un atributo llamado login_text que permita establecer el texto del botón de Login desde el propio layout xml, es decir, en tiempo de diseño.

Primero vamos de declarar el nuevo atributo y lo vamos a asociar al control ControlLogin. Esto debe hacerse en el fichero \res\values\attrs.xml. Para ello, añadiremos una nueva sección <declare-styleable> asociada a ControlLogin dentro del elemento <resources>, donde indicaremos el nombre (name) y el tipo (format) del nuevo atributo.

<resources>
    <declare-styleable name="ControlLogin">
    <attr name="login_text" format="string"/>
    </declare-styleable>
</resources>

En nuestro caso, el tipo del atributo será string, dado que contendrá una cadena de texto con el mensaje a mostrar en el botón.

Con esto ya tendremos permitido el uso del nuevo atributo dentro del layout de la aplicación principal. Ahora nos falta procesar el atributo desde nuestro control personalizado. Este tratamiento lo podemos hacer en el constructor de la clase ControlLogin. Para ello, obtendremos la lista de atributos asociados a ControlLogin mediante el método obtainStyledAttributes() del contexto de la aplicación, obtendremos el valor del nuevo atributo definido (mediante su ID, que estará formado por la concatenación del nombre del control y el nombre del atributo, en nuestro caso “ControlLogin_login_text“) y modificaremos el texto por defecto del control con el nuevo texto.

public ControlLogin(Context context, AttributeSet attrs) {
    super(context, attrs);
    inicializar();

// Procesamos los atributos XML personalizados
    TypedArray a =getContext().obtainStyledAttributes(attrs,
    R.styleable.ControlLogin);
    String textoBoton = a.getString(R.styleable.ControlLogin_login_text);
    btnLogin.setText(textoBoton);
    a.recycle();
}

Ya sólo nos queda utilizarlo. Para ello debemos primero declarar un nuevo espacio de nombres (namespace) local para el paquete java utilizado, que en nuestro caso he llamado “sgo”:

xmlns:sgo="http://schemas.android.com/apk/res/net.sgoliver"

Tras esto, sólo queda asignar el valor del nuevo atributo precedido del nuevo namespace, por ejemplo con el texto “Entrar”:

<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sgo="http://schemas.android.com/apk/res/net.sgoliver"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dip" >

<net.sgoliver.ControlLogin android:id="@+id/CtlLogin"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#0000AA"
sgo:login_text="Entrar" />
</LinearLayout>

Finalmente, si ejecutamos de nuevo la aplicación principal veremos cómo el botón de login se inicializa con el texto definido en el atributo login_text y que todo continúa funcionando correctamente.
Control de Login - Aplicación principal 3
Crear un control desde cero
Sin utilizar como base otros controles existentes. Como ejemplo, vamos a construir un control que nos permita seleccionar un color entre varios disponibles.

Los colores disponibles van a ser sólo cuatro, que se mostrarán en la franja superior del control. En la parte inferior se mostrará el color seleccionado en cada momento, o permanecerá negro si aún no se ha seleccionado ningún color. Valga la siguiente imagen como muestra del aspecto que tendrá nuestro control de selección de color:
control-seleccion-color


Por supuesto este control no tiene mucha utilidad práctica dada su sencillez, pero creo que puede servir como ejemplo para comentar los pasos necesarios para construir cualquier otro control más complejo. Empecemos.

En las anteriores ocasiones vimos cómo el nuevo control creado siempre heredaba de algún otro control o contenedor ya existente. En este caso sin embargo, vamos a heredar nuestro control directamente de la clase View (clase padre de la gran mayoría de elementos visuales de Android). Esto implica, entre otras cosas, que por defecto nuestro control no va a tener ningún tipo de interfaz gráfica, por lo que todo el trabajo de “dibujar” la interfaz lo vamos a tener que hacer nosotros. Además, como paso previo a la representación gráfica de la interfaz, también vamos a tener que determinar las dimensiones que nuestro control tendrá dentro de su elemento contenedor. Como veremos ahora, ambas cosas se llevarán a cabo redefiniendo dos eventos de la clase View, onDraw() para el dibujo de la interfaz, y onMeasure() para el cálculo de las dimensiones.

Por llevar un orden cronológico, empecemos comentando el evento onMeasure(). Este evento se ejecuta automáticamente cada vez que se necesita recalcular el tamaño de un control. Pero como ya hemos visto en varias ocasiones, los elementos gráficos incluidos en una aplicación Android se distribuyen por la pantalla de una forma u otra dependiendo del tipo de contenedor o layout utilizado. Por tanto, el tamaño de un control determinado en la pantalla no dependerá sólo de él, sino de ciertas restricciones impuestas por su elemento contenedor o elemento padre. Para resolver esto, en el evento onMeasure() recibiremos como parámetros las restricciones del elemento padre en cuanto a ancho y alto del control, con lo que podremos tenerlas en cuenta a la hora de determinar el ancho y alto de nuestro control personalizado. Estas restricciones se reciben en forma de objetos MeasureSpec, que contiene dos campos: modo y tamaño. El significado del segundo de ellos es obvio, el primero por su parte sirve para matizar el significado del segundo. Me explico. Este campo modo puede contener tres valores posibles:
  • AT_MOST: indica que el control podrá tener como máximo el tamaño especificado. 
  • EXACTLY: indica que al control se le dará exactamente el tamaño especificado. 
  • UNSPECIFIED: indica que el control padre no impone ninguna restricción sobre el tamaño. 
Dependiendo de esta pareja de datos, podremos calcular el tamaño deseado para nuestro control. Para nuestro control de ejemplo, apuraremos siempre el tamaño máximo disponible (o un tamaño por defecto de 200*100 en caso de no recibir ninguna restricción), por lo que en todos los casos elegiremos como tamaño de nuestro control el tamaño recibido como parámetro:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
    int ancho = calcularAncho(widthMeasureSpec);
    int alto = calcularAlto(heightMeasureSpec);
    setMeasuredDimension(ancho, alto);
}

private int calcularAlto(int limitesSpec)
{
    int res = 100; //Alto por defecto
    int modo = MeasureSpec.getMode(limitesSpec);
    int limite = MeasureSpec.getSize(limitesSpec);
    if (modo == MeasureSpec.AT_MOST) {
        res = limite;
    }
    else if (modo == MeasureSpec.EXACTLY) {
        res = limite;
    }
return res;
}

private int calcularAncho(int limitesSpec)
{
    int res = 200; //Ancho por defecto
    int modo = MeasureSpec.getMode(limitesSpec);
    int limite = MeasureSpec.getSize(limitesSpec);
    if (modo == MeasureSpec.AT_MOST) {
        res = limite;
    }
    else if (modo == MeasureSpec.EXACTLY) {
        res = limite;
    }
return res;
}

Como nota importante, al final del evento onMeasure() siempre debemos llamar al método setMeasuredDimension() pasando como parámetros el ancho y alto calculados para nuestro control.

Con esto ya hemos determinado las dimensiones del control, por lo que tan sólo nos queda dibujar su interfaz gráfica. Como hemos indicado antes, esta tarea se realiza dentro del evento onDraw(). Este evento recibe como parámetro un objeto de tipo Canvas, sobre el que podremos ejecutar todas las operaciones de dibujo de la interfaz. No voy a entrar en detalles de la clase Canvas, porque ése será tema central de un próximo artículo. Por ahora nos vamos a conformar sabiendo que es la clase que contiene la mayor parte de los métodos de dibujo en interfaces Android, por ejemplo drawRect() para dibujar rectángulos, drawCircle() para círculos, drawBitmap() para imagenes, drawText() para texto, e infinidad de posibilidades más. Para consultar todos los métodos disponibles puedes dirigirte a la documentación oficial de la clase Canvas de Android. Además de la clase Canvas, también me gustaría destacar la clase Paint, que permite definir el estilo de dibujo a utilizar en los metodos de dibujo de Canvas, por ejemplo el ancho de trazado de las líneas, los colores de relleno, etc.

Para nuestro ejemplo no necesitaríamos conocer nada más, ya que la interfaz del control es relativamente sencilla. Vemos primero el código y después comentamos los pasos realizados:
@Override
protected void onDraw(Canvas canvas)
{
    //Obtenemos las dimensiones del control
    int alto = getMeasuredHeight();
    int ancho = getMeasuredWidth();
    //Colores Disponibles
    Paint pRelleno = new Paint();
    pRelleno.setStyle(Style.FILL);
    pRelleno.setColor(Color.RED);
    canvas.drawRect(0, 0, ancho/4, alto/2, pRelleno);
    pRelleno.setColor(Color.GREEN);
    canvas.drawRect(ancho/4, 0, 2*(ancho/4), alto/2, pRelleno);
    pRelleno.setColor(Color.BLUE);
    canvas.drawRect(2*(ancho/4), 0, 3*(ancho/4), alto/2, pRelleno);
    pRelleno.setColor(Color.YELLOW);
    canvas.drawRect(3*(ancho/4), 0, 4*(ancho/4), alto/2, pRelleno);
    //Color Seleccionado
    pRelleno.setColor(colorSeleccionado);
    canvas.drawRect(0, alto/2, ancho, alto, pRelleno);
    //Marco del control
    Paint pBorde = new Paint();
    pBorde.setStyle(Style.STROKE);
    pBorde.setColor(Color.WHITE);
    canvas.drawRect(0, 0, ancho-1, alto/2, pBorde);
    canvas.drawRect(0, 0, ancho-1, alto-1, pBorde);
}

En primer lugar obtenemos las dimensiones calculadas en la última llamada a onMeasure() mediante los métodos getMeasuredHeight() y getMeasuredWidth(). Posteriormente definimos un objeto Paint que usaremos para dibujar los rellenos de cada color seleccionable. Para indicar que se trata del color de relleno a utilizar utilizaremos la llamada a setStyle(Style.FILL). Tras esto, ya sólo debemos dibujar cada uno de los cuadros en su posición correspondiente con drawRect(), estableciendo antes de cada uno de ellos el color deseado con setColor(). Por último, dibujamos el marco del control definiendo un nuevo objeto Paint, esta vez con estilo Style.STROKE dado que se utilizará para dibujar sólo líneas, no rellenos.

Con esto, ya deberíamos tener un control con el aspecto exacto que definimos en un principio. El siguiente paso será definir su funcionalidad implementando los eventos a los que queramos que responda nuestro control, tanto eventos internos como externos.

En nuestro caso sólo vamos a tener un evento de cada tipo. En primer lugar definiremos un evento interno (evento que sólo queremos capturar de forma interna al control, sin exponerlo al usuario) para responder a las pulsaciones del usuario sobre los colores de la zona superior, y que utilizaremos para actualizar el color de la zona inferior con el color seleccionado. Para ello implementaremos el evento onTouch(), lanzado cada vez que el usuario toca la pantalla sobre nuestro control. La lógica será sencilla, simplemente consultaremos las coordenadas donde ha pulsado el usuario (mediante los métodos getX() y getY()), y dependiendo del lugar pulsado determinaremos el color sobre el que se ha seleccionado y actualizaremos el valor del atributo colorSeleccionado. Finalmente, llamamos al método invalidate() para refrescar la interfaz del control, reflejando así el cambio en el color seleccionado, si se ha producido.

@Override
public boolean onTouchEvent(MotionEvent event)
{
    //Si se ha pulsado en la zona superior
    if (event.getY() > 0 && event.getY() < (getMeasuredHeight()/2))
    {
        //Si se ha pulsado dentro de los límites del control
        if (event.getX() > 0 && event.getX() < getMeasuredWidth())
        {
            //Determinamos el color seleccionado según el punto pulsado
            if(event.getX() > (getMeasuredWidth()/4)*3)
                colorSeleccionado = Color.YELLOW;
            else if(event.getX() > (getMeasuredWidth()/4)*2)
                colorSeleccionado = Color.BLUE;
            else if(event.getX() > (getMeasuredWidth()/4)*1)
                colorSeleccionado = Color.GREEN;
            else
                colorSeleccionado = Color.RED;
        //Refrescamos el control
        this.invalidate();
        }
    }
return super.onTouchEvent(event);
}

En segundo lugar crearemos un evento externo personalizado, que lanzaremos cuando el usuario pulse sobre la zona inferior del control, como una forma de aceptar definitivamente el color seleccionado. Llamaremos a este evento onSelectedColor(). Primero definiremos una interfaz para el listener de nuestro evento:

package net.sgoliver.android;
public interface OnColorSelectedListener
{
    void onColorSelected(int color);
}

Posteriormente, definiremos un objeto de este tipo como atributo de nuestro control y escribiremos un nuevo método que permita a las aplicaciones suscribirse al evento:

public class SelectorColor extends View
{
    private OnColorSelectedListener listener;
    //...
    public void setOnColorSelectedListener(OnColorSelectedListener l)
    {
        listener = l;
    }
}

Y ya sólo nos quedaría lanzar el evento en el momento preciso. Esto también lo haremos dentro del evento onTouch(), cuando detectemos que el usuario ha pulsado en la zona inferior de nuestro control:

@Override
public boolean onTouchEvent(MotionEvent event)
{
    //Si se ha pulsado en la zona superior
    if (event.getY() > 0 && event.getY() < (getMeasuredHeight()/2))
    {
        //...
    }
    //Si se ha pulsado en la zona inferior
    else if (event.getY() > (getMeasuredHeight()/2) && event.getY() < getMeasuredHeight())
    {
        //Lanzamos el evento externo de selección de color
        listener.onColorSelected(colorSeleccionado);
    }
return super.onTouchEvent(event);
}
Con esto, nuestra aplicación principal ya podría suscribirse a este nuevo evento para estar informada cada vez que se seleccione un color. Sirva la siguiente plantilla a modo de ejemplo:
public class ControlPersonalizado extends Activity
{
    private SelectorColor ctlColor;
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ctlColor = (SelectorColor)findViewById(R.id.scColor);
        ctlColor.setOnColorSelectedListener(new OnColorSelectedListener()
        {
        @Override
                public void onColorSelected(int color)
                {
                    //Aquí se trataría el color seleccionado (parámetro 'color'
                    //...
                }
        });
    }
}

Con esto, tendríamos finalizado nuestro control completamente personalizado, que hemos construido sin utilizar como base ningún otro control predefinido, definiendo desde cero tanto su aspecto visual como su funcionalidad interna o sus eventos públicos.

sgoliver.net