lunes, 30 de noviembre de 2015

Maven compiler plugin example

Hoy he publicado un nuevo artículo en javacodegeeks sobre el plugin compiler de maven el cual nos permite controlar como se compilan nuestras clases

Maven compiler plugin example

Otros artículos sobre plugins de maven:

jueves, 26 de noviembre de 2015

Cargar custom fonts en Android WebView

Cuando usamos el componente WebView de android, cargar fuentes personalizadas puede ser un pequeño infierno. Vamos a ver un ejemplo de como podemos cargar customs fonts en el navegador de android. Para este ejemplo se han usado las siguientes tecnologías
  • Mac OsX El capitan
  • Android Studio 1.5
  • JDK 1.8.0_65

Project ejemplo


Para ver como podemos añadir custom fonts a nuestro WebView vamos a crear un proyecto de ejemplo, para ello pulsamos File -> New -> Project en AndroidStudio


A continuación elegimos la API target de android y el tipo de dispositivos que queremos cubrir, en nuestro caso solo phones y tablets


Ahora elegimos un proyecto vacío


A continuación le damos un nombre a nuestra actividad inicial, en este ejemplo lo dejamos a como viene por defecto


Ahora ya tenemos nuestro proyecto listo.

Pagina a mostrar


Ahora que tenemos nuestro proyecto listo, añadimos al fichero res/layout/content_main.xml un objeto webView
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.example.fhernandez.webviewcustomfontsexample.MainActivity"
    tools:showIn="@layout/activity_main">

    <WebView
        android:layout_width="fill_parent"
        android:layout_height="400dp"
        android:id="@+id/myBowser"
        android:layout_marginTop="10dp" />
</RelativeLayout>

Vamos a definir una pagina HTML estática que será completada dinámicamente en el código dentro de la carpeta de recursos raw de android. Por defecto esta carpeta no se añade al proyecto, para añadirla hacemos doble click sobre el proyecto y pulsamos New -> Android resource directory


A continuación seleccionamos raw


Al hacer esto se nos ha creado la carpeta raw dentro de resources en el proyecto. Dentro de ella creamos un fichero llamado our_page.html y le añadimos el siguiente contenido
<html>
<head>
<style>

    @font-face {
        font-family: "AppleGaramond";
        src: url("fonts/AppleGaramond.ttf");
        font-style: normal;
        font-weight: normal;
    }
    @font-face {
        font-family: "AppleGaramond";
        src: url("fonts/AppleGaramond-Bold.ttf");
        font-style: normal;
        font-weight: bold;
    }


    * {font-family: 'AppleGaramond' !important;}
</style>

</head>

<body>

Hello to all, this is a <b>bold</b> text<br>

Como se puede observar, estamos definiendo nuestras fuentes en los estilos de la páginas y se lo aplicamos a todo la página.

Añadir custom fonts

Para añadir nuestras fuentes lo haremos añadiendo los ficheros al directorio assets. AndroidStudio no añade este directorio por defecto en el proyecto, para añadirlo hacemos doble click sobre el proyecto y pulsamos New -> Folder -> Assets Folder


En la siguiente ventana confirmamos


Ahora tendremos el directorio assets disponible, creamos dentro de él un directorio llamado fonts y copiamos ahi nuestro archivos de fuentes. Nuestro proyecto queda de la siguiente manera



Cargamos nuestra página en el webview

Vamos a modificar la actividad principal para cargar nuestra pagina, para ello el código quedara asi
package com.example.fhernandez.webviewcustomfontsexample;

import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.webkit.WebView;

import java.io.IOException;
import java.io.InputStream;

public class MainActivity extends AppCompatActivity {

    private WebView myBowser;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        myBowser = (WebView) findViewById(R.id.myBowser);

        // Load our page
        loadData();


    }

    private void loadData() {

        // read the file
        String myPage = "";
        try {
            myPage = getStringFromFile(this, R.raw.our_page);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Complete the page
        myPage = myPage + "This is a part added from code <b>awesome!</b></body></html>";

        myBowser.loadData(myPage, "text/html; charset=UTF-8", null);
    }

    // Read from res/raw
    public String getStringFromFile (Context context, int resId) throws IOException {

        String result;
        Resources res = context.getResources();
        InputStream in_s = res.openRawResource(resId);

        byte[] b = new byte[in_s.available()];
        in_s.read(b);
        result = new String(b);

        return result;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}



Resultado

Ahora que ya tenemos nuestro proyecto listo, ejecutamos nuestra aplicación y veremos lo siguiente


El resultado no es correcto, ya que se NO se están usando nuestras fuentes.

Corregir el uso de custom fonts

Para que el navegador pueda cargar nuestras fuentes debemos modificar la forma en que se carga la página y hacerlo de esta forma
myBowser.loadDataWithBaseURL("file:///android_asset/", myPage, "text/html", "utf-8",null);

Ahora, si ejecutamos de nuevo, veremos que carga nuestra fuente correctamente


Puedes descargar el código de este ejemplo desde github

martes, 24 de noviembre de 2015

Maven assembly plugin example

Hoy he publicado un nuevo artículo en javacodegeeks sobre el plugin assembly de maven el cual nos permite controlar como se generan nuestros artefactos:

Maven assembly plugin example

Otros artículos sobre plugins de maven:

miércoles, 18 de noviembre de 2015

Maven antrun plugin

Recientemente me he decidido a publicar artículos en javacodegeeks y aquí va mi primer articulo acerca del plugin ant de maven, el cual nos permite ejecutar scripts de ant dentro del ciclo de vida de maven:

Maven antrun plugin example

Espero que lo disfrutéis!

Otros artículos sobre plugins de maven:

viernes, 6 de noviembre de 2015

Java 8 - Lambdas (Interfaces funcionales)

Siguiendo con los ejemplos de lambdas, vamos a hablar hoy un poco acerca de las interfaces funcionales en Java 8.

Las interfaces han sido revisadas en la versión 8 de Java, añadiendo capacidad de definir métodos por defecto y métodos estáticos en las mismas. También se introduce el concepto de interfaz funcional.

Una interfaz funcional es aquella en la que solo hay un método abstracto, lo cual por otro lado no es nada nuevo. Mas de una vez hemos tenido que tratar con objetos como Runnable, o definir un Comparator, o usar un Callable. Estas interfaces son conocidas como SAM Interfaces (Simple Abstract Method Interface). Java 8 añade la anotación 'java.lang.FunctionalInterface' mediante la cual podemos anotar una interfaz e indicar de este modo que es una 'interfaz funcional'.

Como ejemplo podemos ver la siguiente

@FunctionalInterface
public interface Greeter {

    String saluda(String name); 
}

Este tipo de interfaces solían ser inicializadas con clases anónimas, o bien en un fichero independiente implementando la misma. Veamos un ejemplo con nuestra interfaz

final Greeter greeterClassic = new Greeter() {

    @Override
    public String saluda(String name) {
        return "Hello " + name;
    }
            
};

Java 8 nos permite inicializar este tipo de interfaces a través de expresiones lambdas, con lo que podríamos hacer lo siguiente para iniciar un objeto de este tipo

final Greeter greeterLambda = (name) -> {return "Hello " + name;};

Las posibilidades ahora son mayores y nuestro código mas compacto, con lo que ganamos en varios aspectos como: legibilidad, mantenibilidad, etc...

Además de esto, algunas interfaces se han actualizado para poder ser inicializadas de esta forma, veamos un ejemplo a continuación de Runnable

final Runnable task = () -> {
     greeterClassic();
     greeterLambda();
};
        
task.run();
Podéis ver un ejemplo de uso de lambdas con colecciones aqui.

Podéis descargar el código de este post en github.

lunes, 2 de noviembre de 2015

Java 8 - Lambdas (colecciones)

Hola! Desgraciadamente llevo bastante tiempo sin escribir ninguna entrada en este blog el cual creé en su día para registrar aquellas cosillas que no quería olvidar, o que me parecieran interesantes, o que me gustaría tener de referencia 'a mi forma'... voy a tratar de recuperar esta sana costumbre así que hoy a escribir acerca de Java 8.

Java 8 lleva ya un tiempo publicado, concretamente desde Marzo de 2014, y aportó al lenguaje bastantes novedades, hoy vamos a ver una de ellas aplicadas a colecciones: Expresiones Lambdas.

Las expresiones Lambdas son un tipo de closure en java como lo han sido siempre la clases anónimas y nos acerca al mundo de la programación funcional. ¿Es Java un lenguaje funcional por tener lambdas? La respuesta es No, a java aún le queda mucho si es que algún día lo es para ser un lenguaje funcional pero lo que si es innegable es que este tipo de expresiones pueden hacer que nuestro código sea mucho mas legible y simple que antes, para ello vamos a ver un ejemplo:

Supongamos que tenemos una lista de 100000 elementos Strings que contienen números y queremos saber lo siguiente:
  • El valor medio de todos ellos
  • El numero de elementos que son mayores que un número dado

La solución a este problema pasaría por recorrer la lista de elementos convirtiéndolos a número y:
  • Almacenar la suma de todos los números y dividirla por el número de elementos para el valor medio.
  • Recorrer la lista y contar solo aquellos valores superiores al número dado.

Veamos el código a continuación:

package org.fran.anotherjavaprogrammer.lambda;

import java.time.Clock;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;

public class CollectionTest {

    private static final int MAX = 100_000;
 
    public static void main (String[] args) {
 
        final long start = Clock.tickSeconds(ZoneId.systemDefault()).millis();
        final List&ltString&gt list = new ArrayList<>();
  
        for (int i = 0; i < MAX; i++) {
            list.add(String.valueOf(i));
        }
  
        final long avg = getAverage(list);
  
        final long count = getCount(list, 1000);
  
        final long end = Clock.tickSeconds(ZoneId.systemDefault()).millis();
  
        System.out.println("Average value: " + avg);
        System.out.println("Elements number over 1000: " + count);
        System.out.println("Run time: " + (end-start) + " mls");
    }
 
    private static long getAverage(List<String> list) {
  
        long elementsSum = 0L;
        long average = 0L;
  
        if (list != null && list.size() > 0) {
            for (String elem : list) {
                final Integer e = Integer.parseInt(elem);
                elementsSum += e;
            }
   
            average = elementsSum / list.size();
        }
  
        return average;
  
    }
 
    private static int getCount(List<String> list, Integer lowLimit) {
  
        int count = 0;
  
        if (list != null && list.size() > 0) {
            for (String elem : list) {
                final Integer e = Integer.parseInt(elem);
    
                if (e >= lowLimit) {
                    count++;
                }
            }
        }
  
        return count;
  
    }
}

La salida del programa anterior es la siguiente

Average value: 49999
Elements number over 1000: 99000
Run time: 0 mls

Las expresiones Lambdas por el momento se pueden usar para ejecutar interfaces funcionales y para aquellas zonas de la API que hayan sido preparadas para ello como Collections. En Collections se nos ofrece un Stream a través del método stream mediante el cual podemos crear expresiones lambdas para realizar multitud de operaciones como reducciones, filtrados, recorridos, etc...

Veamos como queda el mismo código usando lambdas:

package org.fran.anotherjavaprogrammer.lambda;

import java.time.Clock;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;
import java.util.OptionalDouble;

public class CollectionLambdaTest {

    private static final int MAX = 100_000;
 
    public static void main (String[] args) {
 
        final long start = Clock.tickSeconds(ZoneId.systemDefault()).millis();
        final List<String> list = new ArrayList<>();
  
        for (int i = 0; i < MAX; i++) {
            list.add(String.valueOf(i));
        }
  
        final OptionalDouble avg = list.stream().
             mapToInt(s -> Integer.parseInt(s)).
             distinct().
             average();
  
        final long count = list.stream().
             mapToInt(s -> Integer.valueOf(s)).
             filter(n -> n >= 1000).
             count();
  
        final long end = Clock.tickSeconds(ZoneId.systemDefault()).millis();
  
        if (avg.isPresent()) {
            System.out.println("Average value: " + avg.getAsDouble());
        } else {
            System.out.println("Is not possible to calculate average value");
        }
  
        System.out.println("Elements number over 1000: " + count);
        System.out.println("Run Time: " + (end-start));
    }
}
El código usando lambdas es mucho mas compacto y legible que el original. Se observan varias cosas:
  • Se usa el separador '_' al definir MAX, esta novedad se introdujo en Java 7.
  • No se define el genérico en la parte derecha del new ArrayList al definir list, esto es otra característica introducida en Java 7, el compilador infiere está información de la parte izquierda de la asignación.

En el caso del cálculo de la media hacemos lo siguiente:
  • Hacemos una transformación de String a Int, devolviendo un IntSream con mapToInt, definiendo el parámetro ToIntFunction como una expresión lambda.
  • Seleccionamos los valores sin duplicados con distinct().
  • Finalmente, para todos los elementos calculamos la media con average().

En el caso de contar los elementos superiores a un numero dado hacemos lo siguiente:
  • Hacemos una transformación de String a Int, devolviendo un IntSream con mapToInt, definiendo el parámetro ToIntFunction como una expresión lambda.
  • Aplicamos un filtro en el cual seleccionamos los elementos que sean mayores o iguales al valor indicado. Definimos el parametro IntPredicate como una lambda.
  • Finalmente, contamos los elementos con count().

La salida del programa es la siguiente:
Average value: 49999.5
Elements number over 1000: 99000
Run Time: 0

En este ejemplo hemos podido ver como las expresiones lambdas se integran en Java a partir de la versión 8.

En este post podrás ver el uso de lambdas con interfaces funcionales.

Puedes descargar el código desde github.