Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Servicios de la plataforma Android
Sesión 1: Librerías de
compatibilidad y servicios
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Puntos a tratar
• Compatibilidad de versiones
• Fragmentos
• Loaders
• Librería de compatibilidad
• Librería de servicios de Google Play
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Compatibilidad de versiones
• A mayor versión utilizada por nuestras aplicaciones
• Mayores funcionalidades y facilidades
• Menor compatibilidad
• Se especifican dos versiones
• Podemos utilizar de forma opcional funcionalidades avanzadas
• Algunas características no pueden ser utilizadas opcionalmente
• Por ejemplo los fragmentos
<uses-‐sdk android:minSdkVersion="4" android:targetSdkVersion="17" />
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // Utilizar características de Android 3.0 (Honeycomb) }
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Fragmentos
• Característica introducida en Android 3.0 (Honeycomb)
• Orientada a la introducción de tablets
• Forma recomendada de estructurar la interfaz
• Nos permiten construir la interfaz de forma modular
• Una actividad puede mostrar varios fragmentos
• Según el tipo de dispositivo se puede variar la disposición
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Creación de fragmentos
• Se crean heredando de Fragment
• Debemos definir el método onCreateView donde contruimos la interfaz
• Normalmente la construimos a partir de un layout XML
• No es una actividad, pero puede que necesitemos tener acceso a la actividad contenedora
• Existen fragmentos especializados
• ListFragment, DialogFragment, PreferencesFragment
public class DetalleFragment extends Fragment { @Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragmento, container, false);
} }
Button boton = (Button)getActivity().findViewById(R.id.boton);
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Ciclo de vida de los fragmentos
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Añadir el fragmento a la actividad
• Los fragmentos siempre deben estar dentro de una actividad
• Existen dos formas de añadirlos:
• Estática
• Se añaden en el layout XML
• No pueden ser modificados en tiempo de ejecución
• Varios fragmentos al mismo tiempo en pantalla (tablets)
• Dinámica
• Se añaden desde el código de la actividad
• Podemos hacer transiciones entre fragmentos
• Útil para dispositivos en los que no se pueden mostrar todos en la
misma pantalla
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Fragments estáticos
• Se definen en el layout XML de la actividad
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<fragment android:name="es.ua.jtech.fragments.PrincipalFragment"
android:id="@+id/principal_fragment"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="es.ua.jtech.fragments.DetalleFragment"
android:id="@+id/detalle_fragment"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Fragments dinámicos
• Definimos en el layout de la actividad un marco vacío
• Si el marco existe, añadimos el fragmento al crear la actividad
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
public class MainActivity extends Activity {
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);
if (findViewById(R.id.fragment_container) != null) { if (savedInstanceState != null) {
return;
}
PrincipalFragment ppalFragment = new PrincipalFragment();
ppalFragment.setArguments(getIntent().getExtras());
getFragmentManager().beginTransaction()
.add(R.id.fragment_container, ppalFragment).commit();
} }
Comprobamos si existe el contenedor Si venimos de
una instancia anterior no hace falta volver a
construir
Mostramos el fragmento con
FragmentManager
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Transiciones entre fragmentos
• En caso de haber añadido el fragmento de forma dinámica, podemos hacer transiciones a otro fragmento
DetalleFragment detalleFragment = new DetalleFragment();
// Pasamos parametros al nuevo fragmento Bundle args = new Bundle();
args.putInt(PARAM_POSICION, posicionSeleccionada);
detalleFragment.setArguments(args);
FragmentTransaction transaction = getFragmentManager()
.beginTransaction();
transaction.replace(R.id.fragment_container, detalleFragment);
transaction.addToBackStack(null);
transaction.commit();
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Comunicación entre fragmentos
• Siempre se hará a través de la actividad contenedora
• Definimos un callback en uno de los fragmentos
public class PrincipalFragment extends ListFragment { OnItemSelectedListener mCallback;
public interface OnItemSelectedListener {
public void onItemSelected(int position);
}
@Override
public void onAttach(Activity activity) { super.onAttach(activity);
try {
mCallback = (OnItemSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " debe implementar OnItemSelectedListener");
} }
...
}
Comprueba que la actividad implemente la interfaz
La actividad debe
implementar esta
interfaz
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Gestión de la comunicación
• En la actividad implementamos la comunicación en el listener
public static class MainActivity extends Activity
implements PrincipalFragment.OnItemSelectedListener { ...
public void onItemSelected(int position) {
DetalleFragment detalleFragment = (DetalleFragment)
getFragmentManager().findFragmentById(R.id.detalle_fragment);
if (detalleFragment != null) {
detalleFragment.setDetalleItem(position);
} else {
detalleFragment = new DetalleFragment();
Bundle args = new Bundle();
args.putInt(PARAM_POSICION, position);
detalleFragment.setArguments(args);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, detalleFragment);
transaction.addToBackStack(null);
transaction.commit();
Tipo estático
Tipo dinámico
Comprobamos
si el fragmento
está en pantalla
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Creación de diálogos
• La forma recomendada de crear diálogos es con fragmentos
• Se crean mediante fragmentos de tipo DialogFragment
• Se puede especificar el título de la ventana del diálogo
• Tiene un método show para mostrarlos
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog, container);
getDialog().setTitle("Título");
return view;
}
FragmentManager manager = getFragmentManager();
MiDialogFragment dialog = new MiDialogFragment();
dialog.setArguments(bundle);
dialog.show(manager, "fragment_dialog");
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Loaders
• Se trata de otra característica introducida en Android 3.0
• Gestiona la carga de datos por parte de actividades/fragmentos
• Normalmente carga datos de proveedores de contenidos o servicios web
• Inicia el proceso de carga si no está ya en marcha
• Si detecta un cambio en los datos, los vuelve a recuperar
• Lo iniciamos normalmente en onCreate
• También podemos solicitar reiniciar la carga
getLoaderManager().initLoader(0, null, this);
getLoaderManager().restartLoader(0, null, this);
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Métodos a definir en el callback
• Al implementar el callback se debe especificar el tipo de datos a utilizar
• Utilizamos genéricos
• Los métodos que debemos definir son los siguientes
public class MiListFragment extends ListFragment
implements LoaderManager.LoaderCallbacks<Tipo> { ...
public Loader<Tipo> onCreateLoader(int id, Bundle args) { ... }
public void onLoadFinished(Loader<Tipo> loader, Cursor data) { ... }
public void onLoaderReset(Loader<Tipo> loader) { ... }
}
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Callback del loader
• Debemos indicar cómo construir el objeto Loader
• También debemos definir qué hacer cuando obtengamos datos
• Y por último, la forma de reiniciar el contenido
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getActivity(), baseUri, proyeccion, seleccion, args, orden);
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) { mAdapter.swapCursor(data);
}
public void onLoaderReset(Loader<Cursor> loader) { mAdapter.swapCursor(null);
}
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Loader personalizado
• Podemos crear nuestro propio tipo de loader
• Normalmente lo hacemos con una subclase de AsyncTaskLoader
• Debemos implementar el proceso de carga de forma similar a una async task
static class MiLoader extends AsyncTaskLoader<Tipo>
{
Tipo mDatos = null;
public MiLoader(Context context) { super(context);
}
@Override
public Tipo loadInBackground() { return cargarDatos();
} ...
}
Descarga los datos en background
Especificamos el tipo
de los datos a obtener
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Otros métodos de AsyncTaskLoader
static class MiLoader extends AsyncTaskLoader<Tipo> { Tipo mDatos = null;
...
@Override protected void onStartLoading() { super.onStartLoading();
if(mDatos != null) {
deliverResult(mDatos);
} else {
forceLoad();
} }
@Override public void deliverResult(Tipo data) { mDatos = data;
super.deliverResult(data);
}
@Override protected void onStopLoading() { super.onStopLoading();
cancelLoad();
}
@Override protected void onReset() { super.onReset();
Comprobamos si tenemos los datos ya descargados o si hay que cargarlos
“Entregamos” los
datos, y los retenemos
Cancelamos la descarga
Borramos los
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Uso del loader
• Podemos utilizar el loader definido desde nuestro callback
public class MiFragmento implements LoaderManager.LoaderCallbacks { ...
public Loader<Tipo> onCreateLoader(int id, Bundle args) { return new MiLoader(getActivity());
}
public void onLoadFinished(Loader<Tipo> loader, Tipo data) { mAdapter.clear();
for(Item item: data) { mAdapter.add(item);
} }
public void onLoaderReset(Loader<Tipo> loader) { mAdapter.clear();
} }
Introduce los datos
cargados en el adaptador
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Librería de compatibilidad
• Fragmentos y loaders son características importantes
• Es importante seguir dando soporte a dispositivos antiguos
• Existen librerías de compatibilidad
• Incorporan estas características a dispositivos a partir de 1.6
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Uso de la librería de compatibilidad
• Copiamos la librería de compatibilidad al directorio libs
• Utilizamos los imports de la librería de compatibilidad
• La actividad debe heredar de FragmentActivity
• Usamos métodos alternativos para obtener los managers
$ANDROID_SDK/extras/android/support/v4/android-‐support-‐v4.jar
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager;
public class MainActivity extends FragmentActivity { ...
}
FragmentManager manager = getSupportFragmentManager();
LoaderManager manager = getSupportLoaderManager();
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Librerías de servicios
• Podemos también añadir librerías para acceso a servicios
• Los servicios de Google se añaden mediante librería externa
• Aporta mayor flexibilidad para suministrar actualizaciones
• Debemos descargar la librería desde Google Play
• Soporta Android 2.2 y superiores
• Para desarrollar un proyecto con ella debemos
• Descargar la librería de servicios Google Play con SDK Manager
• Importar la librería (Existing Android Code into Workspace)
$ANDROID_SDK/extras/google/google_play_services/
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Configuración de los permisos
• Añadir permisos para los servicios
<uses-‐permission android:name="android.permission.INTERNET"/>
<uses-‐permission android:name=
"android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-‐permission android:name=
"com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<uses-‐permission android:name=
"android.permission.ACCESS_COARSE_LOCATION"/>
<uses-‐permission android:name=
"android.permission.ACCESS_FINE_LOCATION"/>
<permission
android:name="es.ua.jtech.permission.MAPS_RECEIVE"
android:protectionLevel="signature"/>
<uses-‐permission android:name="es.ua.jtech.permission.MAPS_RECEIVE"/>
Sustituimos es.ua.jtech por el
paquete de nuestra aplicación
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Clave de desarrollador y OpenGLES 2.0
• Google Maps v2 necesita OpenGLES 2.0
• Se debe indicar en el manifest
• También debemos indicar nuestra clave de desarrollador
• La obtendremos de la siguiente dirección
<uses-‐feature
android:glEsVersion="0x00020000"
android:required="true"/>
<meta-‐data
android:name="com.google.android.maps.v2.API_KEY"
android:value="pon_aqui_tu_clave"/>
https://developers.google.com/maps/documentation/android/start#the_google_maps_api_key
Experto en Desarrollo de Aplicaciones para Dispositivos Móviles
Integración del mapa
• Podemos añadirlo al layout como fragmento
• También podemos utilizar la librería de compatibilidad
<?xml version="1.0" encoding="utf-‐8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.MapFragment"/>
<?xml version="1.0" encoding="utf-‐8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment"/>