Author Archives: Gustavo

Go Hola Mundo

¿Cómo escribir un hola mundo en Golang?

En Go escribes un ‘hola mundo’ de este modo:


package main

import "fmt"

func main() {
	fmt.Printf("hola mundo\n")
}

-Los paquetes se definen en la primer línea como “package”
-A continuación van los “import”
-El ‘main’ del programa se inicia con func main()

¿Como ejecutar hello-world.go?

$ go run hello-world.go
hola mundo

¿Cómo hacer un build de hello-world.go?

$ go build hello-world.go
$ ./hello-world
hola mundo

Compartir esto:

Como instalar Go en Mac OS

Para instalar Golang en Mac OS sigue estos pasos:

Descargar la versión de golang para Mac desde esta dirección:

https://golang.org/dl/

Al finalizar la descarga, ejecuta el archivo pkg descargado.

Sigue los pasos del instalador de go

 

Al finalizar la instalación go queda en instalado en la carpeta /usr/local/go

Agrega Go al PATH

El instalador agrega al PATH la carpeta /usr/local/go/bin .
Verifica el PATH buscando que contenga la ruta /usr/local/go/bin

~ $ echo $PATH
/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/go/bin
~ $

Si no existe agrega go al path en tu archivo ~/.bash_profile

 
vi ~/.bash_profile
export PATH=$PATH:/usr/local/go/bin

Comprueba la instalación

Comprueba go escribiendo go version desde la consola.

~ $ go version
go version go1.9.1 darwin/amd64
~ $

Crea un “hola mundo” en go

En tu home crea la carpeta “hello” y el crea el archivo “hello.go” en esa carpeta.
El archivo hello.go debe lucir de este modo.

package main

import "fmt"

func main() {
    fmt.Printf("hello world\n")
}

Dentro de la carpeta con el archivo hello.go ejecuta go build para crear un ejecutable en go.


~ $ cd hello/
~/hello $ go build
~/hello $ ls
hello		hello.go
~/hello $

Ejecuta el archivo compilado y visualiza la salida.

~/hello $ ./hello
hello world
~/hello $ 

Referencia: golang

Compartir esto:

Strategy Pattern con Spring Boot

Strategy Pattern con Spring Boot

Un buen patrón para resolver la complejidad cuando debes implementar diferentes comportamientos según algún estado es el patrón strategy.

Un patrón strategy encapsula comportamiento que podemos intercambiar en ejecución. Es decir, cambiar el comportamiento o lógica de negocio acorde al estado del modelo o contexto en el que te se encuentra, evitando el uso extensivo de if o switch en tu código.

Es bastante simple. Necesitas una interfaz Strategy y diferentes implementaciones para la resolución de los diferentes comportamientos deseados.

Para este ejemplo suponemos que tenemos una entidad “User” que representa un usuario. Estos users son clasificados por tipo acorde a un enum “UserType”. El UserType determinará el valor del atributo limitCredit del User.


@Entity
public class User {

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;

   @NotBlank
   private String userName;

   @NotBlank
   private String lastName;

   private UserType type;

   private double limitCredit;


}

public enum UserType {
   NORMAL, FULL, GOLD;
}


Queremos que nuestro servicio se desentienda de esta decisión, por lo que crearemos diferentes estrategias para cada tipo de user.

Cómo crear el patrón Strategy

Defines la interfaz de la cual luego vas a implementar los diferentes comportamientos. En este ejemplo simple se trata de cambiar el limite bajo algún cálculo que suponemos complejo para los diferentes tipos de User.

public interface Strategy {
   void changeLimit(User user);
}

Las implementaciones de las estrategias para cada tipo UserType quedan así:


public class StrategyOperationNormal implements Strategy {
   @Override
   public void changeLimit(User user) {
       // a complex calculation.. 
       user.setType(UserType.NORMAL);
       user.setLimitCredit(1000D);
   }
}

public class StrategyOperationFull implements Strategy {
   @Override
   public void changeLimit(User user) {
       user.setType(UserType.FULL);
       user.setLimitCredit(5000D);
   }
}
public class StrategyOperationGold implements Strategy {

   @Override
   public void changeLimit(User user) {
       user.setType(UserType.GOLD);
       user.setLimitCredit(20000D);
   }

}

Usar un Factory que nos devuelva el Strategy

Para simplificar aún más el uso de Strategy en tu servicio creas una clase StrategyFactory. Este factory te devolverá la estrategia correspondiente según el UserType.

Un patrón factory te permite crear objetos bajo alguna condición sin necesidad de conocer o preocuparte acerca de la implementación real, en definitiva solo conoces la interfaz.


@Component
public class StrategyFactory {

   private Map<UserType, Strategy> strategies = new EnumMap<>(UserType.class);

   public StrategyFactory() {
       initStrategies();
   }

   public Strategy getStrategy(UserType userType) {
       if (userType == null || !strategies.containsKey(userType)) {
           throw new IllegalArgumentException("Invalid " + userType);
       }
       return strategies.get(userType);
   }

   private void initStrategies() {
       strategies.put(UserType.NORMAL, new StrategyOperationNormal());
       strategies.put(UserType.FULL, new StrategyOperationFull());
       strategies.put(UserType.GOLD, new StrategyOperationGold());
   }
  
}


En tu servicio haces uso del factory para obtener la estrategia y delegarle la lógica de cambio de tipo de UserType en el metodo “changeType” .


@Service
public class UserService {

   private final StrategyFactory strategyFactory;
   private final UserRepository repository;

   @Autowired
   public UserService(UserRepository repository, StrategyFactory strategyFactory) {
       this.repository = repository;
       this.strategyFactory = strategyFactory;
   }

   // other methods of service..

   public User changeType(long id, UserType type) {
       Strategy strategy = strategyFactory.getStrategy(type);
       User user = repository.findOne(id);
       strategy.changeLimit(user);
       return repository.save(user);
   }

}

Probando tu Strategy

Usando el Controller de la app, que tenemos ya definido, creamos un User. Por defecto el User se crea con un tipo “NORMAL”.

El controller de nuestra app


@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/users", method = RequestMethod.GET)
    public ResponseEntity<User> list() {
        List<User> users = userService.list();
        return new ResponseEntity(users, HttpStatus.OK);
    }

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public ResponseEntity<User> userById(@RequestParam(value = "id") long id) {
        User user = userService.get(id);
        return new ResponseEntity(user, HttpStatus.OK);
    }

    @RequestMapping(value = "/create", method = RequestMethod.POST)
    public ResponseEntity<User> create(@Valid @RequestBody User user) {
        User userCreated = userService.create(user);
        return new ResponseEntity(userCreated, HttpStatus.CREATED);
    }

    @RequestMapping(value = "/change_type", method = RequestMethod.POST)
    public ResponseEntity<User> changeType(@RequestParam(value = "id") long id,
                                           @RequestParam(value = "type") UserType type) {
        User userCreated = userService.changeType(id, type);
        return new ResponseEntity(userCreated, HttpStatus.CREATED);
    }

}

El Service completo de nuestra app


@Service
public class UserService {

    private final StrategyFactory strategyFactory;
    private final UserRepository repository;

    @Autowired
    public UserService(UserRepository repository, StrategyFactory strategyFactory) {
        this.repository = repository;
        this.strategyFactory = strategyFactory;
    }

    public User get(long userId) {
        return repository.findOne(userId);
    }

    public List<User> list() {
        Iterable<User> users = repository.findAll();
        List<User> list = new ArrayList<User>();
        users.forEach(list::add);
        return list;
    }

    public User create(User user) {
        Strategy strategy = strategyFactory.getStrategy(UserType.NORMAL);
        strategy.changeLimit(user);
        return repository.save(user);
    }


    public User changeType(long id, UserType type) {
        Strategy strategy = strategyFactory.getStrategy(type);
        User user = repository.findOne(id);
        strategy.changeLimit(user);
        return repository.save(user);
    }

}

Ejecutas el rest api “/create” para crear un User que luego vamos a modificar. Enviamos en el body lo mínimo que necesita la entidad User.
El user se crea con el type “NORMAL” por defecto.

Luego ejecutas el rest api “/change_type?id=1&type=FULL” para probar tu Servicio que usa el Factory y el Strategy. Aquí intentas cambiar el type de NORMAL a FULL para el user previamente creado.

Observa que el user ha cambiado su límite acorde a la Strategy.

Descarga este código completo desde GitHub

Compartir esto:

Spring Boot – como capturar las excepciones y retornar un json estandar

Cómo capturar y manejar las excepciones con Spring Boot para una Rest Api

En una aplicacion Spring en la cual exponemos endpoints Rest para que los clientes la consuman contamos con un manejador de excepciones de forma global para toda tu app.
Esto nos da la flexibilidad de decidir que devolver al cliente para cada tipo de exception.

En este ejemplo te muestro como capturar una excepción cuando el cliente consume tu api y el body que te ha enviado no es válido.

En el caso de recibir un body inválido Spring crea una excepción MethodArgumentNotValidException y retorna un json que seguramente sobra para el cliente o información que te gustaría estandarizar para todos los errores de forma más acorde para tu api.

El modelo que representa el body que vas a recibir


public class User {

    @JsonProperty
    @NotBlank
    private String userName;

    @JsonProperty
    @NotBlank
    private String lastName;

    @JsonProperty
    @NotNull
    private Gender gender;

    @JsonProperty
    @Min(value = 18)
    @Max(value = 150)
    @NotNull
    private Integer age;

    @JsonProperty
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy")
    @Past
    @NotNull
    private Date dateOfBirth;

Resultado de una exception MethodArgumentNotValidException

Este es el resultado que recibes cuando Spring lanza una excepción al recibir un body inválido en un post. En este ejemplo se omite un tag requerido “dateOfBirth” en el body y por esta razón se lanza la excepción.

¿Cómo debes capturar esta excepción y devolver algo a tu gusto?

Creas la clase que representará la respuesta json

Defines tu clase con los json property que deseas mostrar como retorno del error. Suponemos que deseas devolver tres atributos simples, el mensaje, el status code, y la url.


import com.fasterxml.jackson.annotation.JsonProperty;

public class ErrorInfo {

   @JsonProperty("message")
   private String message;
   @JsonProperty("status_code")
   private int statusCode;
   @JsonProperty("uri")
   private String uriRequested;

   public ErrorInfo(ApiException exception, String uriRequested) {
       this.message = exception.getMessage();
       this.statusCode = exception.getStatusCode().value();
       this.uriRequested = uriRequested;
   }

   public ErrorInfo(int statusCode, String message, String uriRequested) {
       this.message = message;
       this.statusCode = statusCode;
       this.uriRequested = uriRequested;
   }

   public String getMessage() {
       return message;
   }

   public int getStatusCode() {
       return statusCode;
   }

   public String getUriRequested() {
       return uriRequested;
   }

}

Creas el manejador global de excepciones ControllerAdvice

Para capturar este error debes crear una clase anotada @ControllerAdvice allí definirás cada tipo de error que deseas manejar @ExceptionHandler. Este método recibe el request y la excepción en particular. Puedes manejar todas las excepciones que desees.

Para nuestro caso:


@ControllerAdvice
public class ErrorHandler {

   @ExceptionHandler(MethodArgumentNotValidException.class)
   public ResponseEntity<ErrorInfo> methodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) {
       ErrorInfo errorInfo = new ErrorInfo(HttpStatus.BAD_REQUEST.value(), e.getMessage(), request.getRequestURI());
       return new ResponseEntity<>(errorInfo, HttpStatus.BAD_REQUEST);
   }
}

Si pruebas el resultado verás esta respuesta, algo mejor, pero todavia el mensaje está poco claro.

Mejorar el mensaje

Para hacer más legible el mensaje puedes leer cada uno de los FieldError que son devueltos por MethodArgumentNotValidException.getBindingResult() para armar un string más legible.


@ControllerAdvice
public class ErrorHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorInfo> methodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) {

        // get spring errors
        BindingResult result = e.getBindingResult();
        List<FieldError> fieldErrors = result.getFieldErrors();

        // convert errors to standard string
        StringBuilder errorMessage = new StringBuilder();
        fieldErrors.forEach(f -> errorMessage.append(f.getField() + " " + f.getDefaultMessage() +  " "));

        // return error info object with standard json
        ErrorInfo errorInfo = new ErrorInfo(HttpStatus.BAD_REQUEST.value(), errorMessage.toString(), request.getRequestURI());
        return new ResponseEntity<>(errorInfo, HttpStatus.BAD_REQUEST);

    }

}

Observa el resultado nuevamente al enviar un post que no válida:

Conclusión

Puedes hacer uso de ControllerAdvice para capturar todas las excepciones de tu api y devolver un formato json estandarizado para toda tu aplicación.

Te recomiendo la lectura de este libro Spring Boot in Action . Espero te sirva.

Descarga este código completo desde GitHub

Compartir esto:

Spring Boot + MySql + HikariCP

Configurar Spring Boot con MySql usando HikariCP

Para configurar tu proyecto Spring con MySql utilizando HikariCP debes agregar las dependencias y configurar tu archivo properties con estos simples pasos.

Dependencias

Detalle de dependencias que necesitas para tu proyecto Spring con MySql y Hikari


<dependencies>

   <!-- Spring boot -->
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-data-jpa</artifactId>
   </dependency>

   <!-- Hikari -->
   <dependency>
       <groupId>com.zaxxer</groupId>
       <artifactId>HikariCP</artifactId>
       <version>2.6.0</version>
   </dependency>

   <!-- MySQL Connector -->
   <dependency>
       <groupId>mysql</groupId>
       <artifactId>mysql-connector-java</artifactId>
   </dependency>
</dependencies>

Definición del archivo properties

Debes especificar el dialect para MySql, y para el tipo de datasource indicar HikariDataSource.
Así queda tu archivo application.properties


spring.jpa.hibernate.ddl-auto: create-drop
spring.jpa.show-sql: true
spring.jpa.properties.hibernate.dialect: org.hibernate.dialect.MySQL5Dialect

spring.datasource.url: jdbc:mysql://localhost:3306/example_db
spring.datasource.username: root
spring.datasource.password:
spring.datasource.driverClassName: com.mysql.jdbc.Driver
spring.datasource.type: com.zaxxer.hikari.HikariDataSource

 ## HikariCP config - spring.datasource.hikari.*
spring.datasource.hikari.pool-name: pool-hikari-example
spring.datasource.hikari.maximum-pool-size: 10
spring.datasource.hikari.connection-timeout: 60000


Inicia tu aplicación con Spring Boot + MySlq + Hikari

Corres tu app mvn spring-boot:run

Observa en la consola esta salida en dónde puedes ver que ha levantado el datasource de Hikari con el pool que especificaste previamente en tu archivo de configuración.

    spring.datasource.hikari.pool-name: pool-hikari-example

Salida del log de Spring


INFO 59268 --- [           main] com.zaxxer.hikari.HikariDataSource       : pool-hikari-example - Start completed.

Con esto ya tienes tu configuración usando HikariCP.

Descarga este código completo

Compartir esto:

Spring Batch – Como eliminar las tablas de jobs

Spring Batch mantiene diferentes tablas con las instancias de los Jobs, las ejecuciones de los Jobs y las ejecuciones de los Steps, entre otras.

Cuando estas en etapa de desarrollo resulta muchas veces útil eliminar las tablas para iniciar la app en limpio.

Si necesitas eliminar todas las tablas de Jobs y Steps debes correr este script en tu BD en este mismo orden:


DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_CONTEXT ;
DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_CONTEXT ;
DROP TABLE IF EXISTS BATCH_STEP_EXECUTION ;
DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_PARAMS ;
DROP TABLE IF EXISTS BATCH_JOB_EXECUTION ;
DROP TABLE IF EXISTS BATCH_JOB_INSTANCE ;

DROP TABLE IF EXISTS BATCH_STEP_EXECUTION_SEQ ;
DROP TABLE IF EXISTS BATCH_JOB_EXECUTION_SEQ ;
DROP TABLE IF EXISTS BATCH_JOB_SEQ ;

Al reiniciar tu aplicación estas tablas deberían ser creadas nuevamente por Spring Batch.

Compartir esto:

Spring Boot Rest – Ejemplo RestController con RequestBody y Valid

Como crear con Spring Boot una aplicación Rest

Para definir un RestController en Spring Boot debes anotar tu controlador como un @RestController y anotar cada método que responderá con @RequestMapping.

Spring hace uso de la anotación @RequestBody para mapear el json y la anotación @Valid para aplicar los constraints definidos en la clase para el caso de los post.

Para este ejemplo desarrollarás una pequeña aplicación que recibirá un post con un body en json para crear un User mediante un rest api, también tendrás la opción de consultar los User guardados en la BD.

-El endpoint /create recibirá un json con el User, lo validará y lo guardará en la BD.
-Crearemos otro endopoint /users para consultar todos los User en la BD
-También un /user?id=userId para mostrar un User por el id
-Este endpoint para crear, recibirá el json y lo mapeará contra el pojo User

Configurar las dependencias


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
    </dependencies>

Como configurar la Base de datos

El pom definiste una base de datos h2 que correrá embebida en la applicación, en nuestro caso persistirá solamente en memoria.

¿Cómo definir una base de datos H2 en memoria en Spring?

Para que la base de datos h2 persista solo en memoria debemos indicar el datasource en el application.properties de este modo:


#in memory database
spring.datasource.url = jdbc:h2:mem:example_db

#in file database
#spring.datasource.url = jdbc:h2:file:~example_db

spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver

Configurar la app para Spring Boot


@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

Creas el modelo User

En el modelo que soporta nuestro json desde el post agrega los json property @JsonProperty indicando cómo deberá entenderse cada atributo del json. Además anotas las validaciones que deseas. Para este ejemplo, este mismo modelo lo usaremos para la persistencia por eso lo anotamos como @Entity.

Algunas de las validaciones que para este ejemplo son

@NotNull – verifica que el valor no quede nulo.
@NotBlank – verifica que el valor sea nulo o blanco para Strings
@Past – verifica que el valor este en el pasado
@Min – valor mínimo posible
@Max – valor máximo posible
@Email – verifica que el string tenga un formato de email valido


@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @JsonProperty("user_name")
    @NotBlank
    private String userName;

    @JsonProperty("last_name")
    @NotBlank
    private String lastName;

    @JsonProperty("gender")
    @NotNull
    private Gender gender;

    @JsonProperty("age")
    @Min(value = 18)
    @Max(value = 150)
    @NotNull
    private Integer age;

    @JsonProperty("birth")
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy")
    @Past
    @NotNull
    private Date dateOfBirth;
    
   // get and set... 

}


package com.gp.model;
public enum Gender {
    MALE, FEMALE;
}

Creas el servicio y el repository

Tu servicio recibirá las consultas desde el controller y la resolverá lógica del negocio, para el caso del acceso a la base este servicio consultará al repositorio especifico.


@Service
public class UserService {

    @Autowired
    private UserRepository repository;

    public User get(long userId) {
        return repository.findOne(userId);
    }

    public List<User> list() {
        Iterable<User> users = repository.findAll();
        List<User> list = new ArrayList<User>();
        users.forEach(list::add);
        return list;
    }

    public User create(User user) {
        return repository.save(user);
    }
}

El repositorio será un CrudRespository que nos da lo básico para este ejemplo.


import com.gp.model.User;
import org.springframework.data.repository.CrudRepository;

public interface UserRepository extends CrudRepository<User, Long> {

}

Definir un RestController con Spring Boot

Para crear un endpoint a fin de consultar Users y crearlos debes definir un controller de este modo usando las anotaciones @RestController y @RequestMapping


package com.gp.controllers;

import com.gp.model.User;
import com.gp.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.util.List;

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/users", method = RequestMethod.GET)
    public ResponseEntity<User> list() {
        List<User> users = userService.list();
        return new ResponseEntity(users, HttpStatus.OK);
    }

    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public ResponseEntity<User> userById(@RequestParam(value = "id") long id) {
        User user = userService.get(id);
        return new ResponseEntity(user, HttpStatus.OK);
    }

    @RequestMapping(value = "/create", method = RequestMethod.POST)
    public ResponseEntity<User> create(@Valid @RequestBody User user) {
        User userCreated = userService.create(user);
        return new ResponseEntity(userCreated, HttpStatus.CREATED);
    }

}

Pruebas RestController con @Valid y @RequestBody

Prueba enviar tu json con un valor inválido:

Envía al rest /create vía post este body que contiene un valor invalido para el “age”.

Observa la respuesta con un status 400 por no haber pasado los constraint del modelo.

Prueba con valores válidos:

Ahora envía el json con todos lo valores correctos, el endpoint te devuelve el modelo que creado ya con un id asignado.

Consulta de todos los User guardados:

Mediante el el endpoint /users podemos obtener toda la lista de User que hemos ido creando.

Descarga este código completo

Compartir esto:

Android Retrofit con Proguard – Como evitar errores con Gson

Si estas usando Retrofit en tu app Android y a la vez estas ofuscando y minizando tu app con proguard será necesario que agregues estas lineas a tu archivo proguard.txt

# Platform calls Class.forName on types which do not exist on Android to determine platform.
-dontnote retrofit2.Platform
# Platform used when running on Java 8 VMs. Will not be used at runtime.
-dontwarn retrofit2.Platform$Java8
# Retain generic type information for use by reflection by converters and adapters.
-keepattributes Signature
# Retain declared checked exceptions for use by a Proxy instance.
-keepattributes Exceptions

Por otro lado, si estas mapeando tus archivos desde la response de Retrofit con Gson necesitarás además evitar que estas clases de tu modelo que mapeas sean modificadas por proguard. Para esto puedes agregar la anotación @Keep a tus clases

@Keep
public class User {

    @SerializedName("id")
    @Expose
    private String id;
    @SerializedName("username")
    @Expose
    private String username;
 ....
	
}

Observa en tu archivo proguard que por default este evita las clases anotadas de con @Keep

# Understand the @Keep support annotation.
-keep class android.support.annotation.Keep

-keep @android.support.annotation.Keep class * {*;}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <methods>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <fields>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <init>(...);
}

Puedes, en vez de usar @Keep, agregar el paquete de tu modelo en el archivo proguard, aunque yo prefiero usar la anotación.
Suponiendo que las clases de tu modelo estan en “com.model” puedes agregar este paquete para que proguard lo mantenga intacto.

-keep class com.model.** { *; }

Referencias:
http://square.github.io/retrofit/

Compartir esto:

Java 8 Reference method

Puedes utilizar lambda para crear métodos anónimos, pero hay casos en el que estas expresiones lambda no hacen otra cosa más que llamar a otro método. En estos casos resulta más claro llamar al método directamente por su nombre.

Si tienes una lista de personas de este modo y quieres ordenarla harías algo así de forma habitual con Java:


// Without lambda expression
Collections.sort(personList, new Comparator<Person>() {
   @Override
   public int compare(Person a, Person b) {
       return a.getBirthday().compareTo(b.getBirthday());
   }
});

Con lambda expressions puedes hacerlo aún mejor:


// With lambda expression
Collections.sort(personList,
       (Person a, Person b) -> {
           return a.getBirthday().compareTo(b.getBirthday());
       }
);

Observa la expresión lambda anterior. Lo que hace es en definitiva devolver un un int, si quisieras podrías llevarla a un método ‘compareByAge’ que haga lo mismo retornando el int con el resultado de la comparación, de este modo


public class Person {

public enum Sex {
   MALE, FEMALE
}

private String name;
private LocalDate birthday;
private Sex gender;
private String emailAddress;

// static method for lambda method reference example
public static int compareByAge(Person a, Person b) {
   return a.birthday.compareTo(b.birthday);
}

}

Este método puedes usarlo así directamente aprovechando “Method References” de Java 8.
Lo que haces en definitiva es pasar el método a ser ejecutado como argumento

// With method reference
Collections.sort(personList, Person::compareByAge);

Cómo usar la referencia a métodos en Java 8

Existen cuatro formas de llamar a métodos por referencias:

1- Referencia a métodos estáticos
2- Referencia a métodos de instancia de un objeto
4- Referencia a métodos de instancia de un objeto para un tipo en particular
5- Referencias a un constructor

// 1-With method reference
Collections.sort(personList, Person::compareByAge);
// --

// 2-Reference to a static method
personList.forEach(System.out::println);
// --


// 3-Reference to an instance method of a particular object
class ComparisonProvider {
   public int compareByName(Person a, Person b) {
       return a.getName().compareTo(b.getName());
   }

   public int compareByAge(Person a, Person b) {
       return a.getBirthday().compareTo(b.getBirthday());
   }
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();

personList.sort(myComparisonProvider::compareByAge);
// --


// 4-Reference to an instance method of an arbitrary object of a particular type
Collections.sort(personList, Person::compareByAge);
// --

// 5-Reference to a constructor

// Using a lambda expression
Supplier<Person> personSupplier2 = () -> new Person();
Person personFromSupplier2 = personSupplier2.get();

// Using a method reference
Supplier<Person> p = Person::new;
Person onePerson = p.get();

..
Referencias:
https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

Compartir esto:

Java 8 Uso de Stream básico

Podemos definir Streams como una secuencia de funciones que se ejecutan una detrás de otra, de forma anidada.

Las operaciones sobre Stream pueden ser intermedias o finales. En el caso de ser intermedias (filter, sorted, map) stream devolverá nuevamente otro stream permitiendo la continuidad de pasos o funciones sobre ella misma. Esto es llamado ‘pipelining’.

In computing, a pipeline is a set of data processing elements connected in series, where the output of one element is the input of the next one. The elements of a pipeline are often executed in parallel or in time-sliced fashion; in that case, some amount of buffer storage is often inserted between elements.
Wikipedia

Estas son algunas funciones intermedias:

        // Intermediate operations
        Stream<Product> stream = products.stream();
        Stream<Product> filterStream = stream.filter(p -> "B".equals(p.getType()));
        Stream<Product> sorted = filterStream.sorted(comparing(Product::getPrice));

        // Final operation
        sorted.forEach(System.out::println);

Si no has leido sobre Lamndas te recomiendo una breve lectura antes.

Supongamos que quieres procesar una lista de productos y realizar diferentes procesos como:

  • recorrer la lista
  • filtrar durante el recorrido los elementos según alguna condición
  • ordenar el resultado anterior
  • mostrar el resultado

Harías estas operaciones de forma tradicional así:


        List<Product> products = getProducts();
        List<Product> resultFilter = new ArrayList<>();

        // foreach filter
        for(Product p : products) {
            if("B".equals(p.getType())){
                resultFilter.add(p);
            }
        }
        // sort
        Collections.sort(resultFilter, new Comparator<Product>() {
            @Override
            public int compare(Product p1, Product p2) {
                return p1.getPrice().compareTo(p2.getPrice());
            }
        });
        // print
        for(Product p: resultFilter) {
            System.out.println(p);
        }  

La clase Product que usas es:


public class Product {

    private long id;
    private String name;
    private String type;
    private Double price;

    // get and set...
}

Como usar Stream con Java 8

Con Java 8 utilizando Streams y Lambdas haces las mismas operaciones anteriores de este modo:

        List<Product> products = getProducts();

        // filter + sort + print
        products.stream().filter(p ->"B".equals(p.getType())).
                sorted(comparing(Product::getPrice)).
                forEach(System.out::println);

Puedes observar rápidamente la reducción en líneas de código y complejidad con este simple ejemplo.

Ejemplos de uso simple Stream con Java 8

Observa esta operaciones usando Stream y como pueden ayudarte

       List<Product> products = getProducts();

        // limit and foreach
        products.stream().limit(10).forEach(System.out::println);

        // filter and count
        long count = products.stream().filter(product -> product.getPrice() > 200d).count();
        System.out.println(count);

        // collect ids
        List<Long> ids = products.stream().filter(product -> product.getPrice() > 200d).
                map(p -> p.getId()).collect(Collectors.toList());
        ids.forEach(System.out::print);  // print this ids..

        //statistics
        DoubleSummaryStatistics doubleSummaryStatistics = products.stream().mapToDouble(p -> p.getPrice()).summaryStatistics();
        System.out.println(doubleSummaryStatistics.getSum());
        System.out.println(doubleSummaryStatistics.getMax());
        System.out.println(doubleSummaryStatistics.getMin());

Referencias:
http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html

Descarga este código completo

Compartir esto:

Java 8 Usar expresiones Lambda

Que son las expresiones lambda

Las expresiones lambda son una forma de crear funciones anónimas y que puedes utilzar en dónde el párametro recibido sea una interfaz funcional

Una expresión lambda tiene esta forma

(parameters) -> expression
(parameters) -> { statements; }

Estas son algunas expresiones lambda de ejemplo:

  • () -> 5 // directamente devuelve un valor, return 5
  • x -> 2 * x // duplica el valor de x y lo retorna
  • (x, y) -> x – y // toma dos valores y retorna su diferencia, aqui se infiere el tipo int
  • (int x, int y) -> x + y // toma dos enteros y retorna su suma
  • (String s) -> System.out.print(s) // toma un string y lo imprime en la consola

Observa lo siguiente:
-Si se tiene un solo parámetro no es obligatorio usar los paréntesis
x -> 2 * x

-Si no se tienen parámetros o si hay dos o más parámetros debemos usar paréntesis
() -> 5
(x, y) -> x – y

-Si la expresión lambda es de una línea no se requieren llaves ni return.
(String s) -> System.out.print(s)

Expresiones lambda e Interfaces funcionales

Repasando lo dicho, las expresiones lambda son funciones anónimas que puedes utilizarlas en donde el parámetro sea una interfaz funcional.

Por ejemplo, la interface Comparator es una interfaz funcional en el que usas su método abstracto ‘compare’ con expresiones lambda.

@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}

Puedes entonces crear un comparator utilizando una lambda expresión con esta interfaz funcional de este modo.


// JAVA 8 with lambda
Comparator<UserModel> userModelComparator = (UserModel u1, UserModel u2) -> u1.getPosition().compareTo(u2.getPosition());

// JAVA 7 lo harias de este modo
Comparator<UserModel> userModelComparator = new Comparator<UserModel>() {
   @Override
   public int compare(UserModel u1, UserModel u2) {
       return u1.getPosition().compareTo(u2.getPosition());
   }
};

Algunos ejemplos de uso de lambdas

Prueba estos ejemplos simples en tu IDE para comprender mejor el uso de lambdas y cómo puedes simplicar significativamente tu código


public class LambdaExample {

    public static void main(String[] args) {

        List<UserModel> userModels = getUsers();

        // JAVA 8 sort list using lambda
        userModels.sort((UserModel u1, UserModel u2)-> u1.getPosition().compareTo(u2.getPosition()));

        // JAVA 8 Foreach using lambda
        userModels.forEach((userModel)->System.out.println(userModel));

        // JAVA 8 Foreach using lambda and reference method
        userModels.forEach(System.out::println);

        // JAVA 8 Foreach using lambda with sentences
        userModels.forEach(user->{
            if("Gustavo".equals(user.getName())){
                System.out.println("Hi Gustavo!");
            }else{
                System.out.println(user.getName());
            }
        });

        // JAVA 8 Simple Runnable using lambda
        new Thread(() -> System.out.println("Hi thread")).start();

        // JAVA 8 Runnable using lambda
        Runnable task = () -> {
            userModels.forEach(user->{
                System.out.println( "Hi " + user.getName() );
            });
        };
        // start the thread
        new Thread(task).start();


    }

    static class UserModel  {

        String name;
        String position;

        public UserModel(String name, String position) {
            this.name = name;
            this.position = position;
        }

        public String getName() {return name;}

        public String getPosition() {
            return position;
        }
    }


    static List<UserModel> getUsers() {

        List<UserModel> userModels = new ArrayList<>();
        userModels.add(new UserModel("Gus","A"));
        userModels.add(new UserModel("Harrison","Z"));
        userModels.add(new UserModel("James","G"));
        //.. more..
        return userModels;

    }

}

Compartir esto:

Android Guardar el estado de un Activity

Al momento de generar valores puedes necesitar guardar estos cuando el usuario por ejemplo, rota el dispositivo.
Para este propósito Android te provee de dos métodos que debes sobreescribir en tu Activity onSaveInstanceState y onRestoreInstanceState

public void onSaveInstanceState(Bundle savedInstanceState) {}

public void onRestoreInstanceState(Bundle savedInstanceState) {}

Debes utilizar el Bundle que que recibes como parámetro en ambos métodos.

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
   super.onSaveInstanceState(savedInstanceState);

   // Save in savedInstanceState.
   savedInstanceState.putStringArray(SPINNER_DATA, colors);
}

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
   super.onRestoreInstanceState(savedInstanceState);

   // Restore state from the savedInstanceState.
   if(savedInstanceState != null) {
       colors = savedInstanceState.getStringArray(SPINNER_DATA);
       addColors(colors);
   }


Codigo completo de ejemplo para guardar el state de un Activity

Este ejemplo supone que al hacer clic en el botón se ejecuta la acción de crear cierta información, aqui será crear un array de colores, y cargarlo en el spinner.
Este array deseas conservarlo si el usuario rota la pantalla o cambia a otra aplicación y por ello utilizarás los métodos previstos onSaveInstanceState y onRestoreInstanceState.


public class MainActivity extends AppCompatActivity {

   private static final String SPINNER_DATA = "SPINNER_DATA";
   private Spinner spinner;
   private String colors[]  = new String[0];

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       spinner = (Spinner) findViewById(R.id.spinner);
   }

   @Override
   public void onSaveInstanceState(Bundle savedInstanceState) {
       super.onSaveInstanceState(savedInstanceState);

       // Save in savedInstanceState.
       savedInstanceState.putStringArray(SPINNER_DATA, colors);
   }

   @Override
   public void onRestoreInstanceState(Bundle savedInstanceState) {
       super.onRestoreInstanceState(savedInstanceState);

       // Restore state from the savedInstanceState.
       if(savedInstanceState != null) {
           colors = savedInstanceState.getStringArray(SPINNER_DATA);
           addColors(colors);
       }

   }

   /**
    * Click button
    */
   public void doColors(View view) {

       // super simple data for this example
       colors = new String[]{"Red", "Blue", "White", "Yellow", "Black", "Green", "Purple", "Orange", "Grey"};
       addColors(colors);

   }

   /**
    * Add data to spinner
    */
   private void addColors(String[] colorsToAdd) {
       ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, colorsToAdd);
       spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
       spinner.setAdapter(spinnerArrayAdapter);
   }

}

..


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    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"
    tools:context="com.gustavopeiretti.myapplication.MainActivity">


    <Button
        android:text="Click Me"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="33dp"
        android:id="@+id/button"
        android:onClick="doColors"/>

    <Spinner
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/button"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginTop="69dp"
        android:id="@+id/spinner" />

</RelativeLayout>


Descarga este código completo

Compartir esto:

Android Como enviar parámetros entre Activities usando Parcelable

Porque usar Parcelable

Para enviar objetos entre Activities tienes dos formas convenientes.
Una de ellas es enviando un objeto Serializable y la segunda es enviando un Parcelable.

La serialización en Android es bastante lenta y no se recomienda para el envío de objetos entre Activities. Por esto Android creo Parcelable para resolver este problema. Parcelable es mucho más rápido que la serialización estándar aunque su implementación requiere un poco más de trabajo.

A menos que tu objeto sea muy ‘pequeño’ es mejor que implementes siempre Parcelable cuando que necesites enviar un objeto entre Activities.

Cómo definir un objeto Parcelable en android

Dijimos que la mejor opción para pasar objetos entre Activities es implementar interfaz Parcelable.

Observa que en Parcelable debes definir

void writeToParcel(Parcel dest, int flags), 
int describeContents()

También establecer un constructor que reciba el Parcel y un Creator

protected Product(Parcel in) {...}
public static final Creator<Product> CREATOR = new Creator<Product>() {...}

Es importante que respetes el mismo orden de write y read. Es decir si escribiste en primer lugar el id debes leerlo tambien en primer lugar.

Mira como leer y escribir un booleano en el Parcelable.

	….
available = in.readByte() != 0;
...
 dest.writeByte((byte) (available ? 1 : 0));
...

Siempre un ejemplo es mejor.


public class Product implements Parcelable {

   private long productId;
   private String name;
   private String description;
   private boolean available;

   public Product() {
   }

   protected Product(Parcel in) {
       productId = in.readLong();
       name = in.readString();
       description = in.readString();
       available = in.readByte() != 0;
   }

   @Override
   public void writeToParcel(Parcel dest, int flags) {
       dest.writeLong(productId);
       dest.writeString(name);
       dest.writeString(description);
       dest.writeByte((byte) (available ? 1 : 0));
   }

   @Override
   public int describeContents() {
       return 0;
   }

   public static final Creator<Product> CREATOR = new Creator<Product>() {
       @Override
       public Product createFromParcel(Parcel in) {
           return new Product(in);
       }

       @Override
       public Product[] newArray(int size) {
           return new Product[size];
       }
   };

Cómo enviar un parámetro Parcelable entre Activities en android

Necesitas declarar un Intent y cargar un ‘extra data’


public class MainActivity extends AppCompatActivity {

   public static final String PRODUCT_KEY = "PRODUCT_KEY";

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       Product product = createProduct();

       Intent intent = new Intent(this, SecondActivity.class);
       intent.putExtra(PRODUCT_KEY, product);

       startActivity(intent);

   }

   // for the purpose of this example
   private Product createProduct() {
       Product product = new Product();
       product.setProductId(123l);
       product.setName("Android");
       product.setDescription("Android Mobile");
       product.setAvailable(true);
       return product;
   }

}

Cómo recibir un parámetro Parcelable entre Activities en android

Aquí necesitas obtener el intent y leer getParcelableExtra()


public class SecondActivity extends AppCompatActivity {

   private TextView productId;
   private TextView productName;
   private TextView productDescription;
   private TextView productAvailable;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_second);

       initializeViews();

       Product product = getIntent().getParcelableExtra(MainActivity.PRODUCT_KEY);
       if(product != null) {
           updateTextViews(product);
       }else {
           Toast.makeText(this, "NO product to show ", Toast.LENGTH_LONG).show();
       }

   }

   private void initializeViews() {
       productId = (TextView) findViewById(R.id.productId);
       productName = (TextView) findViewById(R.id.productName);
       productDescription = (TextView) findViewById(R.id.productDescription);
       productAvailable = (TextView) findViewById(R.id.productAvailable);
   }

   private void updateTextViews(Product product) {
       productId.setText(String.valueOf(product.getProductId()));
       productName.setText(product.getName());
       productDescription.setText(product.getDescription());
       productAvailable.setText(String.valueOf(product.isAvailable()));
   }

}

Referencias
https://developer.android.com/reference/android/os/Parcelable.html
https://developer.android.com/reference/android/os/Parcel.html

Descarga este código completo

Compartir esto:

Java Codigos de Barra con ZXing

El código de barra QR Code (Quick Response Code) es capaz de almacenar muchos más datos en menos tamaño en comparación con otros códigos de barra.

Veamos con un breve ejemplo utilizando ZXing como crear códigos QR. Este ejemplo también te servirá para cualquier otro tipo de código soportado.

Importar dependencia ZXing con maven

<dependencies>
   <dependency>
       <groupId>com.google.zxing</groupId>
       <artifactId>core</artifactId>
       <version>3.3.0</version>
       <type>pom</type>
   </dependency>

   <dependency>
       <groupId>com.google.zxing</groupId>
       <artifactId>javase</artifactId>
       <version>3.3.0</version>
   </dependency>

</dependencies>

Como escribir código QR Code con ZXing

Debes utilizar la clase BitMatrix que recibirá el texto, formato y tamaño de la imagen. Luego te valdrás de MatrixToImageWriter para escribir este BitMatrix en un OutputStream.

private static void writeQR(String text, String pathname) throws WriterException, IOException {

   int width = 600;
   int height = 400;

   String imageFormat = "png"; // "jpeg" "gif" "tiff"

   BitMatrix bitMatrix = new QRCodeWriter().encode(text, BarcodeFormat.QR_CODE, width, height);
   FileOutputStream outputStream = new FileOutputStream(new File(pathname));
   MatrixToImageWriter.writeToStream(bitMatrix, imageFormat, outputStream);

}

Como leer código QR Code con ZXing

Aquí debes utilizar BinaryBitmap y MultiFormatReader, esta última una clase que intentará decodificar el qr code del bitmap (nuestra imagen qr) y te devolverá el string resultante.

private static String readQR(String pathname) throws FormatException, ChecksumException, NotFoundException, IOException {

   InputStream qrInputStream = new FileInputStream(pathname);
   BufferedImage qrBufferedImage = ImageIO.read(qrInputStream);

   LuminanceSource source = new BufferedImageLuminanceSource(qrBufferedImage);
   BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
   Reader reader = new MultiFormatReader();
   Result stringBarCode = reader.decode(bitmap);

   return stringBarCode.getText();
}

Cómo leer y escribir código QR Code

El código completo te queda de esta manera

public class QRCodeExample {

   public static void main(String[] arg) {

       String pathname = "qrcode_example.png";
       String textToQr = "La imaginación es más importante que el conocimiento";

       try {
           writeQR(textToQr, pathname);
           String text = readQR(pathname);
           System.out.print(text);

       } catch (Exception e) {
           e.printStackTrace();
       }
   }

   private static void writeQR(String text, String pathname) throws WriterException, IOException {

       int width = 600;
       int height = 400;

       String imageFormat = "png"; // "jpeg" "gif" "tiff"

       BitMatrix bitMatrix = new QRCodeWriter().encode(text, BarcodeFormat.QR_CODE, width, height);
       FileOutputStream outputStream = new FileOutputStream(new File(pathname));
       MatrixToImageWriter.writeToStream(bitMatrix, imageFormat, outputStream);

   }

   private static String readQR(String pathname) throws FormatException, ChecksumException, NotFoundException, IOException {

       InputStream qrInputStream = new FileInputStream(pathname);
       BufferedImage qrBufferedImage = ImageIO.read(qrInputStream);

       LuminanceSource source = new BufferedImageLuminanceSource(qrBufferedImage);
       BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
       Reader reader = new MultiFormatReader();
       Result stringBarCode = reader.decode(bitmap);

       return stringBarCode.getText();
   }

}

Otros códigos de barra soportados

Viendo el enum BarcodeFormat vemos estos códigos de barra soportados por ZXing

public enum BarcodeFormat {
    AZTEC, CODABAR, CODE_39, CODE_93, CODE_128, DATA_MATRIX, EAN_8, EAN_13, ITF, MAXICODE, PDF_417, QR_CODE, RSS_14, RSS_EXPANDED, UPC_A, UPC_E, UPC_EAN_EXTENSION;
}

Referencias:
https://github.com/zxing/zxing
https://zxing.github.io/zxing/project-info.html

Descarga este código completo

Compartir esto:

Android Tabs con Fragment y RecyclerView

Vamos a crear un ejemplo para este tutorial en el cual nuestro activity tendrá tabs haciendo uso de fragment y este fragment utilizará un RecyclerView.

Vayamos paso por paso:

Crearemos el Activity que definirá los tab pasandole la lista de nuestro modelo a mostrar en cada tab. Después el Fragment contendrá el RecyclerView; entre ambos trabajrán para mostrar cada ítem adecuadamente y devolverán al Activity la acción sobre un item, enviando el modelo sobre el que se hizo click.

Las dependencias que necesitas en tu archivo build

Estas son las dependencias que incluyes

dependencies {
   compile 'com.android.support:appcompat-v7:25.1.1'
   compile 'com.android.support:design:25.1.1'
   compile 'com.android.support:support-v4:25.1.1'
   compile 'com.android.support:recyclerview-v7:25.1.1'
}

El modelo POJO

Este el un modelo bastante tonto pero que nos servirá bien para que crees tu app de ejemplo con tabs usando fragment. Extiende de Parcelable a fin de enviar este modelo al fragment como parámetro.

public class DummyModel implements Parcelable {
    private final String id;
    private final String title;
    private final String detail;
....
}

El activity principal con el TabLayout

Tu Activity principal con dos elementos, el widget TabLayout que provee un layout horizontal para mostrar tabs y el ViewPager que te permitirá desplazarte entre los tabs deslizando con un gesto horizontalmente.

Tu xml debe lucir de este modo para definir el TabLayout y el ViewPager a fin de mostrar tabs.


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/activity_main"
   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"
   tools:context="com.gustavopeiretti.tabexample.TabExampleMainActivity">

   <android.support.design.widget.TabLayout
       android:id="@+id/tab_layout"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:background="?attr/colorPrimary"
       android:elevation="6dp"
       android:minHeight="?attr/actionBarSize"
       android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>


   <android.support.v4.view.ViewPager
       android:id="@+id/pager"
       android:layout_width="match_parent"
       android:layout_height="fill_parent"
       android:layout_below="@id/tab_layout"/>


</RelativeLayout>

Para el TabLayout agregarás en este ejemplo tres tabs con sus respectivos títulos.

TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
tabLayout.addTab(tabLayout.newTab().setText(R.string.title_tab0));
//... otros tabs

Luego para el ViewPager debes agregarle un adapter que se encargará entre otras cosas de devolver el Fragment que corresponda a la posición del tab elegido.

final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
final PagerAdapter adapter = new PagerAdapter(...)
viewPager.setAdapter(adapter);

El Activity también implementará un Listener ItemFragment.OnListFragmentInteractionListener. Verás mas adelante que recibirá la info desde el RecyclerView cuando se haga click sobre un elemento y enviará el Modelo que se ha clickeado al Activity TabExampleMainActivity.

    public void onListFragmentInteraction(DummyModel model) {
        Toast.makeText(TabExampleMainActivity.this, DummyModel.class.getSimpleName() + ":" + model.getId() + " - "  +model.getTitle(), Toast.LENGTH_LONG).show();
    }

Puedes definir otro listener para cuando se pasa de un tab a otro

   private TabLayout.OnTabSelectedListener getOnTabSelectedListener(final ViewPager viewPager) {
        return new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                viewPager.setCurrentItem(tab.getPosition());
                Toast.makeText(TabExampleMainActivity.this, "Tab selected " +  tab.getPosition(), Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
                // nothing now
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {
                // nothing now
            }
        };
    }

El código completo para mostar tab en un Activity te queda así.


public class TabExampleMainActivity extends AppCompatActivity implements ItemFragment.OnListFragmentInteractionListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.tab_example_activity_main);

        // define TabLayout
        TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
        tabLayout.addTab(tabLayout.newTab().setText(R.string.title_tab0));
        tabLayout.addTab(tabLayout.newTab().setText(R.string.title_tab1));
        tabLayout.addTab(tabLayout.newTab().setText(R.string.title_tab2));
        tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);

        // define ViewPager
        final ViewPager viewPager = (ViewPager) findViewById(R.id.pager);

        // fake list model... for test
        DummyModel[] listModel0 = createDummyListModel("tab 0");
        DummyModel[] listModel1 = createDummyListModel("tab 1");
        DummyModel[] listModel2 = createDummyListModel("tab 2");


        //  ViewPager need a PagerAdapter
        final PagerAdapter adapter = new PagerAdapter
                (getSupportFragmentManager(), tabLayout.getTabCount(), listModel0, listModel1, listModel2 );

        viewPager.setAdapter(adapter);

        // Listeners
        viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
        tabLayout.addOnTabSelectedListener(getOnTabSelectedListener(viewPager));

    }

    /**
     * Listener for tab selected
     * @param viewPager
     * @return
     */
    @NonNull
    private TabLayout.OnTabSelectedListener getOnTabSelectedListener(final ViewPager viewPager) {
        return new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                viewPager.setCurrentItem(tab.getPosition());
                Toast.makeText(TabExampleMainActivity.this, "Tab selected " +  tab.getPosition(), Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
                // nothing now
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {
                // nothing now
            }
        };
    }


    /**
     * Listener that comunicate fragment / recycler with this activity
     * @param model
     */
    @Override
    public void onListFragmentInteraction(DummyModel model) {
        // the user clicked on this item over the list
        Toast.makeText(TabExampleMainActivity.this, DummyModel.class.getSimpleName() + ":" + model.getId() + " - "  +model.getTitle(), Toast.LENGTH_LONG).show();
    }


    // model for test purpose
    private DummyModel[] createDummyListModel(String msj) {
        List<DummyModel> l = new ArrayList<>();
        for(int i = 0; i < 20; i++  ) {
            l.add(new DummyModel(String.valueOf(i), "Title " + i , "Descrip. " + i + " -" + msj));
        }
        return l.toArray(new DummyModel[l.size()]);
    }

}

Creas el PagerAdapter

El PagerAdapter debe extender de FragmentStatePagerAdapter. Observa como recibe la info de nuestro modelo para cada tab y como devuelve un fragment para cada posición del tab según su selección.

public class PagerAdapter extends FragmentStatePagerAdapter {

   private int numTabs;
   private DummyModel[] dummyModels0;
   private DummyModel[] dummyModels1;
   private DummyModel[] dummyModels2;

   public PagerAdapter(FragmentManager fm, int numTabs, DummyModel[] dummyModels0,
                       DummyModel[] dummyModels1, DummyModel[] dummyModels2) {
       super(fm);
       this.numTabs = numTabs;
       this.dummyModels0 = dummyModels0;
       this.dummyModels1 = dummyModels1;
       this.dummyModels2 = dummyModels2;
   }

   @Override
   public Fragment getItem(int position) {
       switch (position) {
           case 0:
               ItemFragment tab1 = ItemFragment.newInstance(dummyModels0);
               return tab1;
           case 1:
               ItemFragment tab2 = ItemFragment.newInstance(dummyModels1);
               return tab2;
           case 2:
               ItemFragment tab3 = ItemFragment.newInstance(dummyModels2);
               return tab3;
           default:
               throw new RuntimeException("Tab position invalid " + position);
       }
   }

   @Override
   public int getCount() {
       return numTabs;
   }

}

El fragment que mostrará la información de nuestro modelo

El fragment lo creas con los datos del model que deseas mostrar y harás uso de un RecyclerView a fin de displayarlos luego.


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:orientation="horizontal">

   <TextView
       android:id="@+id/id"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_margin="@dimen/text_margin"
       android:textAppearance="?attr/textAppearanceListItem" />

   <TextView
       android:id="@+id/title"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_margin="@dimen/text_margin"
       android:textAppearance="?attr/textAppearanceListItem" />

   <TextView
       android:id="@+id/detail"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_margin="@dimen/text_margin"
       android:textAppearance="?attr/textAppearanceListItem" />


</LinearLayout>


Como crear RecyclerView

El fragment utilizará un RecyclerView


<android.support.v7.widget.RecyclerView 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:id="@+id/dummyModels"
   android:name="com.gustavopeiretti.tabexample.ItemFragment"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:layout_marginLeft="16dp"
   android:layout_marginRight="16dp"
   app:layoutManager="LinearLayoutManager"
   tools:context="com.gustavopeiretti.tabexample.ItemFragment"
   tools:listitem="@layout/fragment_item" />

Recuerda que antes creaste el PagerAdapter que recibe este array y luego crea las instancias de ItemFragment para cada tab pasandole el respectivo array del modelo a mostrar.

Tu clase ItemFragment recibirá como argumento el array del modelo que deseas mostrar en este fragment y lo dejará como parámetro para cuando se cree el Fragment efectivamente en onCreate().


public static ItemFragment newInstance(DummyModel[] dummyModels) {
   ItemFragment fragment = new ItemFragment();
   Bundle args = new Bundle();
   args.putParcelableArray(KEY_MODEL, dummyModels);
   fragment.setArguments(args);
   return fragment;
}

onCreate() se ejecuta cuando efectivamente se crea el fragment y allí obtendremos la lista que dejamos previamente en el Bundle.


public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   if (getArguments() == null) {
       throw new RuntimeException("You must to send a dummyModels ");
   }
   dummyModels = (DummyModel[]) getArguments().getParcelableArray(KEY_MODEL);

}

onCreateView() se ejecuta cuando se “pinta” la interface, allí debes crear el RecyclerView que usarás definiendo el Adapter y le pasaremos el listener que al final recibirá la acción click sobre el item y la comunicará al Activitity TabExampleMainActivity
Te recomiendo leer luego este otro post a fin de comprender mejor RecyclerView


public View onCreateView(LayoutInflater inflater, ViewGroup container,
                        Bundle savedInstanceState) {
   View view = inflater.inflate(R.layout.fragment_item_list, container, false);

   Context context = view.getContext();
   RecyclerView recyclerView = (RecyclerView) view;
   recyclerView.setLayoutManager(new LinearLayoutManager(context));
   recyclerView.setAdapter(new ItemRecyclerViewAdapter(dummyModels, interactionListener));
   return view;
}


Observa que las Activities (en este ejemplo ‘TabExampleMainActivity’) que usen este Fragment están obligadas a implementar OnListFragmentInteractionListener . Esto es a fin de poder enviarle luego a la Activity el ítem que fue elegido al hacer click. Por lo que TabExampleMainActivity debe implementar este Listener.

Así queda tu clase ItemFragment completa


public class ItemFragment extends Fragment {

   private static final String KEY_MODEL = "KEY_MODEL";

   private DummyModel[] dummyModels;
   private OnListFragmentInteractionListener interactionListener;

   public ItemFragment() {
   }

    /**
     * Receive the model list
     */
   public static ItemFragment newInstance(DummyModel[] dummyModels) {
       ItemFragment fragment = new ItemFragment();
       Bundle args = new Bundle();
       args.putParcelableArray(KEY_MODEL, dummyModels);
       fragment.setArguments(args);
       return fragment;
   }

   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       if (getArguments() == null) {
           throw new RuntimeException("You must to send a dummyModels ");
       }
       dummyModels = (DummyModel[]) getArguments().getParcelableArray(KEY_MODEL);

   }

   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container,
                            Bundle savedInstanceState) {
       View view = inflater.inflate(R.layout.fragment_item_list, container, false);

       Context context = view.getContext();
       RecyclerView recyclerView = (RecyclerView) view;
       recyclerView.setLayoutManager(new LinearLayoutManager(context));
       recyclerView.setAdapter(new ItemRecyclerViewAdapter(dummyModels, interactionListener));
       return view;
   }


   @Override
   public void onAttach(Context context) {
       super.onAttach(context);
       // activity must implement OnListFragmentInteractionListener
       if (context instanceof OnListFragmentInteractionListener) {
           interactionListener = (OnListFragmentInteractionListener) context;
       } else {
           throw new RuntimeException(context.toString()
                   + " must implement OnListFragmentInteractionListener");
       }
   }

   @Override
   public void onDetach() {
       super.onDetach();
       interactionListener = null;
   }

   /**
    * This interface must be implemented by activities that contain this
    * fragment to allow an interaction in this fragment to be communicated
    * <p/>
    */
   public interface OnListFragmentInteractionListener {
       void onListFragmentInteraction(DummyModel item);
   }
}


El RecyclerView.Adapter

Crea esta clase ItemRecyclerViewAdapter que extienda de RecyclerView.Adapter la que será al final la encargada de setear los valores que recibe y de informar haciendo uso del listener ItemFragment.OnListFragmentInteractionListener cuando se haga click sobre un item.


public class ItemRecyclerViewAdapter extends RecyclerView.Adapter<ItemRecyclerViewAdapter.ViewHolder> {

   private final DummyModel[] dummyModels;
   private final ItemFragment.OnListFragmentInteractionListener interactionListener;

   public ItemRecyclerViewAdapter(DummyModel[] items, ItemFragment.OnListFragmentInteractionListener listener) {
       dummyModels = items;
       interactionListener = listener;
   }

   @Override
   public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
       View view = LayoutInflater.from(parent.getContext())
               .inflate(R.layout.fragment_item, parent, false);
       return new ViewHolder(view);
   }

   @Override
   public void onBindViewHolder(final ViewHolder holder, int position) {
       DummyModel dm = dummyModels[position];
       holder.dummyModelItem = dm;
       holder.idView.setText(dm.getId());
       holder.titleView.setText(dm.getTitle());
       holder.detailView.setText(dm.getDetail());

       holder.mView.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               if (null != interactionListener) {
                   // Notify the active callbacks interface (the activity, if the
                   // fragment is attached to one) that an item has been selected.
                   interactionListener.onListFragmentInteraction(holder.dummyModelItem);
               }
           }
       });
   }

   @Override
   public int getItemCount() {
       return dummyModels.length;
   }

   public class ViewHolder extends RecyclerView.ViewHolder {
       public final View mView;
       public final TextView idView;
       public final TextView titleView;
       public final TextView detailView;
       public DummyModel dummyModelItem;

       public ViewHolder(View view) {
           super(view);
           mView = view;
           idView = (TextView) view.findViewById(R.id.id);
           titleView = (TextView) view.findViewById(R.id.title);
           detailView = (TextView) view.findViewById(R.id.detail);
       }
   }
}

Descarga este código completo para visualizar en Android Studio

Compartir esto:

Android RecyclerView ejemplo simple

¿Qué es RecyclerView?

El RecyclerView es una versión más avanzada del tradicional ListView y lo que hace es mostrar datos cuyos elementos se van reciclando cuando ya no son visibles por el scroll de la lista, lo que mejora la performance en gran medida.

¿Como se usa un RecyclerView?

Para usar RecyclerView tienes que definir un adaptador con un LayoutManager.
El adaptador acercará el modelo de datos para ser mostrados y el LayoutManager será el responsable de posicionar cada ítem dentro del RecyclerView y de decidir cuándo reciclar las vistas de items que ya no son visibles.

RecyclerViewFuente Google

Te muestro con un ejemplo como crear un RecyclerView, así es más simple de comprender:

-Debes agregar este depencia en tu build \app\build.gradle

dependencies {
...
   compile 'com.android.support:recyclerview-v7:23.0.0'
...
}

Supongamos que tienes este modelo a mostrar (en extremo simple para el propósito de este ejemplo)

public class UserModel {
...
   private String name;
}

-En el layout de tu activity debes incorporar un RecyclerView

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/activity_main"
   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"
   tools:context="com.gutavopeiretti.recycleviewexample.MainActivity">

   <android.support.v7.widget.RecyclerView
       android:id="@+id/reyclerViewUser"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:clickable="true"
       android:scrollbars="vertical">
   </android.support.v7.widget.RecyclerView>

</RelativeLayout>

Creas otro layout con la info que deseas mostrar en el RecyclerView

Será \res\layout\user_list_row.xml, en donde tendremos un textview para mostrar el nombre y cualquier dato que quiseramos de nuestro modelo si este fuera mas complejo.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent" android:layout_height="wrap_content">

   <TextView
       android:text="TextView"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentTop="true"
       android:layout_alignParentLeft="true"
       android:layout_alignParentStart="true"
       android:layout_marginTop="5dp"
       android:textSize="25dp"
       android:id="@+id/textUserName" />


</RelativeLayout>

Creas el Adapter

El Adapter debe extender de RecyclerView.Adapter para que usara una instancia de RecyclerView.ViewHolder.
Este ViewHolder se encargará de tomar los valores del layout.

onCreateViewHolder() será quien devuelva el ViewHolder con el layout seteado que previamente definimos \res\layout\user_list_row.xml
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.user_list_row, parent, false);

onBindViewHolder() será quien se encargue de establecer los objetos en el ViewHolder

onItemCount() será quien devuelva la cantidad de items que se encuentra en la lista

public class UserAdapter extends RecyclerView.Adapter<UserAdapter.ViewHolder> {

   private List<UserModel> userModelList;

   public UserAdapter(List<UserModel> userModelList) {
       this.userModelList = userModelList;
   }

   @Override
   public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
       View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.user_list_row, parent, false);
       ViewHolder viewHolder = new ViewHolder(v);
       return viewHolder;
   }

   @Override
   public void onBindViewHolder(ViewHolder holder, int position) {
       String name = userModelList.get(position).getName();
       holder.name.setText(name);
   }

   @Override
   public int getItemCount() {
       return userModelList.size();
   }

   public static class ViewHolder extends RecyclerView.ViewHolder {
       private TextView name;
       public ViewHolder(View v) {
           super(v);
           name = (TextView) v.findViewById(R.id.textUserName);
       }
   }

}

-Luego en la clase Activity inicias el RecyclerView

Usamos uno de los layout manager que nos define Android ‘LinearLayoutManager’ y le pasamos el adapter


protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);

   reyclerViewUser = (RecyclerView) findViewById(R.id.reyclerViewUser);

   // use this setting to improve performance if you know that changes
   // in content do not change the layout size of the RecyclerView
   reyclerViewUser.setHasFixedSize(true);

   // use a linear layout manager
   reyclerViewUser.setLayoutManager(new LinearLayoutManager(this));

   // specify an adapter with the list to show
   mAdapter = new UserAdapter(getData());
   reyclerViewUser.setAdapter(mAdapter);

}

Descarga este código completo para visualizar en Android Studio

Compartir esto:

Android Cómo crear una secuencia de imágenes simple

Puedes crear animaciones simples con una serie de imágenes en secuencia utilizando ImageView en conjunto con AnimationDrawable.

La forma más sencilla de crear una animación por cuadro es definir la animación en un archivo XML, en la carpeta /res/drawable/ y establecerla como fondo de un ImageView para luego iniciar la secuencia.

Lo que tienes que hacer es definir un ImageView al cual le agregas un background resource (setBackgroundResource). Este resource será un xml con la serie de imágenes a mostrar en secuencia.
Luego tomaremos ese brackground en un AnimationDrawable para iniciar la secuencia de imágenes.

Cómo definir definir ImageView y AnimationDrawable

Defines tu ImageView como siempre y luego en tu código se seteas el xml que estable las imágenes a mostrar.


<ImageView
   android:id="@+id/imageView"
   android:layout_width="200dp"
   android:layout_height="200dp"
   android:src="@drawable/anim0"
   android:layout_centerVertical="true"
   android:layout_centerHorizontal="true" />


imageView = (ImageView) findViewById(R.id.imageView);

// set the xml with images
imageView.setBackgroundResource(R.drawable.animation);
// get the background to show the animation 
frameAnimation = (AnimationDrawable) imageView.getBackground();

Cómo definir tu xml para las animaciones

Dentro de la carpeta drawable debes crear un xml, en este caso se llamará \res\drawable\animation.xml

En el xml puedes observar que estableces la duración también de cada cada imagen, podría ser distinta según como lo necesites.

Este xml fue el que seteaste en el Background Resource del ImageView
imageView.setBackgroundResource(R.drawable.animation);

<?xml version="1.0" encoding="utf-8"?>
<animation-list android:id="@+id/selected"
   android:oneshot="false"
   xmlns:android="http://schemas.android.com/apk/res/android">
   <item android:drawable="@drawable/anim0" android:duration="1000" />
   <item android:drawable="@drawable/anim1" android:duration="1000" />
   <item android:drawable="@drawable/anim2" android:duration="1000" />
   <item android:drawable="@drawable/anim3" android:duration="1000" />
   <item android:drawable="@drawable/anim4" android:duration="1000" />
   <item android:drawable="@drawable/anim5" android:duration="1000" />
   <item android:drawable="@drawable/anim6" android:duration="1000" />
   <item android:drawable="@drawable/anim7" android:duration="1000" />
   <item android:drawable="@drawable/anim8" android:duration="1000" />
</animation-list>

Iniciar o parar la animación AnimationDrawable

Tan simple como start() o si quisieramos detenerla .stop()


// start animation
frameAnimation.start();

// stop animation
if(frameAnimation.isRunning()) {
    frameAnimation.stop();
    // set first image (optional)
    frameAnimation.selectDrawable(0);
}

El código para crear una secuencia de imágenes con AnimationDrawable te queda así


public class MainActivity extends AppCompatActivity {

    private ImageView imageView;
    private AnimationDrawable frameAnimation;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = (ImageView) findViewById(R.id.imageView);
        imageView.setImageResource(0);

        // set the xml with images
        imageView.setBackgroundResource(R.drawable.animation);
        // get the background to show the animation
        frameAnimation = (AnimationDrawable) imageView.getBackground();

    }

    // onclick button start in activity xml
    public void start(View view) {
        // start animation
        frameAnimation.start();
    }

    // onclick button stop in activity xml
    public void stop(View view) {
        // stop animation
        if(frameAnimation.isRunning()) {
            frameAnimation.stop();
            // set first image (optional)
            frameAnimation.selectDrawable(0);
        }
    }

}

Descarga este código completo para visualizar en Android Studio

Compartir esto:

Android Enviar parametros entre Activities

A fin de enviar parámetros entre una Activity debes utilizar la clase Intent que te sirve para solicitar acciones de tu aplicación. Intent describe la Actividad que quieres abrir y los parámetros que deseas enviarle.

Android Enviar Parametros entre Activities

Android Enviar Parametros entre Activities

Cómo enviar parámetros a una Activity:

Para iniciar una Activity debemos crear un intent con el contexto y la class de la segunda Activity.
Luego al intent le seteas los parámetros con key_del_parametro / valor_del_parametro

	...
	Intent intent = new Intent(getApplicationContext(), SecondActivity.class);
	intent.putExtra("DATA_NAME_KEY", name.getText().toString());
	intent.putExtra("DATA_LASTNAME_KEY", lastname.getText().toString());	
	...	
 

Cómo recibir parámetros desde una Activity:

Los parámetros los recibes en el método “onCreate” que inicializa tu actividad dentro del parámetro Bundle

        if (bundle != null) {
            String nameParam = bundle.getString("DATA_NAME_KEY");
            String lastNameParam = bundle.getString("DATA_LASTNAME_KEY");
            textViewName.setText(nameParam);
            textViewLastname.setText(lastNameParam);
       }
 

Una forma corta que también puedes utilizar

	getIntent().getStringExtra("DATA_NAME_KEY")

El código completo te queda de este modo

En este ejemplo hay dos activities, la primera pide dos datos y los pasa a la segunda que los muestra.


public class MainActivity extends AppCompatActivity {

    private EditText name;
    private EditText lastname;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        name = (EditText)  findViewById(R.id.name);
        lastname = (EditText)  findViewById(R.id.lastname);

    }

    public void sendParam(View view) {
        Intent intent = new Intent(getApplicationContext(), SecondActivity.class);
        intent.putExtra("DATA_NAME_KEY", name.getText().toString());
        intent.putExtra("DATA_LASTNAME_KEY", lastname.getText().toString());
        startActivity(intent);
    }
}

public class SecondActivity extends AppCompatActivity {

    private TextView textViewName;
    private TextView textViewLastname;

    @Override
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.activity_second);

        textViewName = (TextView) findViewById(R.id.textViewName);
        textViewLastname = (TextView) findViewById(R.id.textViewLastname);

        if (bundle != null) {
            String nameParam = bundle.getString("DATA_NAME_KEY");
            String lastNameParam = bundle.getString("DATA_LASTNAME_KEY");
            textViewName.setText(nameParam);
            textViewLastname.setText(lastNameParam);
        }

        // short way to get intent paramts
        // textViewName.setText(getIntent().getStringExtra("DATA_NAME_KEY"));
        // textViewLastname.setText(getIntent().getStringExtra("DATA_LASTNAME_KEY"));

    }
}

Descarga este código completo para visualizar en Android Studio

Compartir esto:

Android – Como descomprimir un archivo zip

En este ejemplo veremos como descomprimir un archivo zip en Android.

La descompresión de archivos zip en Android no es diferente a la descompresión que habitualmente realizamos con cualquier aplicación en Java. Solo hay que tener cuidado en ubicar las carpetas origen del archivo y destino para la descompresión y en solicitar los permisos necesarios.

En este ejemplo suponemos que nuestro archivo zip estará ubicado en la carpeta publica Download y que queremos descomprimir su contenido en la carpeta privada de Documents de tu aplicación.

Cómo acceder a la carpeta pública download y a la carpeta privada ‘documents’ de la aplicación

  • Carpeta Publica getExternalStoragePublicDirectory(carpeta_tipo)
  • Carpeta Privada de tu app getExternalFilesDir(carpeta_tipo)

Nos queda de este modo:


// archivo zip en la carpeta download del sistema
File downloadFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
File zipFile = new File(downloadFolder, FILE_TEST_ZIP);

// carpeta privada de la app en unidad externa (tarjeta de memoria) dentro 'documents'
File externalFilesDir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);

Usamos ZipInputStream para leer el archivo zip y recorrerlo

Luego usaremos una implementación de InputStream ‘ZipInputStream’ para leer el archivo que nos devuelve sucesivamente una entrada del zip


ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFile));
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {...}

Permisos necesarios para leer y escribir

Para leer y escribir archivos en el almacenamiento externo, tu app debe obtener los permisos de sistema del READ_EXTERNAL_STORAGE o WRITE_EXTERNAL_STORAGE.

A partir de Android 4.4, estos permisos no son necesarios si deseas leer o escribir archivos que solamente son privados para tu app.

A partir de la targetSdkVersion 23 Android 6.0 (API level 23) los permisos deben ser requeridos por nosotros en runtime, en este caso estamos queriendo leer un carpeta del sistema ‘download’ por lo que necesitamos pedirlos en runtime a pesar de que esten declarados en el archivo AndroidManifiest.

Los permisos de WRITE suponen tambien los permisos para READ. A fines de ejemplificar pedimos ambos:


    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Solicitud de permisos en runtime para versiones Android 6 y posteriores:


        // verificar si tengo permisos sobre el storage y lo requiero si no lo tengo
        int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);

        if (permission != PackageManager.PERMISSION_GRANTED) {
            // no tenemos permisos... lo pedimos
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    REQUEST_PERMISSION_CODE);
        } else {
           // ok process ....
        }

También es muy conveniente que verifiques antes de escribir si el almacenamiento esta disponible. Te dejo este post en el que te explico como verificar si el almacenamiento externo esta disponible

Veamos como queda el código completo



public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getName();
    private static final String FILE_TEST_ZIP = "fileTest.zip";
    private static final int REQUEST_PERMISSION_CODE = 5656;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void unzip(View view) {

        verifyStoragePermissions();

    }

    private void processZip() {

        // Zip file in the system download folder
        File downloadFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
        File zipFile = new File(downloadFolder, FILE_TEST_ZIP);

        // Private app folder on external drive (memory card) inside 'documents'
        File externalFilesDir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);

        int countFiles = unZip(zipFile, externalFilesDir);

        Toast.makeText(this,"Files unziped " + countFiles, Toast.LENGTH_LONG).show();

    }


    private int unZip(File zipFile, File folderToExtract) {

        if(!zipFile.exists()) {
            Toast.makeText(this,"Zip do not exist ", Toast.LENGTH_SHORT).show();
            return 0;
        }

        try  {

            int totalFilesInZip = countFiles(zipFile);
            int countFiles = 0;

            ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFile));
            ZipEntry zipEntry;
            byte[] buffer = new byte[1024];
            int count;
            while ((zipEntry = zipInputStream.getNextEntry()) != null) {

                if(zipEntry.isDirectory()) {

                    File f = new File(folderToExtract.getPath() + zipEntry.getName());
                    if(!f.isDirectory()) {
                        f.mkdirs();
                    }

                } else {

                    ++countFiles;
                    Log.v(TAG, "Unzipping " + " File " + countFiles + "/" + totalFilesInZip + " Name: " + zipEntry.getName());

                    File fileOut = new File(folderToExtract, zipEntry.getName());
                    FileOutputStream fileOutputStream = new FileOutputStream(fileOut);
                    while ((count = zipInputStream.read(buffer)) != -1) {
                        fileOutputStream.write(buffer, 0, count);
                    }

                    zipInputStream.closeEntry();
                    fileOutputStream.close();
                }

            }
            zipInputStream.close();

            return countFiles;

        } catch(Exception e) {
            Log.e(TAG, "Unzip file " + zipFile.getName(), e);
            return 0;
        }

    }

    // to show progress
    private int countFiles(File zipFile) throws IOException {
        ZipFile xzipFile = new ZipFile(zipFile);
        final Enumeration&lt;? extends ZipEntry&gt; entries = xzipFile.entries();
        int numRegularFiles = 0;
        while (entries.hasMoreElements()) {
            if (! entries.nextElement().isDirectory()) {
                ++numRegularFiles;
            }
        }
        return numRegularFiles;
    }


    // verify permission
    private void verifyStoragePermissions() {

        // Check if we have write permission
        int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);

        if (permission != PackageManager.PERMISSION_GRANTED) {
            // We don't have permission so prompt the user
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    REQUEST_PERMISSION_CODE);
        } else {

            processZip();

        }
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case REQUEST_PERMISSION_CODE: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length &gt; 0 &amp;&amp; grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    // permission was granted! Do the work unzip!!
                    processZip();

                } else {
                    Toast.makeText(this, "I can't unzip. Please give me permissions", Toast.LENGTH_LONG).show();
                }
                return;
            }
        }
    }

}

Descarga este código completo

Compartir esto:

Android – Como mostrar una barra de progreso con ProgressDialog

ProgressDialog es un diálogo que nos ayudará a mostrar un indicador de progreso en nuestra app de modo muy simple.

Esta barra de progreso resultará muy útil cuando relicemos procesos que pueden demorar cierto tiempo y deseamos mostrar al usuario de nuestra aplicación como va este proceso. Evitará la ansiedad por parte de nuestro usuario sabiendo cómo avanza el resultado.
progress_dialog_2

Inicializar ProgressDialog

Para inicializar ProgressDialog simplemente necesitamos un mensaje (opcional), el valor máximo, el estilo…


ProgressDialog progressDialog;

...
progressDialog = new ProgressDialog(MainActivity.this);
progressDialog.setMessage("Procesando....");
progressDialog.setIndeterminate(false);
progressDialog.setMax(100);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setCancelable(true);

El valor progressDialog.setIndeterminate(boolean); en este caso no aplica porque conocemos el estado del progreso. Sin embargo podríamos utilizarlo en true si no conociéramos el avance del proceso y solo quisiéramos mostrar una barra moviéndose

Actualizar el progreso de ProgressDialog

Para esto ejecutaremos nuestra tarea de forma Asíncrona valiéndonos de AsyncTask.
AsyncTask define tres métodos onPreExecute, onProgressUpdate, onPostExecute . Usaremos para iniciar la barra, actualizar el progreso y hacer ‘algo’ al finalizar.

Simularemos dentro de doInBackground un proceso que demora cierto tiempo.

La clave para actualizar el progreso esta en publishProgress(i) que envia un valor de progreso que será recibido en onProgressUpdate(Integer… progress) para que allí podamos updatear nuestra barra progressDialog.setProgress(progress[0]);

Vemos tambien que podemos recibir un status al final de la ejecución y hacer algo con esto en onPostExecute(String result)


private class HardTask extends AsyncTask<String, Integer, String> {

   private Context context;

   public HardTask(Context context) {
       this.context = context;
   }

   @Override
   protected String doInBackground(String... params) {
       for (int i = 0; i <= 200; i++) {
           publishProgress(i);
           try {
               // simulate hard work
               Thread.sleep(100);
           } catch (InterruptedException e) {
               e.printStackTrace();
               return PROCESS_ERROR;
           }
       }
       return PROCESS_OK;
   }

   @Override
   protected void onPreExecute() {
       super.onPreExecute();
       progressDialog.show();
   }

   @Override
   protected void onProgressUpdate(Integer... progress) {
       super.onProgressUpdate(progress);
       progressDialog.setProgress(progress[0]);
   }

   @Override
   protected void onPostExecute(String result) {
       progressDialog.dismiss();
       if (PROCESS_OK.equals(result)) {
           Toast.makeText(context, "Process OK " + result, Toast.LENGTH_LONG).show();
       } else {
           Toast.makeText(context, "Process ERROR " + result, Toast.LENGTH_LONG).show();
       }
   }
}

De este modo queda nuestro ProgressDialog


public class MainActivity extends AppCompatActivity {

    private static final String PROCESS_OK = "PROCESS_OK";
    private static final String PROCESS_ERROR = "PROCESS_ERROR";
    private ProgressDialog progressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        progressDialog = new ProgressDialog(MainActivity.this);
        progressDialog.setMessage("Procesando....");
                progressDialog.setMax(100);
        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        progressDialog.setCancelable(true);

    }


    public void runProgressBar(View view) {

        final HardTask downloadTask = new HardTask(MainActivity.this);
        downloadTask.execute("some_param");

        progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                downloadTask.cancel(true);
            }
        });
    }


    private class HardTask extends AsyncTask<String, Integer, String> {

        private Context context;

        public HardTask(Context context) {
            this.context = context;
        }

        @Override
        protected String doInBackground(String... params) {
            for (int i = 0; i <= 200; i++) {
                publishProgress(i);
                try {
                    // simulate hard work
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    return PROCESS_ERROR;
                }
            }
            return PROCESS_OK;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            progressDialog.show();
        }

        @Override
        protected void onProgressUpdate(Integer... progress) {
            super.onProgressUpdate(progress);
            progressDialog.setProgress(progress[0]);
        }

        @Override
        protected void onPostExecute(String result) {
            progressDialog.dismiss();
            if (PROCESS_OK.equals(result)) {
                Toast.makeText(context, "Process OK " + result, Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(context, "Process ERROR " + result, Toast.LENGTH_LONG).show();
            }
        }
    }

}

Descarga este código completo

Compartir esto: