Inicio de aplicaciones

Muchas de las aplicaciones que vemos en el mercado tienen un inicio algo brusco, ya que pasan de la pantalla de color negro o gris oscuro a la pantalla inicial de la aplicación.

Para lograr un inicio suave durante el arranque de la aplicación Nick Butcher y Katherine Kuan nos recuerdan que Android muestra esta preview basada en el tema de la aplicación principal.

Para lograr una transición mas suave podemos declarar el tema de nuestra aplicación indicando el color de fondo. Por ejemplo:

<style name="AppBaseTheme" parent="android:Theme.Holo.NoActionBar">
<item name="android:windowBackground">@color/red</item>
</style>

smoothlaunch

Mas información
https://plus.google.com/+AndroidDevelopers/posts/VVpjo7KDx4H

Nexus 7 y la cámara que no responde

Hace poco he  estado usando la cámara para trabajar con las fotos realizadas. El código es sencillo:

Uri uri = Uri.fromFile(mTempPictureFile)
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(intent, TAKE_PICTURE_SUBACTIVITY);

El problema viene en donde colocar el fichero temporal.

Como la SDK de android tiene un método para tal propósito pues lo he usado.

File mTempPictureFile = File.createTempFile ("imagen", "jpg");

No se si en muchos dispositivos, pero esto inicialmente funciona aunque posteriormente he podido comprobar que coloca las imágenes en el raiz, y eso me parece muy poco conveniente.

Y decía en muchos porque no en todos. En la nexus 7 y en creo que en los samsumg III produce un curioso efecto, y eso que después de hacer la foto, no te permite aceptarla para usarla. En ambos casos, el software que controla la cámara te muestra una previsualización para aceptar o no la foto con un a X y un ✓ pero no permite confirmarla. Y para colmo, no deja unas trazas claras del problema.

La solución a este problema ha consistido en cambiar la ubicación del fichero, usando algo como:
File cacheDir = this.getExternalCacheDir();
cacheDir.mkdirs();
mTempPictureFile = new File(cacheDir,"waypointCapture.jpg");
mTempPictureFile.delete();
Uri uri = Uri.fromFile(mTempPictureFile);

Bueno, espero que os sea útil si os habéis encontrado con este problema.

Comartid! 🙂

ListActivity + ActionBarActivity

A muchos se nos ha ocurrido la feliz idea de añadir el ActionBar a nuestras aplicaciones que tienen algún Activity extendiendo de la clase ListActivity.

tulistaProSi nuestra aplicación además es compatible con versiones posteriores a android 2.1 debemos hacer que nuestro Activity extienda de ActionBarActivy y aquí comienzan la complicación: a los chicos de Google no se les ocurrió añadir un “ListActionBarActivity” o algo por el estilo.

Lo que es evidente es que no podemos extender de varias clases ya que java no soporta la multi-herencia así que nos toca buscar otro camino.

Se me ocurren dos soluciones así de pronto:

  1. Implementar nosotros mismos la gestión del ListView.
  2. Implementar una versión de ListActivity que extienda de ActionBarActivity y que luego nosotros usemos.

Vamos por el segundo caso y así, si tenemos varias activities que extiendan de ListActivity las solucionamos todas a la vez.

Como seguro que ya sabéis, desde hace algunas versiones Google incluye la posibilidad de descargar los fuentes de la SDK así que el primer paso es buscar el código fuente del ListView para buscar nuestra inspiración en él para crear nuestra clase ListActionBarActivity.

Hay algunas cosas que no nos sirven y tendremos que hacerlas ligeramente diferentes:

onContentChanged

public void onContentChanged() {
     super.onContentChanged();
     View emptyView = findViewById(com.android.internal.R.id.empty);
     mList = (ListView)findViewById(com.android.internal.R.id.list);
     [...]
}

Si usamos ese código tal cual dará error ya que la clase el contenido del paquete com.android.internal no está disponible para usarlo directamente. Lo que haremos será usar el paquete android.R para referirnos a él quedando el código como:

View emptyView = findViewById(android.R.id.empty);
mList = (ListView)findViewById(android.R.id.list);

Otro método que dará fallos es:
ensureList

private void ensureList() {
   if (mList != null) {
       return;
   }
   setContentView(com.android.internal.R.layout.list_content_simple);
}

Siempre deberíamos tener el contenido indicado no se me ocurre un activity sin su layout, así que de momento lanzaré una excepción a ver si averiguo cuando se puede dar esta situación y así resolverlo.

private void ensureList() {
   if (mList != null) {
     return;
   }
   //setContentView(com.android.internal.R.layout.list_content_simple);
   throw new RuntimeException("Esuring list but list is null");
}

Con estos cambio además de todos los cambios necesarios para añadir al ActionBar que ya vienen en la guía oficial debería funcionar en dispositivos con android v3.0 pero… no lo hará en dispositivos anteriores. ¿Por qué? Parece que la actual librería android.support.v7.app.ActionBarActivity ejecuta el método onContentChanged antes de que se hayan cargado los xml de nuestro layout, por lo que los métodos findViewById devolverán null. Aquí tenéis una entrada al respecto.

¿Cómo lo solucionamos? Pues tendremos que retrasar la inicialización de nuestras variables internas. Añadimos una variable de clase booleana para guardar cuando se puede ejecutar la búsqueda de nuestro ListView.

public void onContentChanged() {
  super.onContentChanged();
  if(mContentCharged){
     View emptyView = findViewById(android.R.id.empty);
     mList = (ListView) findViewById(android.R.id.list);
     [...]
  }
}

Y sobreescribimos el método setContentView que usamos (normalmente este):

@Override
public void setContentView(int layoutResID) {
  super.setContentView(layoutResID);
  mContentCharged = true;
  onContentChanged();
}

Creo que eso es todo!

Si te ha gustado ¡ayúdame a difundirlo! ¡Compártelo!! 😀

Cursor already closed

¿Te sale este error cuando intentas cambiar el cursor de tu adapter?

java.lang.RuntimeException: Unable to resume activity …
java.lang.IllegalStateException: trying to requery an already closed cursor android.database.sqlite.SQLiteCursor

Parece que la gestion de los cursores de SimpleCursorAdapter ha cambiado en las versiones SDK > 11.

Si usas un adapter SimpleCursorAdapter para provisionar tu ListView, y has delegado la gestión del cursor al Activity haciendo startManagingCursor es posible que si cambias el cursor del adapter con changeCursor, te de un error de cursor actualmente cerrado…

Algo así como:

//Inicializamos el adapter en el constructor usando un cusor nulo
SimpleCursorAdapter mCursor = SimpleCursorAdapter(Context, int, Cursor, String[], int[])

[...]
//Presentamos los datos consultando la BBDD
Cursor productosCursor = mListaCompraDao.fetchAllProductosSeleccionados(listActive)
startManagingCursor(productosCursor);
mCursor.changeCursor(productosCursor);

Pues este código, que funciona perfectamente en versiones de SDK < 11 da un error cuando se retoma el activity, por ejemplo lanzando un subactivity y volviendo luego a este.

Para que funcionen en veresiones superiores, hay que quitarle la gestion del cursor antiguo al activity antes de cambiarlo al nuevo.

Algo así como:

if(mCursor.getCursor() != null){
stopManagingCursor(mCursor.getCursor());
mCursor.getCursor().close();
}
//Y ya podemos asignarle el nuevo cursor
startManagingCursor(productosCursor);
mCursor.changeCursor(productosCursor);

¿Te ha gustado? Compártelo!! 😀

Google Play actualiza la política de contenido para los desarrolladores

Google ha actualizado la política de contenido para los desarrolladores de aplicaciones android.

Una de los apartados que me ha llamado la antención ha sido el de
Spam y ubicación en Play Store que inlcuye el siguiente apartado:

No se permite la publicación de aplicaciones cuya principal función sea:
* dirigir tráfico de afiliados a un sitio web,
* proporcionar una vista web de un sitio web del que no seas propietario o administrador (a menos que tengas permiso del administrador o del propietario del sitio web para hacerlo).

Esto dejará fuera muchas apps que simplemente se limitaban a mostrar webs de terceros.

Otro apartado interesante, el cual, me ha gustado mucho ha sido el de:
Política de anuncios
que en su sección Contexto de los anuncios dice:

Los anuncios no deben simular o suplantar las advertencias o las notificaciones del sistema.

Personalmente pienso que usar las notificaciones para hacer anuncios es una de las prácticas mas molestas. De hecho, en mi movil tengo un anuncio reiterativo de “Peligro, puede tener un virus…” y te lleva a una web (que no he llegado a abrir). Esta es una práctica muy molesta y engañosa y me parece muy bien que google ponga estas limitaciones.

Mas info:
http://play.google.com/about/developer-content-policy.html

Cómo Personalizar el título de tu Aplicación Android

Hay varias maneras de personalizar el título de tu aplicación android, yo trataré de explicar aquí la forma mas sencilla que he encontrado.

La pesonalización del título de tu aplicación consiste básicamente en dos pasos:

  1. Crear un layout donde tendremos nuestro título.
  2. Decirle a nuestra aplicación que lo muestre cuando se cree.

Layout (con una imagen y un título):

<?xml version=”1.0″ encoding=”utf-8″?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android&#8221;
android:id=”@+id/main_title_layout”
android:layout_width=”match_parent” android:layout_height=”wrap_content” android:orientation=”horizontal”
android:background=”@android:color/black”>
<ImageView
android:layout_width=”22dp”
android:layout_height=”22dp”
android:contentDescription=”@string/app_name”
android:src=”@drawable/icon”
android:gravity=”center_vertical” />

<TextView
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_gravity=”center_vertical”
android:paddingLeft=”4dp”
android:text=”@string/app_name”
android:textSize=”12sp”
android:textColor=”@android:color/white”
/>
</LinearLayout>

Supongamos que guardamos el layout con el nombre de “main_title.xml”.

Código para usar el layout anterior en cada Activity

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Habilitamos la personalización del título
this.requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.main);
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.main_title);
//Otras acciones de nuestra app
}

Bien, esta sería la teoría, pero en la práctica, si añades un fondo a al layout, se puede apreciar que no encaja del todo:

title_black_padding

Esto parece ser que ocurre, porque realmente lo que hacemos con las instrucciones anteriores es insertar ese layout como título, pero dentro del “título” original.
Para poder solucionarlo añadimos las siguientes instrucciones:

View view = getWindow().findViewById(R.id.main_title_layout);
((FrameLayout) view.getParent()).setPadding(0,0, 0,0);

Donde main_title_layout sería el identificador del Layout contenedor de nuestro título personalizado.

Eso es todo!

A disfrutar!

Habilitar el teclado en el emulador de Android

Esta entrada será corta.

Si estas trabajando con el emulador de android para probar tu aplicación, ir pulsando los botones con el ratón puede ser muuuuyy tedioso. Para habilitarlo puedes hacerlo desde el “Android Virtual Device Manager”

Menu_eclipse

Elegimos nuestro dispositivo virtual y lo editamos. En la sección de hardware añadimos una nueva propiedad del tipo keyboard support y lo ponemos a yes y listo!

keyboard_properties

Si te ha gustado compartelo en tus redes! 😀