Author Archives: Gustavo

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:

Android – Como descargar archivos usando DownloadManager

DownloadManager es servicio de Android que nos facilita las descargar de archivos via HTTP, en especial cuando estos son archivos que pueden demorar en descargar.
DownloadManager gestionará la descarga en segundo plano, cuidando los estados de conectividad del sistema.

downloadmanagerexample

Veremos con un ejemplo sencillo la utilización de esta clase para entender su funcionalidad y lo simple que resulta su utilización.

Obtener una instancia de DownloadManager

Para obtener una instancia de este servicio debemos simplemente solicitarla a través de getSystemService(..) de este modo

...
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
...

Definir el Request a la url

Primero necesitamos definir la url sobre la cual deseamos realizar la descarga y el destino

...
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setDescription("Downloading file " + fileName);
request.setTitle("Downloading");
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
...

Requerir la URL

Luego pasamos ese request al DownloadManager

...
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
...

Verificar cuando ha terminado la descarga con BroadcastReceiver

A fin de verificar el momento en el que DownloadManager ha terminado la descarga de nuestro archivo debemos registrar un BroadcastReceiver de este modo

...
registerReceiver(new DonwloadCompleteReceiver(), new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));

// 
public class DonwloadCompleteReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if(DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)){
            Toast.makeText(context,"Download completed", Toast.LENGTH_LONG).show();
            // DO SOMETHING WITH THIS FILE
        }
    }
}
...

Permisos

Debemos tener en cuenta que necesitaremos permisos para INTERNET y para WRITE_EXTERNAL_STORAGE

De este modo quedaría nuestra Activity de ejemplo completa

public class MainActivity extends AppCompatActivity {

    public static final String URL_TO_DOWNLOAD = "https://upload.wikimedia.org/wikipedia/commons/0/0e/Googleplex-Patio-Aug-2014.JPG";
    private static final short REQUEST_CODE = 6545;
    public static final String NAME_FILE = "googleplex.jpg";

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

    }


    public void download(View view) {
        if (isDownloadManagerAvailable()) {
            checkSelfPermission();
        } else {
            Toast.makeText(this, "Download manager is not available", Toast.LENGTH_LONG).show();
        }

    }

    private static boolean isDownloadManagerAvailable() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
            return true;
        }
        return false;
    }


    private void checkSelfPermission() {

        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {

            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    REQUEST_CODE);

        } else {

            executeDownload();

        }
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CODE: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // permission was granted! Do the work
                    executeDownload();
                } else {
                    // permission denied!
                    Toast.makeText(this, "Please give permissions ", Toast.LENGTH_LONG).show();
                }
                return;
            }
        }
    }

    private void executeDownload() {

        // registrer receiver in order to verify when download is complete
        registerReceiver(new DonwloadCompleteReceiver(), new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));

        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(URL_TO_DOWNLOAD));
        request.setDescription("Downloading file " + NAME_FILE);
        request.setTitle("Downloading");
        // in order for this if to run, you must use the android 3.2 to compile your app
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            request.allowScanningByMediaScanner();
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        }
        request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, NAME_FILE);

        // get download service and enqueue file
        DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
        manager.enqueue(request);



    }

}

Nuestro BroadcastReceiver que recibirá el resultado

...
public class DonwloadCompleteReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if(DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)){
            Toast.makeText(context,"Download completed", Toast.LENGTH_LONG).show();
            // DO SOMETHING WITH THIS FILE
        }
    }
}

Descarga este codigo completo

Referencias:
DownloadManager.Request
DownloadManager

Compartir esto:

Stand Up Meeting – ¿Es realmente ágil?

La empresa tradicional está cambiando y con ella la metodología usada para resolver conflictos o  llevar a cabos proyectos. El mercado se ha saturado con términos como Coaching, Design Thinking, Action Learning y muchos más, está cambiando la manera en la que se hacen los procesos internos de una empresa, los procesos de creación y productividad.

En el universo del desarrollo de software o en los procesos creativos de diseños en ocasiones seguir los procesos tradicionales pueden retrasar la culminación exitosa de un proyecto. Todo lo mencionado anteriormente ocasiona el nacimiento de La Metodología AGILE, un concepto en crecimiento que ha dado de que hablar desde el año 2000 en adelante.

Muchos desarrolladores de software hablan de esta metodología y hay opiniones contradictorias; a muchos les funcionan a otros no tanto, en el post de hoy profundizaremos un poco en este particular en las ventajas y desventajas de la metodología AGILE y sus herramientas, en específico trataremos de ahondar en la “Stand Up” Meeting.

¿Qué es la METODOLOGÍA AGILE?

La Metodología AGILE es una serie de estrategias, un sistema diferente de llevar a cabo un proyecto de manera más rápida y efectiva, centrándose en: el hacer y aprender sobre la marcha, en vez de horas de planeamiento se invierte poco tiempo a esto y se distribuye el trabajo en equipos efectivos. Personalmente creo que la eficacia de los métodos ágiles recae principalmente en el tipo de integrantes de tu equipo.

Para que un equipo realmente funcione es primordial el compromiso e interes con el proyecto en el cual participan. Esto que parece obvio en algunos casos no lo es sobre todo en estructuras bajo exceso de control o verticalistas. Solo en en proyectos con team comprometidos es en dónde va a funcionar mejor un equipo Agile.

Stand Up Meeting, una estrategía para descubrir obstáculos

Dentro de las herramientas que usa la metodología Agile se cuenta la Stand Up Meeting, unas de las más importantes e influyentes de los procesos ágiles.
En esa reunión cada participante debe contestar tres preguntas básicas: ¿Qué se hizo? ¿Qué voy a hacer hoy? ¿Qué impedimentos puedo tener para cumplir con la tarea?
Por lo que es una reunión de sincronización diaria del equipo pero además y muy importante,  pretende eliminar cualquier obstaculo con tiempo, dejándolo a la vista para ser resuelto con prioridad luego de la reunión

¿Cómo Funciona el Stand Up Meeting?

El Stand Up Meeting es un proceso simple con reglas básicas que no deben romperse, una norma muy estricta es su duración; independientemente de la cantidad de integrantes que involucre el proyecto esa reunión debe durar sólo 15 minutos, no más no menos. Que se busca con esto invertir el tiempo de manera efectiva, las reuniones que se eternizan por horas interminables normalmente son causa de estrés y fastidio entre los trabajadores o desarrolladores de software.

Básicamente durante la Stand Up Meeting los integrantes del grupo de trabajo, estando de pie se responden tres preguntas básicas:

  • ¿Qué has hecho desde ayer?
  • ¿Qué es lo que haré hoy?
  • ¿Has tenido algún problema que te haya impedido alcanzar tu objetivo?

La reunión está presidida por un ScrumMaster que es el encargado de recordar y analizar las respuestas del equipo. Adicional una Stand Up Meeting debe cumplir los siguientes requisitos:

  1. La reunión comienza puntualmente a la hora prevista.
  2. Las personas que participan en el proyecto pueden hablar, los demás pueden ser observadores pero en general no tienen participación.
  3. Siempre dura 15 minutos; no importa la cantidad de personas que integren el grupo de trabajo. Los grupos de trabajo Agile deberían ser pequeños.
  4. La reunión siempre se hace en el mismo sitio, a la misma hora de manera puntual.

La pregunta clave: ¿Funciona la Stand Up Meeting?

Pues esto es un punto que analizar; si se usa la metodología Agile específicamente Scrum que es donde pertenece la estrategia Stand Up Meeting siguiendo las normas estrictamente se puede lograr un proyecto exitoso. Pero como se nombra anteriormente el proceso debe seguir las reglas pues muchos se quejan de que la metodología Agile no es más que un fraude y se tiene a un ScrumMaster pendiente todo el día, no hay planeamiento y se sienten sin guía, están expuestos a que el proyecto cambie de dirección a diario; es cierto, la Metodología Agile no siempre es para todos.

Por otra parte muchos mencionan lo bien que se les da el proyecto y lo rápido que sacan este trabajo por medio de la metodología, saber a ciencia cierta si esa metodología es correcta para tu equipo es imposible; la mejor manera de detectar su efectividad es poniéndola en práctica sin embargo te nombro las ventajas y desventajas del método ágil y específicamente del Stand Up.

Ventajas y desventaja de la Metodología AGILE

Personalmente creo que las metodologías ágiles llámense  de diversas maneras o sean una variantes de las mismas son necesarias para una empresa de tecnología. Si trabajas con tecnología, diseño no puedes seguir los cánones del método tradicional que incluyen meses de planeamiento, montón de papeleo y resultados lentos. El cliente quiere rapidez, agilidad, talento e innovación con los métodos ágiles es lo que se busca.

Apuesto 100% a los métodos ágiles pero que sean adaptados a tus necesidades y las de tus consumidores.

Muchas empresas tienen sus propios tableros y métodos el que casi nunca varia es el Stand Up Meeting; económico no representa ningún costo de tiempo ni inversión a la empresa y permite mantener al día a todos con el proyecto y avanzar rápidamente a su culminación con éxito.

Metodología AGILE para todo

Al ver lo beneficioso que resultaba la implementación de la metodología AGILE en el desarrollo de software y otros campos tecnológicos muchos gerentes y líderes de equipo la imparten en su empresa para sacar de manera más rápida y eficiente los proyectos. Como bien dice su nombre la metodología AGILE; valga la redundancia es ágil, es rápida, busca efectividad y ahorro de tiempo y dinero.

En países como Japón, Estados Unidos es un concepto muy usado sin embargo en Latinoamérica se ha quedado un poco atrás, sin embargo ya muchas empresas sobre todo empresas de software lo tienen implementado.

Como último destacó que contra los métodos ortodoxos tiene mejor funcionamiento la metodología ágil, sin embargo hay equipos de trabajo que no están preparados para funcionar bajo esta por lo tanto una fusión siempre es lo mejor.

Hay equipos que dan lo mejor de sí y otros que deben ser supervisados constantemente, la implementación de metodologías que le dan mayor libertad al empleado pero que exigen un alto compromiso no sirven para aquellos que simplemente van por el salario.

Para que una metodología tenga éxito debes tener un equipo de trabajo comprometido.
Y tú qué piensa acerca de la metodología ágil…

Compartir esto:

El Perfeccionismo anti productivo

Pasas horas o días haciendo algo pero parece que no avanzas o nunca lo terminas. ¿Te pasas mejorando los detalles y siempre sientes que hay algo mas para arreglar?

Cuando el perfeccionismo se vuelve anti productivo y paralizante

El perfeccionismo puede llegar a ser un enemigo, suena extraño este concepto pero es cierto; a veces nos enfrascamos en pequeños detalles, en la perfección de nimiedades que en realidad no serán visibles en la calidad final del trabajo o no tendrán tanta repercusión, simplemente nos obsesionamos con un ideal de perfección que paraliza nuestro trabajo, no permitiéndonos avanzar en los plazos establecidos y frustrándonos constantemente.

Si, aunque suene poco lógico el Perfeccionismo puede llegar a ser muy improductivo, es más cada día la perfección parecen más un defecto que una virtud, si se busca la perfección se evita actuar en pro a lo que exija el momento, se actúa en base a prejuicios establecidos de lo que es perfecto y lo que no, y en un mundo de particularidades y singularidades no todos tenemos la misma idea de perfección, a la hora de emprender se tiene que ser más ‘HACEDOR’ que ‘PENSADOR’.

Es mejor HECHO que PERFECTO.

Obviamente no se trata de hacer por hacer, ni de hacer las cosas mal. Hay que dar lo mejor en cada cosa que hacemos pero sabiendo que cometemos errores y que aprendemos de ello.

El nuevo concepto de equipos ‘hacedores’ o ‘markers’

En esta nueva era de cambios donde los empleos rutinarios, con horarios establecidos están siendo reemplazados por colaboraciones freelance y organizaciones más horizontales y flexibles; la globalización de los servicios y productos nos están abriendo el paso a un universo de cambio de lo que se conoce como la empresa tradicional a algo más digital, una nueva generación de emprendedores está creciendo y suplantando el viejo formato de trabajo.

HACER – APRENDER – MEJORAR

Se trata de HACER rápidamente, buscando resolver problemas o necesidades de las personas. El hacedor entusiasta que aprende haciendo apoyándose mas en el sentido común y en el feedback de lo hecho sobresale hoy por sobre el perfeccionista.

Lo vemos en gran cantidad de productos lanzados con el sentido minimalista a diario y que tienen gran acogida en el mundo. Estos productos van aprendiendo de sus mismos usuarios y mejorando con la experiencia. NO se han obsesionado en la perfección del proceso sino en la productividad de los resultados, quizás fallen pero de esas fallas aprenderán y mejoraran, y seguramente surgirán nuevas ideas…

primer-boceto-Twitter

Primer Boceto Twitter

 

Un pensamiento de diseño, el ‘Design Thinking’ se abre paso

Otro concepto que está revocando el perfeccionismo del método y los detalles es el DESING THINKING o lo mejor conocido como el PENSAMIENTO DE DISEÑO.

Básicamente este concepto nos habla de crear a partir de la particularidad, no hay base de dato correcta para lograr el cometido ni lineamientos establecidos y perfectos; NO, no se trata de perfección sino de creación e innovación. Cuando te juzgas y exiges la perfección de un proceso dejas de diseñar tu aprendizaje e innovar y te vuelves un esclavo del prejuicio que tienes establecido acerca de lo perfecto.

desing-thinking

OLVIDA LA PERFECCION. Resulta que esta tiene muy buena publicidad pero el perfeccionista que hoy lee este post sabe que se enfrenta a muchas dificultades por esa manía, a veces destructiva, de juzgarse con una vara muy alta en base a sus conceptos particulares de perfección, que lo hacen rehacer un trabajo una y otra vez quitándole tiempo valioso que puede dedicar en cosas realmente importante.

Utilizar el PENSAMIENTO DE DISEÑO (Design Thinking) como forma de actuar ayuda a resolver y hacer. Piensa como diseñador créate para cada proyecto tu método, no solo aprenderás más sino disfrutaras enormemente el proceso hasta que consigas el método ideal para ti.

La Ley de Pareto. Simplemente identifica lo importante

La ley de Pareto establece que un 20% es el que ocasiona el 80% restante, si haces bien el 20 probablemente el 80 restante tome forma, claro estas matemáticas cambia según cada asunto o proyecto pero básicamente nos indica; IDENTIFICA EL ITEM CON PESO Y TRABAJA EN EL, EL RESTO TOMARA FORMA ESPONTANEAMENTE, no te enfrasques en detalles pequeños, ni en los asuntos que roban mucho tiempo pero que no repercuten en tu proyecto identifica la porción que hará la gran diferencia y comienza, si queda tiempo afina los demás detalles, pero si no tienes el plazo trabajaste en el ítem más importante básicamente le dará forma total a tu proyecto.

TIPS para el Perfeccionista que desea ser productivo

-Evita la parálisis por análisis. Decide con lo que tienes.
-Aplica la ley de Pareto e identifica el 20% importante que darán lo mejor en el resultado final. ¿Cuales son las tareas mas importantes?
-Aplica la ley de parkinson. Ponte fechas limites. La ley de Parkinson dice que la complejidad de una tarea se expande hasta ocupar el tiempo disponible para realizarla. Tu mente se adapta al tiempo disponible, por lo reducir el tiempo que dispones para realizar esa tarea te obligará a simplificar, descartando aquello que no es importantes realmente. Obliga a tu mente a concentrarse.

Quieres ser productivo, DEJA DE SER PERFECCIONISTA, esto no quiere decir que hagas un trabajo mal hecho, que hagas lo primero que se te venga en mente y listo, no, esto quiere decir que no te obsesiones en ser perfecto, PRIORIZA EL HACER, la perfección es solo una manera amable de auto juzgarte que te hace daño, normalmente te ocasiona estrés y frustración, sientes que no avanza, si un detalle te quita mucho tiempo déjalo ya empieza con otra cosas que tenga mayor relevancia y luego vuelve a él veras que se te hace más fácil, pues al estar todo conectado en un proyecto y terminar la parte relevante se te hará más fácil afinar los detalles, no te exijas ni trates mal pensando cosas como: NO SOY PERFECTO, LO ESTOY HACIENDO MAL, NO PUEDO CON ESTO solo empeoraras tu trabajo EL PODER DE TU MENTE SOBRE TUS ACCIONES ES INMENSO, si empieza saboteándose simplemente solo te enfermaras y no darás lo mejor de ti, el mejor trabajo es el que se hace a gusto.

perfect

Siempre aprende, no te limites con la ‘PERFECCIÓN’ y los prejuicios

Cuando te limitas a hacer un trabajo o un proyecto de una manera específica, sin darte permiso de intentar con otras forma simplemente haces eso LIMITARTE con tu ideal de perfección que quizás no está ajustado a lo que realizas, en dicho caso dejas de aprender y repites métodos “perfectos” una y otra vez, los emprendedores más grande de la historia nunca se limitaron a un método innovaron y como lo hicieron SIMPLEMENTE ACTUADO, errando y volviendo a fallar las más grandes victorias e invento de la historia nacieron de un fallo, APRENDE SIEMPRE no te quedes con lo que diga un libro intenta tu técnica puedes que rediseñes un concepto preestablecido, si acatas tus normas de perfección simplemente el proceso creativo muere y tu dejas de aprender, así que aprende más y deja de lado los prejuicios establecidos, veras como pronto eres más productivo, creativo y feliz…

Compartir esto:

Android – Como reducir y ofuscar tu apk

Para lograr reducir el tamaño de tu aplicación Android debes tener en cuenta varios factores, como el tamaño de las imágenes, la calidad de tu código, archivos y demás recursos estáticos que puedas estar utilizando. Sumado a esto Android te provee un método simple para habilitar la reducción de código utilizando un archivo ProGuard.

Sabemos que siempre debemos considerar el uso de la memoria interna en los dispositivos y aquí ProGuard nos ayudará en dicha tarea eliminando clases que no se utilizan, optimizando librerías externas o eliminándolas si no son utilizadas.

Algo muy bueno en este trabajo de ProGuard es la ofuscación de nuestro código ganando bytes a nuestro favor, ya que cambia y reduce nombres de variables, métodos, clases lo que en en aplicaciones medianas a grandes puede significar un ahorro significativo en el tamaño del apk. Además dificultará la ingeniería inversa aumentando la seguridad de tu aplicación.

Como activar la reducción de código y ofuscación en Android con ProGuard

Para activar la reducción de código en Android utilizando Proguard debes modificar tu archivo build.gradle activando minifyEnabled con el valor true

android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile(‘proguard-android.txt'),
                    'proguard-rules.pro'
        }
    }
    ...
}

Activando esta propiedad minifyEnabled, se utilizará el archivo proguard-android.txt por default que se encuentra en tu SDK dentro de la carpeta SDK/tools/proguard.

Mi recomendación es que pruebes sin realizar modificaciones en este archivo proguard-android.txt. Yo he obtenido buenos resultado dejando el archivo tal como esta, por default.

Ten en cuenta que esta reducción y ofuscación solo será realizada cuando generes tu apk en modo release.

Es necesario que pruebes tu apk a fin de asegurarte que este proceso no tiene problemas con tu app.

En dónde esta el archivo proguard

Si utilizas Android Studio, accediendo al menu File / Project Structure verás la ruta d tu sdk y en dónde deberás buscar en “\tools\proguard\” el archivo proguard-android.txt.
Ten en cuenta que este archivo afecta todos los proyectos a menos que defina uno propio para un proyecto especifico.

En este caso la ruta del archivo proguard-android.txt es
C:\Users\\AppData\Local\Android\sdk\tools\proguard\proguard-android.txt

Probando la reducción

Realizando una prueba con uns simple aplicacion “Hola Mundo” en Android vemos que sin activar minifyEnabled obtenemos un archivo de 1206KB y luego al activarlo el mismo código se reduce a 712KB.

android-proguard-result

Conclusión

ProGuard reducuce el tamaño de tus aplicaciones con poco esfuerzo inicial sumando además la ofuscación del código que te dará un poco más de seguridad (aunque no infalible).
En la carpeta de tu SDK /tools/proguard hay buena cantidad de documentación en caso de que quierás revisarla.

El proyecto ProGuard lo encuentras en http://proguard.sourceforge.net/
La documentación Android al respecto en https://developer.android.com/studio/build/shrink-code.html

Compartir esto: