Páginas

Recording the screen on Android

Until now, if you wanted to record a video of the screen of our mobile, we had to use market applications. I have tried some of them, and it´s not a simple process, as most require you to have the phone rooted or auxiliary programs installed on your computer.
Fortunately, since version 4.4 of Android (API level 19), it includes the adb utility screenrecord. With it, recording the display is as simple as plugging the phone and calling the following command:

$ adb shell screenrecord /sdcard/record.mp4

In this way the recording begins. To stop it press ctrl + C. The result is in the path of our phone that we have stated.

The command allows many options, such as setting the recording time, bit rate, etc. Here you can find more information.

Sometimes the command has shown me the error:

unable to create video/avc codec instance

I have not managed to find out why that is, but applying the universal solution to the computer, it solves: reboot your phone.

Assign id to a view programmatically

Views are usually declared in Android layout files with and id.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/boardLayout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

However, sometimes we can´t assign the id. For example, if we want to reuse a view with include tag, and we need each instance to have a different id.

In this situation, we have the option to define ids in a resource file.

<resources>
    <item name="id_1" type="id"/>
</resoruces>

This identifiers are created in R class, so you can access and assign them to a view in code.

String name = "id" + i;   
view.setId(getResources().getIdentifier(name, "id", getPackageName()));

From this point, we can find the view searching by id.

Avoid screen rotation in Android

By default, Android activities adapt to device orientation. If we want to avoid this behaviour, we just have to define and orientation for any activity on AndroidManifest.xml file (portrait or landscape).

<activity android:name="package.YourActivity"
          android:screenOrientation="portrait" >

Keep the screen on


When we are programming a game, we may want the screen of the device to keep on. There´s a solution based on wake locks, but it has two big advantages: it requieres special permission, and the developer needs to worry about releasing unused locks.

There is a much simpler solution, that is recommended in Android developers page: using flag_keep_screen_on. This flag can be activated only in an activity, from manifest or from code. This scond option has the adventage that the flag can be cleared if you don´t need it, allowing the screen to turn off again.

In manifest, we can declare an activity with:

<activity android:name="jvel.android.games.saveme.MainActivity" 
          android:keepScreenOn="true" />

In code, we have to add this line:

protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}

If we choose code option, and we want to clear the flag:

getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)

Making an activity scrollable

A typical question when someone is beginning with Android is how to add scroll to an activity, or a component of the interface. It has a really simple solution. You just have to add a ScrollView tag above the element you want to scroll.

It´s important to note that ScrollView tag supports just one child. It uses to be a layout that contains the rest of the components.

Therefore, if we want to make scroll over the entire interface of an activity, we have to write something like this:

<ScrollView
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
        <!-- your components -->
    </RelativeLayout>
</ScrollView>

If we want to apply scroll to a concrete group of components, we also can do it. This is the structure of configuration activity in Match Pairs Battle, which lets ad at the top, a button at the bottom, and allows scroll over the options.

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

    <LinearLayout
        android:id="@+id/adLayout"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:orientation="horizontal" >
    </LinearLayout>

    <ScrollView
        android:id="@+id/options_layout"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/adLayout"
        android:layout_above="@+id/playButton" >
        
        <RelativeLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content" >
            <!-- your components -->
        </RelativeLayout>
    </ScrollView>

    <Button
        android:id="@+id/playButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
         />

</RelativeLayout>

Multilingual posts in blogger

One of the major shortcomings of Blogger is multilingual and, to date, it does not provide a solution to publish entries in different languages.

After some time testing solutions found in the web, I got a fairly simple and satisfactory option, based on jQuery. Its main advantage over most I've seen, is that it allows one entry with different languages, so that the views, comments, etc., are unique. It also attempts to show visitors the most appropriate language, according to the browser settings.

The solution is to create a panel with the contents of the entry in each of the desired languages, and add buttons for each language, showing the corresponding panel when it´s pressed, and hidding the others.

The solution, step by step:

1. Add the jQuery library to our blog template. To do this, go to Template - Edit HTML, and in code we search for </head>. Just behind, copy the following line:

<script src='https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js' type='text/javascript'/>

2. Add the script to select default language, and control clicks on the buttons. Paste it below the import line.

<script type='text/javascript'>
   $(document).ready(function() {
   <!-- Select the initial language -->
   var userLang = navigator.language || navigator.userLanguage; 
   if (userLang.indexOf(&quot;es&quot;) &gt;= 0) {
  $(&quot;div.english&quot;).hide();
   }
   else {
  $(&quot;div.spanish&quot;).hide();
   }
  <!-- If spanish button is clicked, hide english text, and show spanish text -->
  $(&quot;#spanish_button&quot;).click(function () {
    $(&quot;div.english&quot;).hide();
    $(&quot;div.spanish&quot;).fadeIn(300);
  });
  
  <!-- If english button is clicked, hide spanish text, and show english text -->
  $(&quot;#english_button&quot;).click(function () {
    $(&quot;div.english&quot;).fadeIn(300);
    $(&quot;div.spanish&quot;).hide();
  });
   });
 </script>

3. Add buttons to change language. We can place them in each post; to do that, we have to paste the code in the beginning of each one. The other option is to place the buttons in a fixed location. (Change %your_image% by the url of the image you want to use for the button).

<img align='right' height='50' id='english_button' src=%your_image% width='50'/>
<img align='right' height='50' id='spanish_button' src=%your_image% width='50'/>

4. For each post, write the text for each language in a div tag, indicating the language in class attribute. Do the same for the title.

<div class="english">
English Text.
</div>
<div class="espanish">
Spanish Text.
</div>

And that's it. You can see the result in the entries of this blog. You can add as many languages ​​as you want.

Now, I just have to improve my english...

Alternative text for empty lists

ListView class in Android provides setEmptyView method, which allows us to show a message to the user if the list is empty. To use it, we have to define in the layout file an element with gone visibility. That will be the parameter for setEmptyView.

This example shows how I´ve implemented the players list on Memory Battle. This is the fragment in the layout file:

    <ListView
        android:id="@+id/players_list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" /> 

    <TextView
        android:id="@+id/no_players_text"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/playersList"
        android:text="@string/no_players"
        android:visibility="gone" />

And this is the code to tell the list that it has to show the TextView when it has no elements.

playersList = (ListView) findViewById(R.id.playersList);
playersList.setEmptyView(findViewById(R.id.no_players_text));

You can use any View object as parameter for setEmptyView, so you could change the text for an image, for example.

Parameters in strings

When we get string from string.xml file, we can use some advanzed options, including using parameters.

For example, if we want to show a business data, like username. A simple solution is to concatenate it with a string from xml file:

getString(R.string.hello) + Model.user_name;

This is a correct solution, but not enough if we want to insert the value in the middle of the chain. In this case we can use the possibility to add parameters. We need an entry like this in the file string.xml.


<string name="hello">Hello %1$s, wellcome</string>

Each parameter has one digit that is used as a sequential identifier, and another one to tell the type: s - string, d - decimal...

In our code, we can complete the string with the next line.

String.format(getString(R.string.hello), Model.user_name);

Format method on class String can receive as many parameters as needed, so we can add all we need.

Customize text fonts

The font used in TextView elements can be customized in its xml definition, with the android:typeface tag, which can take sans, serif, monospace and normal values.

<TextView android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"      
        android:typeface="sans" />

If we want to use another font, we need its ttf file. Under Windows we can find all that we have installed in our computer in the folder Windows/Fonts. To add it to our Android application, we just have to put it in assets folder, that is in the project root folder.


The previous picture shows the structure of the project I´m working actually, with the font Bubble Bath, that Rocco Pissani has yielded to me, nicely (Thanks!).

Once the font is placed in its location, we can use it in the code of any activity, and apply it to a TextView.

Typeface tf = Typeface.createFromAsset(this.getAssets(),
    "bubblebath.ttf");
textView.setTypeface(tf);

Remove animation between activities


By default, when we throw or we finish an activity on Android, it is begun with an animation. Most times this behaviour is good, but we can remove it if we need.

To remove the animation, we have to create a new Theme in styles, placed on values/styles.xml.

<style name="noAnimTheme" parent="android:Theme">
        <item name="android:windowAnimationStyle">@null</item>
</style>

Next, we have to change manifest file to tell what activities must use our new style.

<activity android:name="jvel.android.kidsgames.MainActivity"
          android:theme="@style/noAnimTheme" >
</activity>

If we don´t want to remove the animation, but change it, we just have to write the wanted one in android:windowAnimationStyle.

Redimensionar mesas de trabajo en Illustrator

Una de las tareas más tediosas cuando se desarrolla para Android es la de crear los recursos gráficos (iconos, imágenes, etc.) para las distintas densidades de pantalla.

He hecho algunas pruebas con Adobe Illustrator CS6, y el uso de las mesas de trabajo o artboards puede ayudar mucho, ya que a la hora de exportar el trabajo a png, cada mesa de trabajo genera un archivo independiente.

El gran problema viene cuando tengo todo lo que necesito para la aplicación, y toca generar las versiones para cada densidad de pantalla. La idea es muy simple, selecciono todos los elementos del ficheros, y aplico el factor de escala necesario. El primer problema que surge es que esta transformación no afecta a las mesas de trabajo, y no existe (que yo conozca), un método para redimensionarlas por grupos. Así que toca ir una a una dándoles el tamaño correcto. Esto, cuando se trabaja con montones de imágenes, y con al menos 3 densidades de pantalla, resulta inasumible.

Por suerte Illustrator permite realizar scripts (secuencias de comandos) con los que automatizar estas tareas. Así, podríamos usar el siguiente script para redimensionar varias mesas de trabajo de una vez.

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
if (app.documents.length > 0) {
 var idoc = app.activeDocument;

 var fromArtBoard = idoc.artboards.length + 1;
 var toArtBoard = idoc.artboards.length + 1;

 while (fromArtBoard < 0 || toArtBoard < 0 || fromArtBoard > idoc.artboards.length || toArtBoard > idoc.artboards.length) {
  fromArtBoard = Number(Window.prompt ("Enter the first artboard to resize", 1)) - 1;
  toArtBoard = Number(Window.prompt ("Enter the last artboard to resize", idoc.artboards.length)) - 1;
 }

 var width = Number(Window.prompt ("Enter new Artboard width", "new width"));
 var height = Number(Window.prompt ("Enter new Artboard height", "new height"));
 
    for (i=fromArtBoard; i<=toArtBoard; i++) {
        var abBounds = idoc.artboards[i].artboardRect;// left, top, right, bottom
 
        var ableft = abBounds[0];
        var abtop = abBounds[1];
        var abwidth = abBounds[2] - ableft;
        var abheight = abtop - abBounds[3];
        
        var abctrx = abwidth/2+ableft;
        var abctry = abtop-abheight/2;
             
        var ableft = abctrx-width/2; 
        var abtop = abctry+height/2;
        var abright = abctrx+width/2; 
        var abbottom = abctry-height/2;
        
        idoc.artboards[i].artboardRect = [ableft, abtop, abright, abbottom];
    }
}

Pero aquí no acaban los problemas. Si usamos este script y redimensionamos nuestras imágenes con la transformación de escala, seguramente nos encontraremos que la posición de las imágenes ya no coincide con la de sus respectivas mesas de trabajo. De nuevo tendríamos que ir recolocándolas una a una en su posición. Para evitarlo, tendríamos que generar un script que se encargase de redimensionar tanto las mesas de trabajo como las imágenes, de forma que se mantuviesen en la misma posición. Podría ser algo así.


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
if (app.documents.length > 0) {
 var idoc = app.activeDocument;

 var factor = Number(Window.prompt ("Enter scale factor as percentage", 100));
 
 var ilayer = idoc.activeLayer;
 
 /* Recorremos todos los grupos de objetos escalando */
 for(i = 0; i < idoc.groupItems.length; i++) {
  if (idoc.groupItems[i].parent == ilayer) {
   idoc.groupItems[i].resize(factor,factor,true,true,true,true, factor, Transformation.CENTER);
  }
    }
 
 /* Recorremos todas las mesas de trabajo escalando*/
    for (i=0; i<idoc.artboards.length; i++) {
        var abBounds = idoc.artboards[i].artboardRect;
 
        var ableft = abBounds[0];
        var abtop = abBounds[1];
        var abwidth = abBounds[2] - ableft;
        var abheight = abtop - abBounds[3];
        
        var abctrx = abwidth/2+ableft;
        var abctry = abtop-abheight/2;
             
  var width = abwidth * factor / 100;
  var height = abheight * factor / 100;
        var ableft = abctrx-width/2; 
        var abtop = abctry+height/2;
        var abright = abctrx+width/2; 
        var abbottom = abctry-height/2;
        
        idoc.artboards[i].artboardRect = [ableft, abtop, abright, abbottom];
    }
}
 else  {
        alert ("there are no open documents");
}

Hay un tema importante a tener en cuenta. Lo que hace el script es redimensionar todas las agrupaciones de objetos que se encuentran directamente en la capa activa del documento. Lo he hecho así porque en mis pruebas trabajé con una única capa, con todos los elementos de una mesa de trabajo en un único grupo, por lo que es el funcionamiento que necesitaba. Si trabajas con varias capas puedes modificar el script para recorrerlas todas, o para que te pida qué capas procesar. Si no agrupas los elementos tendrás que modificar el script para recorrer todos los elementos del documento, en lugar de los grupos.

Activar opciones de desarrollo

Una de las decisiones que ha tomado la gente de Android en las últimas versiones (creo que desde la 4.2), es la de ocultar por defecto las opciones de desarrollo.

Acceder a estas opciones es imprescindible si queremos empezar a utilizar nuestro teléfono para probar nuestras aplicaciones porque, entre otras muchas cosas, nos permiten habilitar la depuración por usb.

La activación es muy simple. Sólo tenemos que acceder al menú ajustes, meternos en información del teléfono, y pulsar 7 veces sobre el número de compilación. Sí, es un poco extraño. Estos de Android...

Si todo ha ido bien, verás un mensaje en el que tu teléfono te nombra oficialmente desarrollador, y podrás acceder a las opciones bajo el menú de ajustes.

Ya podemos empezar a cacharrear.

Splash Screen

Un clásico de cualquier aplicación móvil son las splash screen, o pantallas de bienvenida. No son más que pantallas de transición que se muestran a los usuarios antes de darles acceso a la aplicación. Algunas veces se aprovechan para hacer cosas en segundo plano, como cargar recursos, y otras simplemente sirven para mostrar tu logo y meterle a los usuarios tu marca.

Hacer una splash screen en Android es muy simple. Primero necesitamos una activity cuyo layout muestre tu logo. Por ejemplo:

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    tools:context=".JVelActivity" 
    android:onClick="onClick"
    android:background="#0C416F">

<ImageView android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:contentDescription="@string/jvel"
    android:src="@drawable/jvel"/>

</LinearLayout>

Y en el código de la misma podemos hacer algo como esto:

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class JVelActivity extends FullScreenActivity {
 
 private boolean gone = false;
 
 private Thread waitThread;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_jvel);
  
  waitThread = new Thread()
     {
         public void run()
         {
             try
             {
                 //Mostramos la pantalla durante tres segundos, una vez terminados, vamos a la pantalla principal
                 sleep(3000);
             }
             catch (InterruptedException e) 
             {}
             finally
             {   
                 toMainActivity();
             }
         }
     };
     waitThread.start();
 }
 
 private void toMainActivity() {
  if (!gone) {
  Intent intent = new Intent(this, MenuActivity.class);
         startActivity(intent);
         gone = true;
         finish();
  }
 }
 
 public void onClick(View view) {
  toMainActivity();
 }
}

Lo único que hace la actividad es crear un hilo que se duerme durante 3 segundos y, pasado ese tiempo, envía la aplicación a la verdadera pantalla principal (MenuActivity en este caso). También se contempla la posibilidad de que el usuario se harte de esperar, y toque la pantalla. En ese caso pasará inmediatamente al menú, gracias al método onClick, que se ha añadido como oyente para el LinearLayout de fondo.

Lógicamente la actividad debe ser la inicial de la aplicación. Ya sabéis que podéis configurarla como tal en el manifest:

1
2
3
4
5
6
7
8
<activity
            android:name="jvel.android.game.calculator.JVelActivity"
            android:screenOrientation="portrait" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

Puedes ver una implementación de esta pantalla (con el espectacular logo de jVel) en cualquiera de mis juegos. En algunos casos se complementa con una carga de recursos en segundo plano, y un mensaje informativo. Eso para otro día.

Librería de juegos: FullScreenActivity

Uno de los objetivos que me marco conforme voy haciendo nuevas cosas en Android, es crear una librería con las utilidades que suelo acabar utilizando en la mayoría de mis programas. Así es como nace mi GameLibrary.


Creo que lo primero que añadí a la librería fue la clase FullScreenActivity. Una clase tremendamente sencilla, pero que me ahorra muchas líneas de código. También es verdad que a mí me gusta que todas mis actividades se presenten en pantalla completa, eliminando la barra de título. La clase en cuestión queda así:

1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class FullScreenActivity extends Activity {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  requestWindowFeature(Window.FEATURE_NO_TITLE);
  getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
    WindowManager.LayoutParams.FLAG_FULLSCREEN);
 }
}

Sólo tienes que hacer que una actividad herede de ella para tener una actividad de pantalla completa. Si sólo quieres aplicarlo a alguna clase concreta, mejor copiar las dos líneas de código directamente en ella.