No es un bug, es una característica no documentada

Mostrando entradas con la etiqueta Programación multimedia y dispositivos móviles. Mostrar todas las entradas
Mostrando entradas con la etiqueta Programación multimedia y dispositivos móviles. Mostrar todas las entradas

19/1/16

Programación multimedia. Intents implícitos

Cuando queremos interactuar con nuestro dispositivo Android desde una aplicación programada por nosotros, le deberemos indicar dos cosas:
  • Como queremos que se comporte nuestra aplicación (puede ser usando una funcionalidad del sistema que integremos en nuestra app, como ya vimos anteriormente al programar el navegador web), o la segunda opción, es decir que desde nuestra aplicación se ejecute algún proceso del sistema operativo independiente de nuestra app, como podría ser abrir la cámara de fotos, la aplicación de Gmail…
  • Los permisos que debemos atribuirle al Manifest para poder hacer uso de ese recurso del sistema.

En esta sección nos centraremos en la opción de lanzar recursos del sistema operativo desde nuestra app, a través de intents implícitos.

Para explicarlo en mejor detalle, vamos a realizar paso a paso el siguiente ejercicio:

Ejercicio: Realiza un proyecto de una aplicación semejante a la de la siguiente imagen



16/1/16

Programación multimedia. Configurar GenyMotion para Android Studio

A estas alturas, a no ser que tengamos un pepino de ordenador, nos habremos dado cuenta de que el emulador de smartphones de Android Studio va bastante lento, tarda en cargarse…

Por eso quiero hablaros de GenyMotion, un excelente emulador de Android que permitirá testear las aplicaciones y que es muy superior (es mi opinión) al que trae integrado por defecto Android Studio.

GenyMotion es una máquina virtual creada desde VirtualBox, que nos permitirá emular varios dispositivos que ya están preconfigurados y deberemos descargar, o modificar para poder crearnos el nuestro propio.

Lo primero es descargarnos la versión gratuíta del software desde la página oficial https://www.genymotion.com/#!/ (previa creación de una cuenta) y lo instalamos.


Abrimos el panel de control de la aplicación y pulsar sobre el botón de + para añadir un nuevo dispositivo Smartphone.


9/11/15

Programación multimedia. Menús

Antes de empezar hay que saber que a partir de la versión 3 de Android los menús han sido relegado a un segundo plano en sustitución de la ActionBar. De hecho a partir de la API 11 los menús definidos aparecerán en el menú desplegable de la ActionBar en lugar de en la sección inferior.

En Android hay tres tipos de menús:
  • Menús principales. Los más habituales, aparecen en la zona inferior de la pantalla al pulsar el botón menú del teléfono. 
  • Submenús. Son menús secundarios que se pueden mostrar al pulsar sobre una opción de un menú principal.
  • Menús contextuales. Son los que aparecen al mantener una pulsación larga en algún elemento de la pantalla.

Para mostrar un menú vamos a tener dos alternativas.
  • La primera es mediante la definición del menú en un fichero XML definido en la ruta res/menú
  • La segunda creándolo a través de código directamente.

Menús clásicos

Supongamos que queremos montar un menú con dos opciones, Configuración y Acerca de definiéndolo en XML.


Vamos a la ruta res/menú y abrimos el archivo que nos aparece por defecto. 


3/11/15

Programación multimedia. Mejoras en diseño

Para mejorar el diseño de nuestra aplicación tenemos diversos elementos que nos podrán ayudar.

En las siguientes páginas vamos a hacer un repaso por cada uno de ellos, a saber:
  • Lanzadores. Representa la aplicación en la pantalla principal
  • Barra de estados. Barra con pequeños iconos que nos alertan de notificaciones
  • Menú. Cada actividad puede tener su propio menú
  • Barra de acciones. Situada en la parte superior, fue creada para que el usuario tuviera una experiencia unificada a través de distintas aplicaciones.
  • Pestañas
  • Otros…


Programación multimedia. Procesos

En la mayoría de los casos, una aplicación Android es independiente y se ejecuta dentro de su propio proceso.

Cada proceso tiene su propia máquina virtual de Java que a su vez reside en un proceso de Linux – Kernel administrado.

El proceso es creado para ejecutar el código de la aplicación y es el sistema quien pedirá y reclamará su memoria para reasignarla a otra aplicación.

A cada aplicación se le asigna un identificador de usuario (UID) distinto. Los permisos de los archivos que refieren a la aplicación (caché, datos, etc.) son solo accesibles por dicho usuario.

Tiempo de vida de un proceso 

Android como SO usa el 100% de la memoria RAM. Si luego otro programa tiene que acceder a la memoria RAM, libera memoria para poder acoger a dichas aplicaciones.

El tiempo de vida de un proceso no está controlado directamente por la aplicación y el usuario. Es el sistema el que lo gestiona.


Prioridad de los procesos / Estados de una actividad



25/10/15

18/10/15

Programación multimedia. Botones personalizados

Vamos a ver cómo se pueden personalizar los botones de nuestra aplicación, pero no modificando bordes del botón, tamaño y demás, estoy hablando de crear botones a partir de imágenes que consigan sensación de movimiento al realizar determinada acción. Veamos cómo.

Sobre un nuevo proyecto, creamos el fichero boton.xml en el directorio res/drawable y escribimos el siguiente código:


Este XML define un recurso único gráfico (drawable) que cambiará en función del estado del botón. El primer <ítem> define la imagen usada cuando se pulsa el botón, el segundo <ítem> define la imagen usada cuando el botón tiene el foco (cuando el botón está seleccionado con la rueda de desplazamiento o las teclas de dirección), el tercero la imagen en estado normal.

El orden de los <ítem> es importante. Cuando se va a dibujar se recorrer los ítems en orden hasta que se cumpla una condición. Debido a que “botonnormal” es el último, sólo se aplica cuando las condiciones state_pressed y state_focused no se cumplen.

Las imágenes usadas para crear el botón son las siguientes


botonnormal.png


botonpulsado.png


botonconfoco.png

Al escribir el código así de primeras saldrá error al escribir la ruta de las imágenes. Eso se arrastra agregándolas a la carpeta res/drawable del proyecto.

Ahora en el diseño del Layout eliminamos el TextView existente (como siempre) y especificamos que el RelativeLayout tenga la propiedad Background blanca (#FFFFFF en hexadecimal).


Dentro del RelativeLayout arrastramos un Button y le indicamos que su propiedad Background sea Drawable/boton. Esa opción la encontraremos entrando en el selector de recursos (los puntos suspensivos)


Modificamos el atributo Text para que no tenga ningún valor


Ve al fichero con las funciones programables java e introduce al final, antes de la última llave, el siguiente código


Vuelve al diseño del Layout y modifica la propiedad onClick dándole el valor sePulsa.


Y al ejecutar el programa podremos ver cómo se comporta nuestro botón, animándose y mostrando un mensaje cuando es pulsado.

Programación multimedia. Controles de selección

Android dispone de diversos controles que nos permiten seleccionar una opción dentro de una lista de posibilidades
  • Spinner
  • ListView
  • GridView
  • Gallery

Asímismo también tenemos adaptadores. Los adaptadores sirven para enlazar los datos con alguna de estas listas anteriores. Hay de varios tipos
  •  ArrayAdapter. Provee datos a la lista de selección a partir de un array de objetos
  • SimpleCursorAdapter. Enlaza el control con datos devueltos en un cursor (por ejemplo para consultar a una base de datos o a un ContentProvider).
  • ListAdapter
  • SpinnerAdapter
  • Adaptadores propios que definimos nosotros

ArrayAdapter

Sirve para ligar clases de AdapterView con un array de objetos. Por defecto <String>

Los pasos básicos a seguir son:

En el código hacemos la llamada a un Spinner ya creado
sp1 = (Spinner)findViewById(…)

Se crea el adaptador
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, opción)

Mostramos una interfaz gráfica ya definida en Android
adapter.setDropDownViewResource(android.R.layout.simple_spinner_item);

Asociamos el adaptador a un spinner
sp1.setAdapter(adapter);

Y con el siguiente método indicamos que debe hacer cuando cambiar de opción
sp1.setOnItemSelectedListener(this)

Manejando ListAdapter 

Para aprender a manejar los ListAdapter vamos a hacer un ListAdapter que muestre varias asignaturas e indiquen a que curso pertenece (primero o segundo).

Sección diseño


En el Layout de la actividad principal insertamos un ListView (situado en el grupo Containers), y habrá que darle un id ya definido por Android (@android:id/list) para poder utilizar el ListAdapter. Para hacernos una idea, esto es un ListView en modo diseño


Sección XML

Ahora en el fichero res/values/strings.xml hay que crear dos arrays de string. Uno llamado “curso” que tenga tres ítems con valores 1º, 1º y 2º respectivamente y otro llamado “opcion” que tenga tres ítem con valores BD, XML y Android.

¿Cómo se crean estos arrays de string?

<string-array name="curso">
     <item>Primer valor</item>
     <item>Segundo valor</item>
     <item>Tercer valor</item>
</string-array>

Sección java

A continuación modificamos el fichero MainActivity.java para que su contenido sea (con los import necesarios) el siguiente e interpreta su contenido.

public class MainActivity ListActivity{

     private TextView tv2;
     private ListView lv1;
     private String[] cursos;
     private String[] asignaturas;
    
     @Override
     public void onCreate(Bundle savedInstanceState){
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          tv2 = (TextView)findViewById(R.id.textView2);
          lv1 = (ListView)findViewById(android.R.id.list);
          asignaturas = getResources().getStringArray(R.array.opcion);
          ListAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, asignaturas);
          lv1.setAdapter(adapter);
     }
    
     @Override
     protected void onListItemClick(ListView l, View v, int position, long id){
          super.onListItemClick(l, v, position, id);
          cursos = getResources().getStringArray(R.array.curso);
          tv2.setText(l.getItemAtPosition(position) + " es de " + cursos[position]);
     }
}

Del siguiente código hay dos cosas a destacar:

El extends a ListActivity se realiza porque la clase ListActivity nos proporciona los métodos útiles para trabajar con ListAdapter.

El método onListItemClick es el encargado de detectar una pulsación sobre un elemento del ListActivity. Sus parámetros son:
  • l: El ListView donde ocurrió el click
  • v: La vista (View) que fue pulsada
  • position. La posición de la vista en la lista
  • id: El identificador de la fila del ítem que fue pulsado

Mi aplicación de ejemplo

El Layout tiene el siguiente aspecto


Con los componentes


Strings.xml

<resources>
    <string name="app_name">ListAdapter</string>

    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>
   
    <string-array name="curso">
        <item>1º</item>
        <item>1º</item>
        <item>2º</item>
    </string-array>
   
    <string-array name="opcion">
        <item>BD</item>
        <item>XML</item>
        <item>Android</item>
    </string-array>
</resources>

Código Java (enmarcado el código modificado)

package programaciones.inazio.listadapter;

import android.app.ListActivity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;

import org.w3c.dom.Text;

public class Listas extends ListActivity {

    private TextView resultado;
    private ListView lista;
    private String[] cursos;
    private String[] asignaturas;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_listas);
        resultado = (TextView)findViewById(R.id.lblResultado);
        lista = (ListView)findViewById(android.R.id.list);

        asignaturas = getResources().getStringArray(R.array.opcion);
        ListAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, asignaturas);
        lista.setAdapter(adapter);
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id){
        super.onListItemClick(l, v, position, id);
        cursos = getResources().getStringArray(R.array.curso);
        resultado.setText(l.getItemAtPosition(position) + " es de " + cursos[position]);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_listas, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Y al ejecutar la aplicación, podemos ver el resultado correcto

Programación multimedia. Segunda aplicación. Transparentando una imagen

Desarrolla una aplicación que aplique transparencia a una imagen, teniendo en cuenta que el método setAlpha() está obsoleto a partir de API 16 y que el método setImageAlpha() no es válido para API 8, solo a partir de API 11. Ambos métodos indican su valor por entero (0 transparente, 255 opaco).

La guía del Layout es la siguiente


Como siempre, mi Layout es un poco distinto, siendo así


Mi idea era crear una barra de porcentaje de 0 a 100 y que conforme fuera moviéndose la imagen modificara su transparencia, pero he tenido problemas para buscar la información para llevar a cabo esa idea (la acabaré haciendo más adelante), así que lo que he hecho es capturar un porcentaje de texto de 0 a 100, mostrar un mensaje de advertencia en caso de que se introduzca un número fuera de ese rango, y calcular el porcentaje introducido sobre la escala de transparencia de 0 a 255.

Los componentes usados son


El código programado para los eventos es este (en cuadrado la parte realizada por mí, lo demás está generado automáticamente).

package es.claver.inazio.transparencia;

import android.annotation.TargetApi;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;

public class Transparencia extends AppCompatActivity {

    private Button btnConversor;
    private ImageView ordicuso;
    private EditText txtPorcentaje;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_transparencia);
        btnConversor = (Button)findViewById(R.id.btnCambiar);
        btnConversor.setOnClickListener(new View.OnClickListener() {
            @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
            @Override
            public void onClick(View v) {
                ordicuso = (ImageView)findViewById(R.id.imgOrdicuso);
                txtPorcentaje = (EditText)findViewById(R.id.txtPorcentaje);
                double p = Double.parseDouble(txtPorcentaje.getText().toString());

                if (p > 100 || p < 0){
                    Toast t = Toast.makeText(getApplicationContext(), "Valor de transparencia no permitido", Toast.LENGTH_SHORT);
                    t.show();
                }
                else{
                    double r = (255 / 100) * p;
                    int porcentaje = (int)Math.floor(r);
                    ordicuso.setImageAlpha(porcentaje);
                }
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_transparencia, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Es importante recalcar que el número capturado para el porcentaje hay que hacerlo en double (o float, algo que permita trabajar con decimales) y luego parsear el resultado a un entero para cargarlo en el método de transparencia. El resultado sería algo tal que así.




Por cierto, la imagen es una vista de los ibones de Ordicuso. Aprovechad a visitarlos, es un paisaje precioso con formaciones espectaculares y por desgracia están desapareciendo…

Versión 2

Mejora la aplicación anterior para conseguir cambiar el color de fondo (método setBackgroundResource()).

La ayuda del ejercicio es este Layout, pero como siempre me lo saltaré a la torera…


Mi idea es generar un color aleatorio cada vez que se pulse el botón que acabo de insertar.

El layout quedará de la siguiente forma


Y el código para cambiarlo es:

package es.claver.inazio.transparencia;

import android.annotation.TargetApi;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.provider.CalendarContract;
import android.support.annotation.DrawableRes;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Layout;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;

public class Transparencia extends AppCompatActivity {

    private Button btnConversor;
    private ImageView ordicuso;
    private EditText txtPorcentaje;
    private Button btnColor;
    private RelativeLayout relLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_transparencia);
        btnConversor = (Button)findViewById(R.id.btnCambiar);
        btnConversor.setOnClickListener(new View.OnClickListener() {
            @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
            @Override
            public void onClick(View v) {
                ordicuso = (ImageView)findViewById(R.id.imgOrdicuso);
                txtPorcentaje = (EditText)findViewById(R.id.txtPorcentaje);
                double p = Double.parseDouble(txtPorcentaje.getText().toString());

                if (p > 100 || p < 0){
                    Toast t = Toast.makeText(getApplicationContext(), "Valor de transparencia no permitido", Toast.LENGTH_SHORT);
                    t.show();
                }
                else{
                    double r = (255 / 100) * p;
                    int porcentaje = (int)Math.floor(r);
                    ordicuso.setImageAlpha(porcentaje);
                }
            }
        });

        // Código para cambiar color de fondo
        btnColor = (Button)findViewById(R.id.btnNuevoColor);
        btnColor.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                getWindow().setBackgroundDrawable(new ColorDrawable(Color.argb(Color.alpha(generarColor()), Color.red(generarColor()), Color.green(generarColor()), Color.blue(generarColor()))));

            }
        });
    }

    public int generarColor(){
        return (int)Math.random()*255+1;
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_transparencia, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Y cada vez que pulsemos el botón se modificará el color de la app.