Diferencia entre revisiones de «PDM Activities»

De MediaWiki
Ir a la navegación Ir a la búsqueda
 
(No se muestran 17 ediciones intermedias de otro usuario)
Línea 13: Línea 13:
  
  
 +
'''Preparación previa:'''
 +
* Partimos que xa temos creado o proxecto inicial como [https://wiki.cifprodolfoucha.es/index.php?title=PDM_UD1_Co%C3%B1ecendoAndroidStudio#Creando_un_proxecto_Base xa indicamos anteriormente].
 +
: Se non o temos creado antes, crea un paquete de nome '''UD2''' e dentro deste outro paquete de nome '''Activities'''.
 +
[[Imagen:PDM_Activity_0A.jpg|350px|center]]
  
 +
: Vos debe quedar como na imaxe anterior. Indicar que fisicamente temos creados os cartafoles UD2/Activities pero como non existen clases no cartafol UD2/ Android Studio o oculta.
 
<br />
 
<br />
  
 
=Creando Activities=
 
=Creando Activities=
 +
 +
* En Android existen moitos tipos de 'Activities'.
 +
: Todas elas teñen en común que son ventás onde se van 'debuxar' elementos da interface do usuario.
 +
 +
* Neste punto imos centrarnos en dous tipos de activity:
 +
:* [https://developer.android.com/reference/android/app/Activity?hl=en Activity]
 +
:: Son activities clásicas onde o usuario van interaccionar con compoñentes gráficos.
 +
 +
:* [https://developer.android.com/reference/androidx/appcompat/app/AppCompatActivity AppCompatActivity]
 +
:: Son activities que derivan de Activity e que se atopan dentro da librería de compatibilidade v7.
 +
:: Teñen a mesma función que as de clase Activity (servir como pantallas para interaccionar con compoñentes gráficos). Inicialmente foron creadas para que se puidera empregar o AppBar en versións de Android anteriores a que aparecera.
 +
:: Existen novas funcionalidades que van poder ser empregadas en versións Android anteriores a que apareceran grazas o uso desta clase [https://wiki.cifprodolfoucha.es/index.php?title=PDM_UD1_Bibliotecas_de_compatibilidade como xa vimos anteriormente nesta Wiki].
 +
 +
:: Na versión actual de Android Studio (Sept.2019) xa crea por defecto as activities desta clase.
 +
 +
 +
  
 
* Cada vez que se crea unha activity, aparece unha nova entrada no arquivo '''AndroidManifiest.xml'''.
 
* Cada vez que se crea unha activity, aparece unha nova entrada no arquivo '''AndroidManifiest.xml'''.
 
: Vexamos un exemplo, e crearemos unha nova activity no asistente:
 
: Vexamos un exemplo, e crearemos unha nova activity no asistente:
<gallery caption="Creando unha nova activity" widths="350" heights="300px" perrow="2">
+
<gallery caption="Creando unha nova activity no paquete UD2.Activities" widths="350" heights="300px" perrow="2">
 
Image:PDM_Activity_1.jpg| Creamos unha 'Empty Activity'. Podemos crear diferentes activities cunha serie de compoñentes gráficas xa engadidas previamente e así aforrarnos traballo.
 
Image:PDM_Activity_1.jpg| Creamos unha 'Empty Activity'. Podemos crear diferentes activities cunha serie de compoñentes gráficas xa engadidas previamente e así aforrarnos traballo.
Image:PDM_Activity_2.jpg| Non marcamos nin a opción de que poda ser lanzada nin a biblioteca de compatibilidade.
+
Image:PDM_Activity_2.jpg| '''Non marcamos a opción de que poda ser lanzada''' (Launcher Activity).  
Image:PDM_Activity_3.jpg| Como vemos no arquivo de AndroidManifiest.xml se crea unha nova entrada para a activity creada. Antepón un punto xa que Android para lanzar a activity fai uso do identificar completo formado polo nome do paquete e o nome da activity.
+
Image:PDM_Activity_3.jpg| Como vemos no arquivo de AndroidManifiest.xml se crea unha nova entrada para a activity creada. Antepón un punto xa que Android para lanzar a activity fai uso do identificar completo formado polo nome do paquete e o nome da activity (vos aparecerá un paquete diferente ao da imaxe, pero o concepto é o mesmo).
Image:PDM_Activity_4.jpg| Como sucede en Java, o nome da clase ten que ser o mesmo que o nome físico do arquivo onde está creada. Este nome ten que corresponder có nome indicado no AndroidManifiest.xml
+
Image:PDM_Activity_4.jpg| Como sucede en Java, o nome da clase ten que ser o mesmo que o nome físico do arquivo onde está creada. Este nome ten que corresponder có nome indicado no AndroidManifiest.xml.
 
Image:PDM_Activity_4B.jpg| Se ao crear unha activity vos da un erro coa clase R, pode ser debido a que non teñades importado dita clase.
 
Image:PDM_Activity_4B.jpg| Se ao crear unha activity vos da un erro coa clase R, pode ser debido a que non teñades importado dita clase.
 
Image:PDM_Activity_4C.jpg| Se non tedes [http://wiki.cifprodolfoucha.es/index.php?title=PDM_UD1_Co%C3%B1ecendoAndroidStudio#Automaticamente a importación automática de clases] teredes que escribilo manualmente o sobre o erro (neste caso teriades que situar o cursor sobre a clase R) premer as teclas '''Alt+Enter''', escollendo a opción '''import class'''. Se tedes activada a importación automática soamente tedes que premer en calquera liña da activity.
 
Image:PDM_Activity_4C.jpg| Se non tedes [http://wiki.cifprodolfoucha.es/index.php?title=PDM_UD1_Co%C3%B1ecendoAndroidStudio#Automaticamente a importación automática de clases] teredes que escribilo manualmente o sobre o erro (neste caso teriades que situar o cursor sobre a clase R) premer as teclas '''Alt+Enter''', escollendo a opción '''import class'''. Se tedes activada a importación automática soamente tedes que premer en calquera liña da activity.
 
Image:PDM_Activity_5.jpg| Para cambiar o nome podemos facer uso da utilidade Refactor => Rename. Premendo o botón dereito sobre o recurso que queiramos cambiar, escolleremos estas opcións y automaticamente cambiará o nome en todos los arquivos necesarios.
 
Image:PDM_Activity_5.jpg| Para cambiar o nome podemos facer uso da utilidade Refactor => Rename. Premendo o botón dereito sobre o recurso que queiramos cambiar, escolleremos estas opcións y automaticamente cambiará o nome en todos los arquivos necesarios.
 +
</gallery>
 +
 +
: <u>Nota:</u> Fixarse que na imaxe o nome do paquete está errado. Pon 'UD2.Acvities'. Probade a cambiar o nome coa mesma opción 'Refactor'.
 +
 +
 +
 +
<br />
 +
* Ao cambiar a forma en como Android Studio crea a activity por defecto (de clase AppCompatActivity), teremos dúas consecuencias:
 +
 +
:* Se [https://wiki.cifprodolfoucha.es/index.php?title=PDM_UD1_Bibliotecas_de_compatibilidade#Versi.C3.B3n_antiga empregamos a opción antiga das bibliotecas de compatabilidade]:
 +
::<gallery caption="Empregando AppCompactActivity con bibliotecas de compatibilidade antigas" widths="350" heights="300px" perrow="2">
 +
Image:PDM_Activity_8.jpg| A activity creada <u>xa non deriva da clase Activity</u>, se non da clase AppCompatActivity do paquete de compatibilidade v7.
 +
Image:PDM_Activity_9.jpg| Engadiuse ao arquivo '''build.gradle''' a biblioteca de compatibilidade xa que facemos uso dela na activity creada.
 +
</gallery>
 +
 +
<br />
 +
:* Se [https://wiki.cifprodolfoucha.es/index.php?title=PDM_UD1_Bibliotecas_de_compatibilidade#Versi.C3.B3n_actual empregamos a nova forma androidx] teremos que:
 +
::<gallery caption="Empregando AppCompactActivity con bibliotecas de compatibilidade androidx" widths="350" heights="300px" perrow="2">
 +
Image:PDM_Activity_8B.jpg| A activity creada <u>xa non deriva da clase Activity</u>, se non da clase AppCompatActivity do paquete de compatibilidade androidx.appcompat.app.
 +
Image:PDM_Activity_9B.jpg| Engadiuse ao arquivo '''build.gradle''' a biblioteca de compatibilidade xa que facemos uso dela na activity creada.
 
</gallery>
 
</gallery>
  
  
  
* Como vimos no asistente, á hora de crear unha activity aparecen dúas opcións que non marcamos:
+
 
 +
 
 +
 
 +
 
 +
 
 +
 
 +
* Como vimos no asistente, á hora de crear unha activity aparece unha opción que indica Launcher Activity:
 
:* Launcher Acivity: Fai que dita activity poida ser lanzada ou executada de forma independente. Isto quere dicir que o resto de activities que non teñen marcada esta opción van necesitar ser invocadas dende outra activity. Ao crear unha activity e marcar esta opción, levará consigo dúas consecuencias:
 
:* Launcher Acivity: Fai que dita activity poida ser lanzada ou executada de forma independente. Isto quere dicir que o resto de activities que non teñen marcada esta opción van necesitar ser invocadas dende outra activity. Ao crear unha activity e marcar esta opción, levará consigo dúas consecuencias:
  
 
<gallery caption="Creando unha nova activity coa opción Launcher actividada" widths="350" heights="300px" perrow="2">
 
<gallery caption="Creando unha nova activity coa opción Launcher actividada" widths="350" heights="300px" perrow="2">
 +
Image:PDM_Activity_6A.jpg| Creamos unha segunda activity no mesmo paquete (UD2.Activities) coa opción Launcher Actividada.
 
Image:PDM_Activity_6.jpg| Engade unha sección nova á activity no arquivo AndroidManifiest.xml. Xa veremos para que serve pero esas dúas liñas estalle a indicar ao S.O. Android de que activity pode ser lanzada de forma independente e que non vai recibir datos doutra activity.
 
Image:PDM_Activity_6.jpg| Engade unha sección nova á activity no arquivo AndroidManifiest.xml. Xa veremos para que serve pero esas dúas liñas estalle a indicar ao S.O. Android de que activity pode ser lanzada de forma independente e que non vai recibir datos doutra activity.
Image:PDM_Activity_7.jpg| Se ides ao dispositivo físico ou emulador podedes comprobar como aparecen as dúas activities que poden ser lanzadas de forma independente.
+
Image:PDM_Activity_7.jpg| Se lanzadas a aplicación e ides ao dispositivo físico ou emulador podedes comprobar como aparecen as dúas activities que poden ser lanzadas de forma independente.
 
</gallery>
 
</gallery>
  
  
  
:* A outra opción é a de '''Backwards Compatibility''': Esta opción está colocada para facer uso das bibliotecas de compatibilidade de Android, xa vistas [http://wiki.cifprodolfoucha.es/index.php?title=PDM_UD1_Bibliotecas_de_compatibilidade nesta wiki anteriormente].
 
:: Ao marcar esta opción teremos dúas consecuencias:
 
 
<gallery caption="Creando unha nova activity coa opción Backwards Compatibility" widths="350" heights="300px" perrow="2">
 
Image:PDM_Activity_8.jpg| A activity creada <u>xa non deriva da clase Activity</u>, se non da clase AppActivity do paquete de compatibilidade v7.
 
Image:PDM_Activity_9.jpg| Engadiuse ao arquivo '''build.gradle''' a biblioteca de compatibilidade xa que facemos uso dela na activity creada.
 
</gallery>
 
  
  
Línea 65: Línea 107:
 
<gallery caption="Creando un novo paquete" widths="350" heights="300px" perrow="2">
 
<gallery caption="Creando un novo paquete" widths="350" heights="300px" perrow="2">
 
Image:PDM_Activity_12.jpg| Botón dereito sobre o paquete no que queremos crear un subpaquete.
 
Image:PDM_Activity_12.jpg| Botón dereito sobre o paquete no que queremos crear un subpaquete.
Image:PDM_Activity_13.jpg| Introducimos o nome do paquete a crear.
+
Image:PDM_Activity_13.jpg| Introducimos o nome do paquete a crear. Lembrar que cada paquete está dentro dun cartafol. O paquete UD2 non aparece xa que non hai clases nel. Se abrimos o explorador de arquivos e creamos un arquivo dentro do cartafol UD2 veremos como Android Studio o amosa.
 
Image:PDM_Activity_14.jpg| Arrastramos as activities ao novo paquete. Sairá unha ventá e premeremos o botón '''Refactor'''. Se vos da algún tipo de erro probade a arrastralas dunha en unha e limpar o proxecto e volvelo a compilar.
 
Image:PDM_Activity_14.jpg| Arrastramos as activities ao novo paquete. Sairá unha ventá e premeremos o botón '''Refactor'''. Se vos da algún tipo de erro probade a arrastralas dunha en unha e limpar o proxecto e volvelo a compilar.
 +
Image:PDM_Activity_14B.jpg| Prememos o botón de 'Refactor'.
 
Image:PDM_Activity_15.jpg| Fixarse como agora no AndroidManifiest.xml aparece o novo paquete.
 
Image:PDM_Activity_15.jpg| Fixarse como agora no AndroidManifiest.xml aparece o novo paquete.
 
</gallery>
 
</gallery>
Línea 80: Línea 123:
  
  
* Cando se lanza unha activity esta pasa por unha serie de estados e eses estados chaman a unha serie de métodos.
+
* Unha aplicación de Android pode estar conformada por moitas activities (pantallas) e incluso llegar a chamar a outras aplicación.
 +
: Cada vez que unha activity chama a outra, a primeira non se destrúe, se non que queda nunha cola LIFO (Stack).
 +
: Cando prememos o botón Back do móbil ou pechamos a activity actual, recupera a activity que estaba na cola.
 +
[[Imagen:PDM_Ciclo_Vida_1.jpg|400px|center]]
 +
 
 +
 
 +
 
 +
 
 +
 
 +
* Cando se lanza unha activity esta pasa por unha serie de estados e eses estados chaman a unha serie de métodos, ao igual que cando pechamos ou abrimos unha nova activity.
  
[[Imagen:00_basic-lifecycle.png|500px|center]]
+
[[Imagen:00_basic-lifecycle.png|800px|center]]
  
 
<u>Nota:</u> O estado '''Resumed:''' ven ser o estado de running.
 
<u>Nota:</u> O estado '''Resumed:''' ven ser o estado de running.
  
  
 +
* Veremos [http://wiki.cifprodolfoucha.es/index.php?title=Ciclo_de_vida_dunha_aplicaci%C3%B3n máis adiante nesta Wiki] diferentes métodos para gardar e recuperar o estado dunha Activity.
  
 
<br />
 
<br />
 +
==Métodos asociados aos estados==
 +
 
* Estes son os diferentes métodos e o que se debería facer en cada un deles:
 
* Estes son os diferentes métodos e o que se debería facer en cada un deles:
  
Línea 94: Línea 149:
 
::* Definir / instanciar variables – clases  
 
::* Definir / instanciar variables – clases  
 
::* Definir a interface do usuario.
 
::* Definir a interface do usuario.
 +
::* Vincular datos a listas.
  
 
:: Unha vez chamado o método onCreate chama os métodos onStart e onResume de forma consecutiva.
 
:: Unha vez chamado o método onCreate chama os métodos onStart e onResume de forma consecutiva.
Línea 99: Línea 155:
  
  
:* '''Método onDestroy''': Chamado cando remata a aplicación e se libera da memoria do dispositivo. Tamén pode ser provocado polo S.O. para liberar espazo de memoria. Normalmente non se fai nada neste método, xa que a liberación de recursos se fai nos métodos onPause e onStop, pero se a aplicación usa background Threads ou outro recurso que poida consumir memoria, debe ser destruído neste evento.
+
:* '''Método onDestroy''': Chamado cando remata a aplicación e se libera da memoria do dispositivo, cando pechamos unha activity ou cando prememos o botón BACK. Tamén pode ser provocado polo S.O. para liberar espazo de memoria. Se a aplicación usa background Threads ou outro recurso que poida consumir memoria, debe ser destruído neste evento. Normalmente liberaremos os recursos que foron 'instanciados' no método onCreate.
  
 
:: <u>Nota:</u> Só hai un caso no que non pasa polos métodos de onPause e onStop, é cando destruímos a aplicación chamando o método finish() dende onCreate.
 
:: <u>Nota:</u> Só hai un caso no que non pasa polos métodos de onPause e onStop, é cando destruímos a aplicación chamando o método finish() dende onCreate.
Línea 121: Línea 177:
 
:: Hai que ter en conta que entramos neste método cando vimos de inicializar a aplicación e cando paramos a aplicación, polo que pode ser necesario verificar se xa estaban creados os recursos antes de volver a crealos.
 
:: Hai que ter en conta que entramos neste método cando vimos de inicializar a aplicación e cando paramos a aplicación, polo que pode ser necesario verificar se xa estaban creados os recursos antes de volver a crealos.
  
:: Vexamos un suposto exemplo no que facemos uso da cámara. Como vemos comprobamos se o obxecto que vai facer uso da cámara está xa inicializado.
+
:: A aplicación permanece neste estado ata que se cambie de activity ou
 +
 
 +
:: Vexamos un suposto exemplo no que facemos uso da cámara. Como vemos comprobamos se o obxecto que vai facer uso da cámara está xa inicializado. Isto pode pasar nunha aplicación multiventana no que pasemos o foco a outra ventá e despois volvamos á ventá inicial.
 
<syntaxhighlight lang="java" line highlight="">
 
<syntaxhighlight lang="java" line highlight="">
 
     @Override
 
     @Override
Línea 156: Línea 214:
 
:: No método onRestart se poden recuperar cursores de acceso a datos que foran pechados previamente no método onStop. Máis información [https://developer.android.com/reference/android/app/Activity#onRestart() neste enlace].
 
:: No método onRestart se poden recuperar cursores de acceso a datos que foran pechados previamente no método onStop. Máis información [https://developer.android.com/reference/android/app/Activity#onRestart() neste enlace].
  
 +
:: A aplicación non permanece nestes estados, se non que pasa directamente ao estado Resumed e chama ao método asociado onResume().
  
  
  
<br />
+
* '''<u>NOTA IMPORTANTE:</u>''' Se sobreescribides calquera destes métodos tedes que chamar en primeiro lugar ao método da superclase da forma: super.Método();
 
 
=Recreando unha Activity=
 
 
 
* Existen varias casos que poden provocar a eliminación da nosa aplicación en segundo plano, coma son que non se usa en bastante tempo (e permaneza en background) ou que o sistema necesite memoria e teña que liberar recursos coma son os procesos en background.
 
 
 
* Nestes casos, se o usuario quere volver á nosa aplicación, o sistema operativo garda anteriormente o ‘estado’ en que quedou a nosa aplicación, no que se coñece coma ‘instance state’ ou estado de instancia. Dito estado está gardado nun obxecto da clase Bundle que garda pares de clave-valor.
 
: Por iso cando volvemos os elementos gráficos están tal como os deixamos (caixas de texto con datos, elementos de lista escollidos,...)
 
: Soamente se se destrúe a aplicación (por necesidades de memoria, por exemplo) estes datos se perden.
 
 
 
 
 
: <u>Nota:</u> cando un usuario move a pantalla e a reorienta noutra dirección, a aplicación se destrúe e se volve a crear.
 
 
 
 
 
* Seguramente a aplicación ten outra información aparte do estado das View´s que é necesario recuperar, como as variables usadas para gardar o progreso do usuario ou calquera outra información que nos como programadores queiramos gardar.
 
 
 
: Para iso existe o método onSaveInstanceState que chama o sistema cando a aplicación pasa a segundo plano ou se destrúe.
 
[[Imagen:PDM_Activity_16.jpg|400px|center]]
 
 
 
 
 
: A este método lle pasa o obxecto da [https://developer.android.com/reference/android/os/Bundle?hl=es-419 clase Bundle] onde está gardada toda a información (view´s) da aplicación, polo que o programador pode engadir máis información para ser recuperada posteriormente se o sistema volta a chamar a aplicación, xa que lle volve a pasar o mesmo obxecto da clase Bundle.
 
: O obxecto da clase Bundle funciona en base a pares '''CLAVE-VALOR'''. A clave e o identificar do valor que se vai pasar. Ven ser como o nome dunha variable en programación.
 
: Así teremos:
 
:* método '''putXXXXX(CLAVE,VALOR)''' sendo XXXXX un tipo de dato, por exemplo, putInt(), para engadir datos ao obxecto Bundle.
 
:* método '''getXXXXX(CLAVE)''''  sendo XXXXX un tipo de dato, por exemplo, getInt(), par recuperar o valor dun elemento clave gardado.
 
 
 
: Por exemplo, se quero gardar unha cadea nunha 'clave' de nome 'LOGIN', poñería:
 
:: obxectoBundle.putString("LOGIN","CADEA A GARDAR");
 
 
 
: Se quere recuperar dito valor, no mesmo obxecto bundle:
 
:: String login = obxectoBundle.getString("LOGIN");
 
 
 
 
 
: Loxicamente o nome da clave ten que ser a mesma cando gardamos o dato e cando o introducimos.
 
 
 
 
 
 
 
* Cando a aplicación se re-inicializa (isto é, cando se rota o dispositivo ou volvemos a aplicación unha vez que o S.O. liberou a súa memoria por necesidade) se chama ao '''método onRestoreInstanceState''', onde se recuperan os datos gardados.
 
 
 
: <u>Nota:</u> Antes de chegar o estado Resumed pasa polo estado Started.
 
 
 
 
 
 
 
* Exemplo:
 
<syntaxhighlight lang="java" line highlight="">
 
@Override
 
public void onSaveInstanceState(Bundle datosgardados){
 
 
datosgardados.putInt(“POSICION”,reproductor.getPosicionActualCancion());
 
datosgardados.putString(“CANCION”,reproductor.cancionactual);
 
 
 
// SEMPRE O CODIGO POSTO POR NOS ANTES DA CHAMADA A SUPER.
 
super.onSaveInstanceState(datosgardados);
 
}
 
</syntaxhighlight>
 
 
 
 
 
: Para restaurar o sistema usaremos o método onRestoreInstanceState.
 
<syntaxhighlight lang="java" line highlight="">
 
@Override
 
public void onRestoreInstanceState(Bundle datosgardados){
 
 
super.onRestoreInstanceState(datosgardados);
 
 
 
// CODIGO POSTO POR NOS PARA RECUPERAR OS DATOS, SEMPRE DESPOIS
 
// DA CHAMADA A SUPER
 
reproductor.cancionactual=datosgardados.getString(C_DATOCANCION_PECHAR);
 
reproductor.posicioncancion=datosgardados.getInt(C_DATOPOSCANCION_PECHAR);
 
 
}
 
 
 
</syntaxhighlight>
 
 
 
:<u>Nota:</u> É importante chamar os métodos super.XXXXX na orde indicada no exemplo anterior.
 
 
 
 
 
* Imos ver un exemplo práctico disto.
 
: Crea un novo paquete de nome '''gardandoestado'''.
 
: Dentro do paquete crea unha nova clase de tipo '''Launcher Activity''' de nome '''UD2_01_gardando_estado'''.
 
: Cambia o contido do arquivo do Layout XML por este:
 
 
 
 
 
<syntaxhighlight lang="java" line highlight="">
 
<?xml version="1.0" encoding="utf-8"?>
 
<android.support.constraint.ConstraintLayout 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:layout_width="match_parent"
 
    android:layout_height="match_parent"
 
    tools:context=".gardandoestado.UD2_01_gardando_estado">
 
 
 
 
 
    <EditText
 
        android:id="@+id/editText"
 
        android:layout_width="wrap_content"
 
        android:layout_height="wrap_content"
 
        android:layout_marginBottom="8dp"
 
        android:layout_marginEnd="8dp"
 
        android:layout_marginStart="8dp"
 
        android:layout_marginTop="8dp"
 
        android:ems="10"
 
        android:inputType="textPersonName"
 
        android:text="Name"
 
        app:layout_constraintBottom_toBottomOf="parent"
 
        app:layout_constraintEnd_toEndOf="parent"
 
        app:layout_constraintStart_toStartOf="parent"
 
        app:layout_constraintTop_toTopOf="parent"
 
        app:layout_constraintVertical_bias="0.073" />
 
 
 
    <Button
 
        android:id="@+id/button1"
 
        android:layout_width="wrap_content"
 
        android:layout_height="wrap_content"
 
        android:layout_marginEnd="8dp"
 
        android:layout_marginStart="8dp"
 
        android:layout_marginTop="84dp"
 
        android:text="Dar Valor"
 
        app:layout_constraintEnd_toEndOf="parent"
 
        app:layout_constraintStart_toStartOf="parent"
 
        app:layout_constraintTop_toBottomOf="@+id/editText" />
 
 
 
    <Button
 
        android:id="@+id/button2"
 
        android:layout_width="wrap_content"
 
        android:layout_height="wrap_content"
 
        android:layout_marginEnd="8dp"
 
        android:layout_marginStart="8dp"
 
        android:layout_marginTop="48dp"
 
        android:text="Ver valor"
 
        app:layout_constraintEnd_toEndOf="parent"
 
        app:layout_constraintStart_toStartOf="parent"
 
        app:layout_constraintTop_toBottomOf="@+id/button1" />
 
 
 
</android.support.constraint.ConstraintLayout>
 
</syntaxhighlight>
 
 
 
 
 
* Cambio o contido da activity por este:
 
<syntaxhighlight lang="java" line highlight="">
 
package es.cursoandroid.cifprodolfoucha.aprendiendo.gardandoestado;
 
 
 
import android.app.Activity;
 
import android.os.Bundle;
 
import android.view.View;
 
import android.widget.Button;
 
import android.widget.Toast;
 
 
 
import es.cursoandroid.cifprodolfoucha.aprendiendo.R;
 
 
 
 
 
public class UD2_01_gardando_estado extends Activity {
 
 
 
    private int valor = 0;
 
 
 
    @Override
 
    protected void onCreate(Bundle savedInstanceState) {
 
        super.onCreate(savedInstanceState);
 
        setContentView(R.layout.activity_ud2_01_gardando_estado);
 
 
 
        Button btnBoton1 = findViewById(R.id.button1);
 
        btnBoton1.setOnClickListener(new View.OnClickListener() {
 
            @Override
 
            public void onClick(View v) {
 
                valor = 5;
 
                Toast.makeText(getApplicationContext(), "Valor: " + valor, Toast.LENGTH_LONG).show();
 
            }
 
        });
 
 
 
        Button btnBoton2 = findViewById(R.id.button2);
 
        btnBoton2.setOnClickListener(new View.OnClickListener() {
 
            @Override
 
            public void onClick(View v) {
 
                Toast.makeText(getApplicationContext(), "Valor: " + String.valueOf(valor), Toast.LENGTH_LONG).show();
 
            }
 
        });
 
 
 
 
 
    }
 
}
 
 
 
</syntaxhighlight>
 
 
 
 
 
* Se executamos esta aplicación podemos comprobar como cando se inicia o valor da variable vale 0.
 
: Modificaremos o contido da caixa de texto e premeremos no botón.
 
 
 
: Nese intre a nivel de programación a variable vale 5.
 
 
 
[[Imagen:PDM_Activity_20.jpg|500px|center]]
 
 
 
 
 
: Se marchamos da aplicación e volvemos, podemos comprobar como o contido da caixa de texto permanece e se prememos sobre o botón 'Ver Valor' tamén gardou o dato.
 
: Neste caso non teríamos que gardar o estado de nada para que a aplicación continuase (noutro caso, por exemplo, nun reprodutor de música poderíamos gardar a posición da canción que está sendo reproducida).
 
 
 
: Pero que pasa se o S.O. necesita memoria e elimina dita aplicación do Stack ?
 
: Tamén pasa o mesmo cando rotamos o dispositivos. A activity se 'recrea' novamente.
 
 
 
[[Imagen:PDM_Activity_21.jpg|500px|center]]
 
 
 
: Os datos perdéronse.
 
 
 
* Imos modificar o código para que se garden os datos introducidos:
 
<syntaxhighlight lang="java" line highlight="17-33">
 
package es.cursoandroid.cifprodolfoucha.aprendiendo.gardandoestado;
 
 
 
import android.app.Activity;
 
import android.os.Bundle;
 
import android.view.View;
 
import android.widget.Button;
 
import android.widget.Toast;
 
 
 
import es.cursoandroid.cifprodolfoucha.aprendiendo.R;
 
 
 
 
 
public class UD2_01_gardando_estado extends Activity {
 
 
 
    private int valor = 0;
 
 
 
    @Override
 
    public void onSaveInstanceState(Bundle datosgardados){
 
 
 
        datosgardados.putInt("VALOR",valor);
 
 
 
        // SEMPRE O CODIGO POSTO POR NOS ANTES DA CHAMADA A SUPER.
 
        super.onSaveInstanceState(datosgardados);
 
    }
 
 
 
    @Override
 
    public void onRestoreInstanceState(Bundle datosgardados){
 
        super.onRestoreInstanceState(datosgardados);
 
 
 
        // CODIGO POSTO POR NOS PARA RECUPERAR OS DATOS, SEMPRE DESPOIS
 
        // DA CHAMADA A SUPER
 
        valor  = datosgardados.getInt("VALOR");
 
 
 
    }
 
 
 
 
 
    @Override
 
    protected void onCreate(Bundle savedInstanceState) {
 
        super.onCreate(savedInstanceState);
 
        setContentView(R.layout.activity_ud2_01_gardando_estado);
 
 
 
        Button btnBoton1 = findViewById(R.id.button1);
 
        btnBoton1.setOnClickListener(new View.OnClickListener() {
 
            @Override
 
            public void onClick(View v) {
 
                valor = 5;
 
                Toast.makeText(getApplicationContext(), "Valor: " + valor, Toast.LENGTH_LONG).show();
 
            }
 
        });
 
 
 
        Button btnBoton2 = findViewById(R.id.button2);
 
        btnBoton2.setOnClickListener(new View.OnClickListener() {
 
            @Override
 
            public void onClick(View v) {
 
                Toast.makeText(getApplicationContext(), "Valor: " + String.valueOf(valor), Toast.LENGTH_LONG).show();
 
            }
 
        });
 
 
 
 
 
    }
 
}
 
</syntaxhighlight>
 
 
 
 
 
 
 
 
 
 
 
* Podemos comprobar como agora ao rotar a pantalla os datos permanecen.
 
[[Imagen:PDM_Activity_22.jpg|500px|center]]
 
 
 
 
 
 
 
 
 
* Neste caso o view é gardado polo propio android, xa que todos os <u>view´s cun id posto </u> gardan o seu estado.
 
: De todas formas pode ser necesario gardar o estado dalgún view, xa que pode ser inicializado no método onStart.
 
: Por exemplo, cargar unha ListBox cun conxunto de datos da base de datos, que o usuario elixa un deses datos.
 
: Se nese momento a actividade se recrea, ao iniciarse, se volverían a cargar os datos da ListBox, pero a elección do usuario se perdería.
 
  
  
Línea 443: Línea 224:
  
 
<br />
 
<br />
=Intent en Activities=
+
'''[https://wiki.cifprodolfoucha.es/index.php?title=Programaci%C3%B3n_de_dispositivos_m%C3%B3biles#UNIDADE_2:_A_interface_de_usuario. Enlace a la página principal de la UD2]'''
 
 
 
 
* Máis información [http://developer.android.com/training/basics/firstapp/starting-activity.html neste enlace].
 
 
 
* Para lanzar outra activity se fai uso dos Intent.
 
 
 
* '''<u>Nota Importante</u>:''' En Android cando lanzamos unha nova activity, a anterior queda en memoria formando unha cola LIFO. O pechar a nova activity aparecerá a anterior. SE QUEREMOS PECHAR UNHA ACTIVITY PARA ABRIR UNHA NOVA DEBEMOS DE ABRIR A NOVA ACTIVITY E CHAMAR Ó MÉTODO finish()  da activity que chama.
 
 
 
 
 
 
 
 
<br />
 
<br />
==Construíndo un Intent==
 
 
* http://developer.android.com/training/basics/firstapp/starting-activity.html
 
* http://developer.android.com/guide/components/intents-filters.html
 
 
 
 
* A efectos prácticos, unha activity vense a identificar cunha pantalla. Os intent´s vannos servir para chamar a ditas pantallas entre outras cousas.
 
: Tamén van servir para chamar a outras aplicacións ANDROID e esperar unha resposta (ou non).
 
 
* Un intent prové un enlace entre diferentes compoñentes coma poden ser dúas activity´s. Se pode traducir coma a intención da aplicación de facer algo.
 
: Cando chama, o pode facer indicando a Activity que quere lanzar ('''Intent Explícitos''') ou simplemente indica a acción que quere realizar e é o S.O. quen decide en base a acción e o tipo de información, que aplicación debe lanzar ('''Intent Implícitos''').
 
 
 
* Para chamar a unha activity dende outra temos o seguinte código:
 
<syntaxhighlight lang="java" line highlight="">
 
Intent intent = new Intent(this, DisplayMessageActivity.class);
 
</syntaxhighlight>
 
 
 
* O primeiro parámetro é unha referencia ó contexto (a clase Activity é unha subclase de Context, por iso poñemos this).
 
: O segundo parámetro é a clase que o Sistema Operativo ‘intentará’ cargar (no noso caso a activity a cargar).
 
 
 
* <u>Nota:</u> O '''context''' é unha interface que nos permite acceder os recursos da aplicación, clases e outras operacións, como acceso as activities…
 
 
 
 
* Imos velo cun exemplo.
 
: Indicar que no exemplo aparece código que aínda non vimos.
 
: Deberedes centrarvos no uso do Intent. Volveremos posteriormente a este código cando avancemos nas explicacións.
 
 
 
<gallery caption="Exemplo de Intent Explícito" widths="350" heights="300px" perrow="2">
 
Image:PDM_Activity_23.jpg| A primeira activity terá un botón que ao premerse lanzará a segunda activity.
 
Image:PDM_Activity_24.jpg| Na segunda activity se prememos o botón pechar, fará un finish() da activity aparecendo a primeira.
 
</gallery>
 
 
 
 
* Crear un novo paquete de nome '''activities''' (ver [http://wiki.cifprodolfoucha.es/index.php?title=PDM_Creando_proxecto_base no enlace como crear novos paquetes]).
 
 
 
* Faremos unha pantalla cun botón e ao premelo, lanzaramos a segunda activity.
 
: Esta segunda activity terá un botón para pecharse.
 
 
: Crea unha 'Empty Activity' de nome '''UD02_01_A1_activities''' (UD2, Exercicio01, A1, sección Activities), sen compatibilidade y de tipo 'launcher'.
 
: O código XML do layout:
 
<syntaxhighlight lang="java" line highlight="">
 
<?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:layout_width="match_parent"
 
    android:layout_height="match_parent" >
 
 
 
    <Button
 
        android:id="@+id/button"
 
        android:layout_width="wrap_content"
 
        android:layout_height="wrap_content"
 
        android:layout_alignParentStart="true"
 
        android:layout_alignParentTop="true"
 
        android:text="Lanzar"
 
        android:onClick="lanzar_activity"
 
        />
 
</RelativeLayout>
 
</syntaxhighlight>
 
 
 
 
 
: Crea unha 'Empty Activity' de nome '''UD02_01_A2_activities''' (UD2, Exercicio01, Acivity 2ª, sección Activities), sen compatibilidade y <u>non 'launcher'</u>.
 
: O código XML do layout:
 
<syntaxhighlight lang="java" line highlight="">
 
<?xml version="1.0" encoding="utf-8"?>
 
<android.support.constraint.ConstraintLayout 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:layout_width="match_parent"
 
    android:layout_height="match_parent"
 
    tools:context=".activities.UD02_01_A2_activities">
 
 
    <TextView
 
        android:id="@+id/textView"
 
        android:layout_width="135dp"
 
        android:layout_height="24dp"
 
        android:layout_marginStart="8dp"
 
        android:layout_marginTop="8dp"
 
        android:text="Segunda Activity"
 
        app:layout_constraintStart_toStartOf="parent"
 
        app:layout_constraintTop_toTopOf="parent" />
 
 
    <Button
 
        android:id="@+id/button3"
 
        android:layout_width="wrap_content"
 
        android:layout_height="wrap_content"
 
        android:layout_marginStart="28dp"
 
        android:layout_marginTop="24dp"
 
        android:text="Pechar"
 
        app:layout_constraintStart_toStartOf="parent"
 
        android:onClick="pecharActivity"
 
        />
 
</android.support.constraint.ConstraintLayout>
 
</syntaxhighlight>
 
 
 
 
 
* O código da activity '''UD02_01_A1_activities''':
 
<syntaxhighlight lang="java" line highlight="">
 
package es.cursoandroid.cifprodolfoucha.aprendiendo.activities;
 
 
import android.app.Activity;
 
import android.content.Intent;
 
import android.os.Bundle;
 
import android.view.View;
 
 
import es.cursoandroid.cifprodolfoucha.aprendiendo.R;
 
 
public class UD02_01_A1_activities extends Activity {
 
 
    protected void lanzar_activity(View v){
 
        Intent intent = new Intent(getApplicationContext(),UD02_01_A2_activities.class);    // INTENT EXPLICITO
 
        startActivity(intent);
 
    }
 
 
    @Override
 
    protected void onCreate(Bundle savedInstanceState) {
 
        super.onCreate(savedInstanceState);
 
        setContentView(R.layout.activity_ud02_01__a1_activities);
 
    }
 
}
 
 
</syntaxhighlight>
 
 
 
* O código da segunda activity '''UD02_01_A2_activities''':
 
<syntaxhighlight lang="java" line highlight="">
 
package es.cursoandroid.cifprodolfoucha.aprendiendo.activities;
 
 
import android.app.Activity;
 
import android.os.Bundle;
 
import android.view.View;
 
 
import es.cursoandroid.cifprodolfoucha.aprendiendo.R;
 
 
public class UD02_01_A2_activities extends Activity {
 
 
    protected void pecharActivity(View v){
 
        finish();
 
    }
 
 
 
    @Override
 
    protected void onCreate(Bundle savedInstanceState) {
 
        super.onCreate(savedInstanceState);
 
        setContentView(R.layout.activity_ud02_01__a2_activities);
 
    }
 
}
 
</syntaxhighlight>
 
 
 
* Se executamos a primeira activity podemos comprobar como ao premer no botón aparece a segunda activity a cal pode ser pechada ao premer no botón 'Pechar'.
 
 
 
 
<br />
 
<br />
==Etiqueta Action==
+
'''[https://wiki.cifprodolfoucha.es/index.php?title=Programación_de_dispositivos_móbiles Enlace a la página principal del curso]'''
 
 
* Como vimos anteriormente, cada vez que se crea unha activity se engade no AndroidManifiest.xml.
 
: Dita activity ten asociado un filtro de intento (<intent-filter>) ou podemos engadirllo en caso de non ser un tipo 'launcher'.
 
: Dentro deste temos a etiqueta <action....> no que se define o nome do filtro para a activity.
 
 
 
: Desta forma podemos chamar a outra activity polo seu nome:
 
 
 
:* startActivity(new Intent(this,NomeClase.class);              :Esta é a opción vista ata o de agora.
 
:* startActivity(new Intent(“nome.definido.etiqueta.action”));  :Chamamos co nome definido na etiqueta action.
 
:: Neste segundo caso teremos que definir unha categoría por defecto, para poder chamar a activity polo seu nome (<category android:name="android.intent.category.DEFAULT" />)
 
 
 
 
 
 
 
 
 
: Vexamos un exemplo:
 
:<syntaxhighlight lang="java" line highlight="12-15">
 
        <activity
 
            android:name="com.example.variasactiviy.MainActivity1"
 
            android:label="@string/app_name1" >
 
            <intent-filter>
 
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
 
            </intent-filter>
 
        </activity>
 
        <activity
 
            android:name="com.example.variasactiviy.MainActivity2"
 
            android:label="@string/app_name2" >
 
            <intent-filter>
 
                <action android:name="com.example.variasactiviy.ActionMainActivity2" />
 
          <category android:name="android.intent.category.DEFAULT" />
 
            </intent-filter>
 
        </activity>
 
</syntaxhighlight>
 
 
 
: Para chamar a segunda activity:
 
:<syntaxhighlight lang="java" line highlight="">
 
startActivity(new Intent("com.example.variasactiviy.ActionMainActivity2"));
 
</syntaxhighlight>
 
 
 
* Nome dos action:
 
:* Aínda que o action pode ter calquera nome, por convención se usa este formato nos action definidos por Android: '''.intent.action.nome_acción'''.
 
:: Por exemplo, 'android.intent.action.MAIN' é o action que define Android para unha activity de tipo 'Launcher'.
 
:* No caso das activities definidas por nos que non teñen un action 'predefinido' de Android, o name terá o formato: '''nome_paquete.nome_accion'''.
 
:: No exemplo anterior: com.example.variasactiviy.ActionMainActivity2
 
 
 
: Unha activity pode levar varias accións asociadas.
 
: Se existen varias activities co mesmo nome o S.O. amosará unha mensaxe para que escollamos cal delas debe abrir.
 
 
 
 
 
 
 
* Os intent´s vainos servir para chamar a outras aplicacións que estean instaladas no sistema operativo Android. A idea é chamar a unha aplicación cun tipo de dato, e que sexa o S.O. en base os 'filter' das aplicacións instaladas, o que chame á aplicación correspondente.
 
Dentro do Intent irán os datos necesarios para que a aplicación que reciba a petición poida dar o servizo.
 
Así, se abro o navegador, podo enviarlle a url que quero que abra (é opcional mandalo). Para mandar os datos faremos uso do método putExtra da clase Intent (o veremos despois noutro punto).
 
 
 
: Así, grazas a esta forma de traballar, podemos usar Intent´s:
 
:* Para chamar a un teléfono
 
:* Para enviar un sms/correo
 
:* Para abrir o navegador
 
:* Para abrir unha localización dentro dun mapa
 
:* Ou para chamar a Activities doutras aplicacións.
 
 
 
 
 
: Por exemplo:
 
:* Un intent para '''enviar un correo electrónico''':
 
::<syntaxhighlight lang="java" line highlight="">
 
Intent intent = new Intent(Intent.ACTION_SEND);
 
intent.setType("application/octet-stream");
 
intent.putExtra(Intent.EXTRA_SUBJECT, "Subject");
 
intent.putExtra(Intent.EXTRA_TEXT, "Texto do Mail");
 
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{"android@cursoandroid.es"});
 
startActivity(intent);
 
</syntaxhighlight>
 
 
 
:* Un intent para '''amosar o dial de teléfono''':
 
::<syntaxhighlight lang="java" line highlight="">
 
Intent intent = new Intent(Intent.ACTION_DIAL);
 
startActivity(intent);
 
</syntaxhighlight>
 
 
 
:* Un intent para '''lanzar unha busca do navegador web''':
 
::<syntaxhighlight lang="java" line highlight="">
 
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
 
intent.putExtra(SearchManager.QUERY, "Android");
 
startActivity(intent);
 
</syntaxhighlight>
 
 
 
 
 
 
 
 
 
* A un Intent podemos asociarlle unha acción, uns datos e unha categoría.
 
: As activities poden declarar o tipo de accións que poden levar a cabo e os tipos de datos que poden xestionar.
 
: As accións son cadeas de texto estándar que describen o que unha activity pode facer.
 
: Por exemplo android.intent.action.VIEW é unha acción que indica que a actividade pode amosar datos ó usuario.
 
 
 
: Máis información en:
 
:* http://developer.android.com/guide/components/intents-filters.html
 
:* http://developer.android.com/reference/android/content/Intent.html
 
 
 
: Cando definimos unha activity normalmente aparece isto no AndroidManifiest,xml:
 
 
 
::<syntaxhighlight lang="java" line highlight="">
 
        <activity
 
            android:name=".MainActivity"
 
            android:label="@string/title_activity_main" >
 
            <intent-filter>
 
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
 
            </intent-filter>
 
        </activity>
 
</syntaxhighlight>
 
 
 
: Como vemos temos:
 
:* <action android:name="android.intent.action.MAIN" />: indica que é unha activity inicial que non se lle vai a enviar datos nin devolver datos.
 
:* <category android:name="android.intent.category.LAUNCHER" />: indica que a activity pode ser a actividade inicial dunha tarefa.
 
 
 
 
 
 
 
: Outro exemplo sería o seguinte:
 
::<syntaxhighlight lang="java" line highlight="">
 
  <intent-filter >
 
<action android:name="android.intent.action.VIEW" />
 
<category android:name="android.intent.category.DEFAULT" />
 
<category android:name="android.intent.category.BROWSABLE" />
 
<data android:scheme="http" android:pathPattern=".*mp3" android:mimeType="audio/*" />
 
  </intent-filter>     
 
</syntaxhighlight>
 
 
 
: Neste caso cando esteamos navegando, ó premer sobre unha canción mp3 abrirase a posibilidade de reproducila coa nosa activity. Xa dentro da nosa activity, o dato que podemos obter é a URL da canción a reproducir os poderíamos obter da seguinte forma:
 
::<syntaxhighlight lang="java" line highlight="">
 
Intent intent = getIntent();
 
        // Por se voltamos do navegador web.
 
        if (intent != null && intent.getData() != null) {
 
        TextView txturl = (TextView)findViewById(R.id.txtURL);
 
        txturl.setText(intent.getData().toString());
 
        }
 
</syntaxhighlight>
 
 
 
 
 
 
 
 
 
* No caso da ACTION_VIEW é unha acción moi xenérica e Android ten que descubrir que  activity pode chamar facendo uso da composición da URI.
 
: Para iso mira o esquema que posúe a URI (neste caso http) e pregunta a todas as actividades cal delas entende dito esquema.
 
: Poderíamos ter rexistrados varios esquemas:
 
 
 
::<syntaxhighlight lang="java" line highlight="">
 
<activity ...>
 
  <intent -filter>
 
      <action android:name="android.intent.action.VIEW"/>
 
      <data android:scheme="http" />
 
      <data android:scheme="https" />
 
  </intent>
 
</activity>
 
</syntaxhighlight>
 
 
 
: Máis información sobre a etiqueta <data> en http://developer.android.com/guide/topics/manifest/data-element.html
 
 
 
 
 
 
 
 
<br />
 
<br />
  
==Etiqueta category==
 
 
* No que se refire a etiqueta <category>  esta se utiliza para categorizar as activities e que se podan atopar máis facilmente.
 
 
* Máis información en: http://developer.android.com/reference/android/content/Intent.html#CATEGORY_ALTERNATIVE
 
 
 
* Anteriormente vimos un exemplo no que tiñamos:  <category android:name="android.intent.category.BROWSABLE" />
 
 
: Se ides o enlace anterior e buscades dita categoría, vos informa que dita categoría é necesaria para que unha activity sexa chamada dende o navegador.
 
: Por exemplo, mentres que o sistema se está iniciando busca as activities que teñan a category de launcher: <category android:name="android.intent.category.LAUNCHER" />
 
 
 
: Por exemplo, poderíamos lanzar unha activity en base a súa categoría:
 
 
::<syntaxhighlight lang="java" line highlight="">
 
Intent intento = new Intent(Intent.ACTION_MAIN, null);
 
intento.addCategory(Intent.CATEGORY_LAUNCHER);
 
startActivity(i);
 
</syntaxhighlight>
 
 
Dará como resultado:
 
[[Imagen:PDM_Activity_25.jpg|400px|center]]
 
 
 
 
 
 
<br />
 
=Pasando datos entre activities=
 
  
  
 
<br> -- [[Usuario:angelfg|Ángel D. Fernández González]] -- (2018).
 
<br> -- [[Usuario:angelfg|Ángel D. Fernández González]] -- (2018).

Revisión actual del 13:01 21 nov 2020

Introdución

  • Como xa comentamos antes, podemos identificar unha 'Activity' con cada unha das pantallas que conforman unha aplicación.
  • Isto non é totalmente certo, xa que podemos ter aplicacións que non teñan interface gráfica, coma os servizos, programas que se executan en segundo plano e que responden a un determinado tipo de evento.


  • Neste punto imos explicar os diferentes métodos polos que pasa unha Activity cando se crea, ponse en segundo plano ou se destrúe.
Indicaremos en cada un deles cales serían as principais funcións que poderíamos programar.
Tamén explicaremos como se define unha activity a nivel de proxecto.


Preparación previa:

Se non o temos creado antes, crea un paquete de nome UD2 e dentro deste outro paquete de nome Activities.
PDM Activity 0A.jpg
Vos debe quedar como na imaxe anterior. Indicar que fisicamente temos creados os cartafoles UD2/Activities pero como non existen clases no cartafol UD2/ Android Studio o oculta.


Creando Activities

  • En Android existen moitos tipos de 'Activities'.
Todas elas teñen en común que son ventás onde se van 'debuxar' elementos da interface do usuario.
  • Neste punto imos centrarnos en dous tipos de activity:
Son activities clásicas onde o usuario van interaccionar con compoñentes gráficos.
Son activities que derivan de Activity e que se atopan dentro da librería de compatibilidade v7.
Teñen a mesma función que as de clase Activity (servir como pantallas para interaccionar con compoñentes gráficos). Inicialmente foron creadas para que se puidera empregar o AppBar en versións de Android anteriores a que aparecera.
Existen novas funcionalidades que van poder ser empregadas en versións Android anteriores a que apareceran grazas o uso desta clase como xa vimos anteriormente nesta Wiki.
Na versión actual de Android Studio (Sept.2019) xa crea por defecto as activities desta clase.



  • Cada vez que se crea unha activity, aparece unha nova entrada no arquivo AndroidManifiest.xml.
Vexamos un exemplo, e crearemos unha nova activity no asistente:
Nota: Fixarse que na imaxe o nome do paquete está errado. Pon 'UD2.Acvities'. Probade a cambiar o nome coa mesma opción 'Refactor'.



  • Ao cambiar a forma en como Android Studio crea a activity por defecto (de clase AppCompatActivity), teremos dúas consecuencias:
  • Empregando AppCompactActivity con bibliotecas de compatibilidade antigas
  • A activity creada xa non deriva da clase Activity, se non da clase AppCompatActivity do paquete de compatibilidade v7.

  • Engadiuse ao arquivo build.gradle a biblioteca de compatibilidade xa que facemos uso dela na activity creada.


  • Empregando AppCompactActivity con bibliotecas de compatibilidade androidx
  • A activity creada xa non deriva da clase Activity, se non da clase AppCompatActivity do paquete de compatibilidade androidx.appcompat.app.

  • Engadiuse ao arquivo build.gradle a biblioteca de compatibilidade xa que facemos uso dela na activity creada.





    • Como vimos no asistente, á hora de crear unha activity aparece unha opción que indica Launcher Activity:
    • Launcher Acivity: Fai que dita activity poida ser lanzada ou executada de forma independente. Isto quere dicir que o resto de activities que non teñen marcada esta opción van necesitar ser invocadas dende outra activity. Ao crear unha activity e marcar esta opción, levará consigo dúas consecuencias:




    • Indicar que nas opcións de configuración de execución do proxecto, podemos indicar cal é a activity por defecto a lanzar (se non se especifica será a primeira que sexa de tipo launcher) e o emulador por defecto que vai lanzar:


    • Crea un novo paquete de nome 'tiposactivities' e move as activities creadas anteriormente a ese paquete.
    Deixa a lo menos unha activity no paquete inicial, xa que se non a nivel gráfico o Android Studio o fai desaparecer.
    Nota: Lembra, como sucede en Java, cada paquete se traduce nun cartafol a nivel de S.O.



    Ciclo de vida das Activities


    • Unha aplicación de Android pode estar conformada por moitas activities (pantallas) e incluso llegar a chamar a outras aplicación.
    Cada vez que unha activity chama a outra, a primeira non se destrúe, se non que queda nunha cola LIFO (Stack).
    Cando prememos o botón Back do móbil ou pechamos a activity actual, recupera a activity que estaba na cola.
    PDM Ciclo Vida 1.jpg



    • Cando se lanza unha activity esta pasa por unha serie de estados e eses estados chaman a unha serie de métodos, ao igual que cando pechamos ou abrimos unha nova activity.
    00 basic-lifecycle.png

    Nota: O estado Resumed: ven ser o estado de running.



    Métodos asociados aos estados

    • Estes son os diferentes métodos e o que se debería facer en cada un deles:
    • Método onCreate:
    • Definir / instanciar variables – clases
    • Definir a interface do usuario.
    • Vincular datos a listas.
    Unha vez chamado o método onCreate chama os métodos onStart e onResume de forma consecutiva.


    • Método onDestroy: Chamado cando remata a aplicación e se libera da memoria do dispositivo, cando pechamos unha activity ou cando prememos o botón BACK. Tamén pode ser provocado polo S.O. para liberar espazo de memoria. Se a aplicación usa background Threads ou outro recurso que poida consumir memoria, debe ser destruído neste evento. Normalmente liberaremos os recursos que foron 'instanciados' no método onCreate.
    Nota: Só hai un caso no que non pasa polos métodos de onPause e onStop, é cando destruímos a aplicación chamando o método finish() dende onCreate.


    • Método onPause: Cando unha aplicación, ocupa parte da pantalla, pero deixa visible parte de aplicación en execución, esta pasa a un estado de PAUSE (por exemplo unha caixa de diálogo aberta por unha aplicación). En caso contrario, se queda totalmente tapada pasa a o estado de STOP.
    Ó volver do estado de Pause, se chama o método onResume.
    Normalmente cando se chama o proc. onPause, é que a aplicación vai deixar de se usada por o usuario. Neste caso se aproveita este estado para facer:
    • Parar animacións ou outras actividades que consuman CPU.
    • Gardar a información que o usuario espera atopar se volve a cargar a aplicación (redacción dun correo sen rematar,…).
    • Liberar recursos do sistema, como transferencias, sensores (GPS), ou calquera recurso que poida afectar á batería (como a cámara).
    Non se debe sobrecargar este método con ‘traballo’ pesado de liberación, xa que podemos enlentecer o paso ó seguinte estado. Para iso temos o método onStop.


    • Método onResume: Úsase este método para volver a crear aqueles recursos que liberamos no método onPause.
    Hai que ter en conta que entramos neste método cando vimos de inicializar a aplicación e cando paramos a aplicación, polo que pode ser necesario verificar se xa estaban creados os recursos antes de volver a crealos.
    A aplicación permanece neste estado ata que se cambie de activity ou
    Vexamos un suposto exemplo no que facemos uso da cámara. Como vemos comprobamos se o obxecto que vai facer uso da cámara está xa inicializado. Isto pode pasar nunha aplicación multiventana no que pasemos o foco a outra ventá e despois volvamos á ventá inicial.
    1     @Override
    2     protected  void onResume(){
    3         super.onResume();
    4 
    5         if (mCamara==null){
    6             inicializarCamara();
    7         }
    8     }
    


    • Método onStop.
    Existen uns poucos casos no que isto sucede:
    • Usuario abre outra aplicación ou vai as aplicación recentes e escolle outra. Ata que non volva á nosa aplicación, esta pasa a un estado de stop e se queda en segundo plano.
    • Usuario provoca a aparición doutra activity. A primeira activity é parada e comeza a segunda. Se o usuario preme o botón de ‘Volver’ a primeira actividade é reiniciada.
    • Usuario recibe unha chamada de teléfono.
    Temos por tanto os métodos onStop e onRestart para dar soporte a estes eventos.
    No método onStop é onde debemos facer as operación que máis leven de liberación de recursos coma poidan ser gardar datos nunha base de datos.
    Nota: O sistema garda o estado das View, polo que os contidos de caixas de texto e outros recursos gráficos se manteñen ó reiniciar a aplicación. Soamente se perden se o S.O. necesita espazo en memoria e quita a aplicación do Stack, destruíndo todo. Nese caso se o usuario volve, os elementos gráficos estarían inicializados.


    • Método onRestart e método onStart:
    Cando o sistema chama o método reiniciar tamén chama despois o método onStart (cada vez que aplicación se pon visible).
    Debido a isto, se debe de inicializar os compoñentes que foron liberados no método onStop e tamén se debe aproveitar ese mesmo código cando se crea por primeira vez a aplicación.
    Por exemplo, no método onStart se poden poñer as condicións necesarias para que poida funcionar a aplicación (coma por exemplo recursos que necesitemos, coma GPS, unha cámara,....).
    No método onRestart se poden recuperar cursores de acceso a datos que foran pechados previamente no método onStop. Máis información neste enlace.
    A aplicación non permanece nestes estados, se non que pasa directamente ao estado Resumed e chama ao método asociado onResume().


    • NOTA IMPORTANTE: Se sobreescribides calquera destes métodos tedes que chamar en primeiro lugar ao método da superclase da forma: super.Método();




    Enlace a la página principal de la UD2

    Enlace a la página principal del curso



    -- Ángel D. Fernández González -- (2018).