EditText con sugerencias de autocompletado en Android

AutoCompleteTextView

En este post crearemos un elemento EditText, que al momento que vayamos escribiendo palabras sobre este, nos vaya sugiriendo coincidencias obtenidas de un arreglo de palabras definidos por nosotros mismos anteriormente, como pueden ver a continuación.
Lo primero que tenemos que hacer es agregar el elemento EditText en la vista de nuestra Activity, que en este caso se llama AutoCompleteTextView.

<AutoCompleteTextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/autocomplete_region" android:layout_width="fill_parent" android:layout_height="wrap_content" />

Luego debemos crear la lista de las sugerencias que ofreceremos al usuario, esto debemos hacerlo en el archivo res/values/strings.xml. Recuerden que las etiquetas <resources></resources> son la raíz del archivo, por lo que si tienen más strings definidos deben ir también dentro de estas etiquetas.

<resources>
  <string-array name="region_array">
    <item>Región Metropolitana</item>
    <item>Región de Aríca y Parinacota</item>
    <item>Región de Tarapacá</item>
    <item>Región de Antofagasta</item>
    <item>Región de Atacama</item>
    <item>Región de Coquimbo</item>
    <item>Región de Valparaíso</item>
    <item>Región de O\'Higgins</item>
    <item>Región de Maule</item>
    <item>Región del Biobío</item>
    <item>Región de la Araucanía</item>
    <item>Región de los Ríos</item>
    <item>Región de los Lagos</item>
    <item>Región de Aysén</item>
    <item>Región de Magallanes y Antártica</item>
  </string-array>
</resources>

A continuación, dentro de la función onCreate, partimos referenciando el elemento AutoCompleteTextView que creamos anteriormente en la vista. Luego obtenemos las regiones que creamos en el archivos de string y creamos un adaptador ArrayAdapter que viene por defecto en Android. Cabe señalar que el elemento android.R.layout.simple_list_item_1 es también una vista que viene con el mismo android. Nosotros también podríamos crear nuestro propio adaptador extendiendo de ArrayAdapter (o de cualquier similar) y crear nuestra propia vista para los elementos que se desplieguen. Con lo que finalmente le pasamos el adaptador al elemento en la vista.

// Referencia al elemento en la vista
AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.autocomplete_region);

// Arreglo con las regiones
String[] regions = getResources().getStringArray(R.array.region_array);

// Le pasamos las regiones al adaptador
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, regions);

// finalmente le asignamos el adaptador a nuestro elemento
textView.setAdapter(adapter);

Recuerden que pueden consultar la documentación completa de AutoCompleteTextView ya que dispone de muchas funcionalidades para que podemos utilizar.

Espero que les haya servido y saludos!.

Cómo referenciar nuestras vistas y recursos con Butter Knife en Android

buttlerknife

Butter Knife es una excelente librería que nos ayudará a ahorrar código al momento de referenciar elementos de nuestras vistas, recursos del proyecto o hasta referencias eventos que interactúan con el usuario, utilizando connotaciones para cada una de estas.
Para comenzar, compilamos la librería en nuestro archivo gradle con las siguientes dependencias

compile 'com.jakewharton:butterknife:8.0.1'

Normalmente referenciamos elementos de nuestras vistas de la siguiente forma

class ExampleActivity extends Activity {
  TextView textViewName;
  TextView textViewCity;

  @Override 
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.simple_activity);
    textViewName = (TextView)findViewById(R.id.textViewName);
    textViewCity = (TextView)findViewById(R.id.textViewCity);
  }
}

Con la librería Butter Knife podríamos hacerlo así

class ExampleActivity extends Activity {
  @BindView(R.id.textViewName) TextView title;
  @BindView(R.id.textViewCity) TextView subtitle;

  @Override 
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.simple_activity);
    ButterKnife.bind(this);
  }
}

Si estamos dentro de un fragment pasamos la vista a la instancia de ButterKnife

View view = inflater.inflate(R.layout.fragment, container, false);
ButterKnife.bind(this, view);

Es importante liberar la memoria utilizada, una vez que destruyamos el fragment.

// ...
private Unbinder unbinder;
// ...

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  View view = inflater.inflate(R.layout.fragment, container, false);
  unbinder = ButterKnife.bind(this, view);
  return view;
}
@Override 
public void onDestroyView() {
  super.onDestroyView();
  unbinder.unbind();
}

Y a su vez también disponemos de otras connotaciones para para referenciar los recursos de nuestro proyecto, utilizando @BindBool, @BindColor, @BindDimen, @BindDrawable, @BindInt, @BindString

@BindString(R.string.title) String title;
@BindDrawable(R.drawable.graphic) Drawable graphic;
@BindColor(R.color.red) int red;
@BindDimen(R.dimen.left_margin) Float left_margin;

Podemos asignar eventos a los elementos de la vista

@OnClick(R.id.buttonSubmit) void submit() {
  // Aquí nuestro código...
}

Pasar la vista como parámetro

@OnClick(R.id.buttonSubmit) void submit(View view) {
  // Aquí nuestro código...
}

O pasar la posición si corresponde

@OnItemClick(R.id.list_of_things) void onItemClick(int position) {
  // Aquí nuestro código...
}

Tenemos disponible los siguientes eventos para asignar: @OnLongClick, @OnPageChange, @OnTextChanged, @OnTouch, @OnItemLongClick y @OnCheckedChanged. Incluso asignar el evento a varios elementos de una sola vez

@OnClick(R.id.buttonOne, R.id.buttonTwo, R.id.buttonThree)
public void submit(View view) {
  // Aquí nuestro código...
}

Podemos hacer listas de vistas y aplicar propiedades

@BindViews({ R.id.first_name, R.id.middle_name, R.id.last_name })
List<EditText> nameViews;

ButterKnife.apply(nameViews, View.ALPHA, 0.0f);
ButterKnife.apply(nameViews, View.VISIBLE, false);
ButterKnife.apply(nameViews, DISABLE, false);

Finalmente podemos especificar que un elemento de la vista puede ser nulo al momento de referenciarlo o opcional al momento de asignar un evento.

@Nullable @BindView(R.id.might_not_be_there) TextView mightNotBeThere;

@Optional @OnClick(R.id.maybe_missing) void onMaybeMissingClicked() {
  // Nuestro código...
}

Espero que el post haya sido de ayuda y no duden en comentar sus dudas o sugerencias. Saludos!

Conectarse a socket.io de NodeJS con Android

logo android

Lo primero es agregar la dependencia de la librería a nuestro archivo grade en nuestro proyecto en Android Studio.

compile 'com.github.nkzawa:socket.io-client:0.4.0'

Así en la actividad que queramos utilizar el socket creamos el objeto Socket.

Socket socket;

y algunas constantes que utilizaremos en nuestro código.

final static String URL_SOCKET = "http://localhost";
final static String MY_EVENT = "my event";
final static String PARAM_NAME = "name";
final static String PARAM_IMAGE = "image";

Instanciamos nuestra conexión al servidor pasando como parámetro la url de este mismo.

try{
  /* Instance object socket */
  socket = IO.socket(URL_SOCKET);
}catch (URISyntaxException e){
  e.printStackTrace();
}

Podemos instanciar el objecto con opciones predeterminadas para que se reconecte el socket.

IO.Options opts = new IO.Options();
opts.forceNew = true;
opts.reconnection = false;
try{
  /* Instance object socket */
  socket = IO.socket(URL_SOCKET, opts);
}catch (URISyntaxException e){
  e.printStackTrace();
}

O para utilizar conexiones SSL por defecto o como parámetro al instanciar el objeto

/* Default SSLContext for all sockets */
IO.setDefaultSSLContext(mySSLContext);

/* Set as an option */
opts = new IO.Options();
opts.sslContext = mySSLContext;
try{
  /* Instance object socket */
  socket = IO.socket(URL_SOCKET, opts);
}catch (URISyntaxException e){
  e.printStackTrace();
}

Luego creamos los eventos principales de nuestro socket: EVENT_CONNECT y EVENT_DISCONNECT, estos son los que recibirán las emisiones desde el servidor.

socket.on(Socket.EVENT_CONNECT, new Emitter.Listener(){
  @Override
  public void call(Object... args) {
    /* Our code */
  }
}).on(Socket.EVENT_DISCONNECT, new Emitter.Listener(){
  @Override
  public void call(Object... args) {
    /* Our code */      
  }
});

Y crear nuestros propios eventos listener

socket.on(MY_EVENT, new Emitter.Listener(){
  @Override
  public void call(Object... args) {
    /* Our code */      
  }
});

Con esto ya podemos abrir la conexión

socket.connect();

Además disponemos de otras funciones como para verificar el estado de la conexión.

socket.connected()

Para desconectar nuestro socket.

socket.disconnect()

Si lo que queremos es emitir eventos por nuestro socket utilizamos, enviando cadenas de texto o también datos binarios.

JSONObject obj = new JSONObject();
obj.put(PARAM_NAME, "Pablo");
obj.put(PARAM_IMAGE, new byte[42]);

/* Emit event */
socket.emit(MY_EVENT, obj)

También podemos capturar el callback que retorna el servidor

socket.emit(MY_EVENT, obj, new Ack() {
  @Override
  public void call(Object... args) {
    /* Our code */
  }
});

Y eso es todo, Saludos!

Cambiar el tema de un fragment en Android

logo androidEstoy programando mucho en Android, pero no he subido nada al respecto, por lo que iré compartiendo pequeños códigos para hacer cosas especificas, que también claro, servirán para anotaciones personales mias.
Los fragment estan inmersos en las actividades que los contienen, por lo que no podemos cambiarlos directamente en el archivo AndroidManifest.xml, así que heredan los estilos que las actividades tengan definidas, y si estas últimas no tienen ningún tema definido heredan el que tenga la aplicación. Pero podemos cambiar esto utilizando las siguientes 3 lineas dentro de la función onCreateView de nuestro fragment.
Primero traemos el tema que queremos aplicar al fragment creando un objeto de contexto. Deben reemplazar MyAppTheme por su tema.

final Context themeWrapper = 
	new ContextThemeWrapper(getActivity(), R.style.MyAppTheme);

Luego definimos un LayoutInflater al cuál le aplicamos nuestro contexto anterior.

LayoutInflater cloneInflate = 
	inflater.cloneInContext(themeWrapper);

Y finalmente inflamos el layout de nuestro fragment utilizando el layout inflate definido anteriormente. Recuerden que deben reemplazar fragment_layout por el que corresponda con su fragment, y que será el que finalmente retornaremos en la función.

View view = 
	cloneInflate.inflate(R.layout.fragment_layout, container, false);

Eso es todo! Saludos!

Matemáticas, física y animación con Canvas html5 – parte 2

canvas
Estimados! Disculpen por dejar tanto tiempo el blog botado, ahora continuaré con los ejercicios de canvas. Este post es la continuación de Matemáticas, física y animación con Canvas html5 – parte 1. Para esta ocación no profundizaré tanto en puntos matemáticos como lo hice en la primera parte, por lo que recomiendo que si no la han leído lo hagan y luego continuen con esta.
Para este ejercicio vamos a crear muchas pelotas chocando con las paredes (no entre si) y moviendose a diferentes velocidades según el tamaño de cada una.
Si quieren ver el resultado bajen al final de este post
Al igual que el post anterior, comenzaré pegando todo el código para que lo vean y luego explicando cada uno de los puntos relevantes.
Comenzamos importando nuestra librería modernizr entre las etiquetas “head” de nuestro HTML para garantizar la compatibilidad de nuestro canvas con todos los navegadores.

<script src="http://cdnjs.cloudflare.com/ajax/libs/modernizr/2.8.1/modernizr.min.js"></script>

y dentro de nuestras etiquetas body creamos el canvas donde se dibujará nuestro script.

<div>
	<canvas id="canvasTwo" width="600" height="300"> 
		Cambiate de navegador!
	</canvas>
</div>

Ahora el código completo de nuestro javascript

window.addEventListener('load', eventWindowLoaded, false);
function eventWindowLoaded(){
  canvasAppPartTwo();
}
function canvasSupport(){
  return Modernizr.canvas;
}
function canvasAppPartTwo(){
  if(!canvasSupport()){
    return;
  }

  function drawScreen(){
    context.fillStyle = '#EEEEEE';
    context.fillRect(0, 0, theCanvas.width, theCanvas.height);
    context.strokeStyle = '#000000';
    context.strokeRect(1, 1, theCanvas.width-2, theCanvas.height-2);
    
    var ball;
	for(var i = 0; i < balls.length; i++){
      ball = balls[i];
      context.fillStyle = ball.color;
      ball.x += ball.xunits;
      ball.y += ball.yunits;

      context.beginPath();
      context.arc(ball.x, ball.y, ball.radius, 0, Math.PI*2, true);
      context.closePath();
      context.fill();

	  if((ball.x+ball.radius) > theCanvas.width || ball.x < 0){       
        ball.angle = 180 - ball.angle;       
        updateBall(ball);     
      }else if((ball.y+ball.radius) > theCanvas.height || ball.y < 0){
        ball.angle = 360 - ball.angle;
        updateBall(ball);
      }
	}
  }
  function updateBall(ball){
	ball.radians = ball.angle * Math.PI/ 180;
    ball.xunits = Math.cos(ball.radians) * ball.speed;
    ball.yunits = Math.sin(ball.radians) * ball.speed;
  }

  var numBalls = 1000;
  var maxSize = 8;
  var minSize = 5;
  var maxSpeed = maxSize + 5;
  var balls = new Array();
  var tempBall;
  var tempX;
  var tempY;
  var tempSpeed;
  var tempAngle;
  var tempRadius;
  var tempRadians;
  var tempXunits;
  var tempYunits;
  var tempColor;

  theCanvas = document.getElementById('canvasTwo');
  context = theCanvas.getContext('2d');

  for(var i = 0; i < numBalls; i++){
    tempRadius = Math.floor(Math.random()*maxSize)+minSize;
    tempX = tempRadius*2 + (Math.floor(Math.random()*theCanvas.width)-tempRadius*2);
    tempY = tempRadius*2 + (Math.floor(Math.random()*theCanvas.height)-tempRadius*2);
    tempSpeed = maxSpeed-tempRadius;
    tempAngle = Math.floor(Math.random()*360);
    tempRadians = tempAngle * Math.PI/ 180;
    tempXunits = Math.cos(tempRadians) * tempSpeed;
    tempYunits = Math.sin(tempRadians) * tempSpeed;
    tempColor = '#'+Math.floor(Math.random()*16777215).toString(16);
    tempBall = {
      x: tempX,
      y: tempY,
      radius: tempRadius,
      speed: tempSpeed,
      angle: tempAngle,
      xunits: tempXunits,
      yunits: tempYunits,
      color: tempColor
    }
    balls.push(tempBall);
  }
  setInterval(drawScreen, 33);
}

Ahora explicaré las cambios. Los primero es determinar la función que se ejecutará al momento que se cargue el javascript.

window.addEventListener('load', eventWindowLoaded, false);
function eventWindowLoaded(){
  canvasAppPartTwo();
}

Definimos un intervalo para ir ejecutando el canvas.

setInterval(drawScreen, 33);

Entonces en orden de ejecución debemos definir las variables que utilizares. Solo comentaré las nuevas que agregé a este ejemplo. Las demás son variables que utilizaremos de forma temporal.

var numBalls = 1000; // Numero de bolas
var maxSize = 8; // Tamaño máximo que pueden tener
var minSize = 5; // Tamaño minimo
var maxSpeed = maxSize + 5; // Velocidad máxima
var balls = new Array(); // Arreglo que contendrá las bolas que creemos.

Rerefenciamos el canvas en el contexto de nuestro script

theCanvas = document.getElementById('canvasTwo');
context = theCanvas.getContext('2d');

Lo siguiente es hacer un “for” que recorrerá para crear cada una de las pelotas, para este ejemplo serán 1000 (pueden poner el número que quieran, solo sean considerados con su máquina).

for(var i = 0; i < numBalls; i++){

}

Con esto vamos creando lo valores temprales de nuestras bolas. No me detendré en este punto porque ya está explicado en el primero post como voy sacando los cálculos, la única diferencia es que voy jugando con valores random.
Así encontes calculamos el radio entre el tamaño minimo que determinamos y el máximo.

tempRadius = Math.floor(Math.random()*maxSize)+minSize;

Luego calculamos la posición de la bola dentro del canvas, el valor random es el tamaño del canvas menos su radio al cuadrado.

tempX = tempRadius*2 + (Math.floor(Math.random()*theCanvas.width)-tempRadius*2);
tempY = tempRadius*2 + (Math.floor(Math.random()*theCanvas.height)-tempRadius*2);

La velocidad que está directamente relacionada con el tamaño de la bola, ya que mientras más grande sea, más lento debe moverse.

tempSpeed = maxSpeed-tempRadius;

Y los cambios de angulos en la dirección de nuestra bola.

tempAngle = Math.floor(Math.random()*360);
tempRadians = tempAngle * Math.PI/ 180;
tempXunits = Math.cos(tempRadians) * tempSpeed;
tempYunits = Math.sin(tempRadians) * tempSpeed;

FinaLmente le damos un color random a la pelota para poder diferenciarlas unas de otras.

tempColor = '#'+Math.floor(Math.random()*16777215).toString(16);

Ya teniendo todos los valores, agregamos la pelota a nuestro arreglo de bolas.

tempBall = {
	x: tempX,
    y: tempY,
    radius: tempRadius,
    speed: tempSpeed,
    angle: tempAngle,
    xunits: tempXunits,
    yunits: tempYunits,
    color: tempColor
}
balls.push(tempBall);				

Ya dentro de la función drawScreen dibujamos el contenedor de nuestro canvas.

context.fillStyle = '#EEEEEE';
context.fillRect(0, 0, theCanvas.width, theCanvas.height);
context.strokeStyle = '#000000';
context.strokeRect(1, 1, theCanvas.width-2, theCanvas.height-2);

Y volvemos a recorrer el arreglo de bolas para id dibujandolas dentro de nuestro canvas. Utilizaré la variable auxiliar ball donde iré referenciando cada bola.

var ball;
for(var i = 0; i < balls.length; i++){
								
}

Referenciamos los valores.

ball = balls[i];
ball.x += ball.xunits;
ball.y += ball.yunits;					

Y dibujamos la bola con su respectivo color y tamaño.

context.fillStyle = ball.color;
context.beginPath();
context.arc(ball.x, ball.y, ball.radius, 0, Math.PI*2, true);
context.closePath();
context.fill();

Ahora capturo el momento en donde la bola debe cambiar su dirección para no salir del canvas. Llamando a la función updateBall.

if((ball.x+ball.radius) > theCanvas.width || ball.x < 0){       
	ball.angle = 180 - ball.angle;       
    updateBall(ball);     
}else if((ball.y+ball.radius) > theCanvas.height || ball.y < 0){
	ball.angle = 360 - ball.angle;
    updateBall(ball);
}

Al comienzo del for definimos una variable auxiliar que ahora utilizamos para poder actualizar la bola fuera del for.

function updateBall(ball){
	ball.radians = ball.angle * Math.PI/ 180;
    ball.xunits = Math.cos(ball.radians) * ball.speed;
    ball.yunits = Math.sin(ball.radians) * ball.speed;
}													

Y por fin el resultado final! Hay varias cosas que podemos mejorar, como por ejemplo que las pelotas choquen entre ellas. Haré una tercera parte para eso.
Gracias por su tiempo y por espero que el tutorial les haya gustado.

Actualiza tu browser por la chucha!