¡Hola a todos! Bienvenidos a este tutorial donde aprenderemos a acceder a la galeria de imagenes Android mediante dependencyService y visualizar las imagenes seleccionadas. ¡Empecemos!

¿Qué es DependencyService?

DependencyService permite a las aplicaciones llamar a funciones específicas de una plataforma desde el código compartido de tu aplicación. Con esto, permite a Xamarin.Forms hacer todo aquello que hace una aplicación nativa.

Después de esta breve explicación, procedemos a crear nuestro proyecto.

Creación del Proyecto.

Para empezar vamos a crear nuestro proyecto, para eso vamos a File>New>Project y seleccionamos la opción Cross-Platform.

DependencyService1

Ahora seleccionamos la opción Blank.

DependencyService2

Implementación en Android

Una vez creado el proyecto veremos que se han creado 4 proyectos, el primero de todos es el proyecto de código compartido, el segundo es el de android, el tercero el de iOS y el cuarto es de Universal Windows.

Vamos a crear una carpeta en el proyecto de código compartido, la llamaremos Dependencies. Dentro de la carpeta Dependencies crearemos una interfaz llamada IGaleriaImagenes, para crearlo damos click derecho sobre la carpeta Dependencies, elegimos las opción Add y dentro la opción New Item y seleccionamos la opción Interface.

El código de IGaleriaImagenes debe de ser así:

public interface IGaleriaImagenes
{
       Task<Stream> GetFotoAsync();
}

Una vez hecho esto, vamos al proyecto Android y damos doble click sobre MainActivity. Ahora insertamos el siguiente código:

public static readonly int idImagen = 1000;

public TaskCompletionSource<Stream> ImagenTaskCompletionSource { set; get; }
public static object Instance { get; internal set; }

protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
{
       base.OnActivityResult(requestCode, resultCode, intent);

       if (requestCode == idImagen)
       {
              if ((resultCode == Result.Ok) && (intent != null))
              {
                     Android.Net.Uri uri = intent.Data;
                     Stream stream = ContentResolver.OpenInputStream(uri);

                     ImagenTaskCompletionSource.SetResult(stream);
              }
              else
              {
                    ImagenTaskCompletionSource.SetResult(null);
              }
       }
}

Explicación: Hemos sobreescrito el método OnActivityResult que se activa cuando el usuario ha elegido una imagen, lo que le hemos escrito es que recoge la Uri de la imagen y poderla convertir en un objeto Stream de .NET llamando al método OpenInputStream.

La implementación de Android se usa un elemento llamado TaskCompletionSource para indicar que se ha completado la tarea. Este objeto se declara como propiedad pública en la clase MainActivity. Esto a su vez, permite hacer referencia a la propiedad en la clase AndroidImplementation que haremos ahora.

Creamos la clase llamada AndroidImplementation, para crear la clase damos click derecho sobre el proyecto, elegimos la opción Add y dentro la opción Class…

Dentro de la clase heredaremos de la interface IGaleriaImagenes y tendrá el siguiente código:

public class AndroidImplementation : IGaleriaImagenes
{
       public Task<Stream> GetFotoAsync()
       {
              Intent intent = new Intent();
              intent.SetType("image/*");
              intent.SetAction(Intent.ActionGetContent);
              MainActivity activity = Forms.Context as MainActivity;
           
           activity.StartActivityForResult(Intent.CreateChooser(intent, "Selecciona una Imagen"), MainActivity.idImagen);

            activity.ImagenTaskCompletionSource = new TaskCompletionSource<Stream>();

              return activity.ImagenTaskCompletionSource.Task;
       }        
}

Explicación: este método accede a la clase MainActivity para acceder a las propiedades Instance, idImagen, ImagenTaskCompletionSource y para llamar a StartActivityForResult. GetFotoAsync() crea un objeto Intent en el que obtiene la imagen seleccionada, luego crea el activity de seleccion de imagen y guarda el TaskCompletionSource. Por último devuelve el task object.

Models y ViewModels

Ya tenemos la implementación en Android lista, ahora procederemos a crear unos modelos para poder visualizar las imagenes que seleccionamos.

Primero, vamos al proyecto de código compartido y creamos una carpeta llamada Models, después creamos una clase llamada Foto.

Dentro de la clase Foto tiene el siguiente código:

public class Foto
{
       public Foto()
       {
              Imagen = new Image();
       }

       public String name { get; set; }

       public Image Imagen { get; set; }
}

Ahora crearemos otra carpeta sobre el proyecto de código compartido llamará Base, dentro de esa carpeta tendrá una clase llamada ViewModelBase que tendrá el siguiente código:

public class ViewModelBase : INotifyPropertyChanged
{
       public event PropertyChangedEventHandler PropertyChanged;

       public void OnPropertyChanged(String propertyname)
       {
              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
       }
}

Explicación: Con la propiedad PropertyChanged nos permitirá actualizar nuestro ViewModel pudiendo ver los cambios que se hacen en la vista.

Lo siguiente que haremos es crear otra carpeta sobre el proyecto de código compartido llamado ViewModel que tendrá una clase llamada FotosViewModel. El código de la clase es el siguiente:

public class FotosViewModel: ViewModelBase
{
       public FotosViewModel()
       {
              this.Fotos = new ObservableCollection<Foto>();
       }
        
       private ObservableCollection<Foto> _Fotos;
       public ObservableCollection<Foto> Fotos
       {
              get { return this._Fotos; }
              set { this._Fotos = value; OnPropertyChanged("Fotos"); }
       }

       private int num = 1;
       public Command CargarFoto
       {
              get
              {
                     return new Command(async () =>
                     {
                            Stream stream = await 
                            DependencyService.Get<IGaleriaImagenes>().GetFotoAsync();
                            if (stream != null)
                            {
                                   Foto foto = new Foto();
                                   foto.Imagen.Source = ImageSource.FromStream(() => stream);

                                   foto.name = "Imagen "+num;
                                   num++;

                                   this.Fotos.Add(foto);
                           }
                    });
             }
      }
}

Explicación: esta clase hereda de ViewModelBase, por lo que le permite a sus propiedades que se actualicen el la vista. Tiene una propiedad prublica llamada ObervableCollection que permite crear colecciones de elementos y mejora la renderización de dichos elementos en la vista. También tiene un Command llamado CargarFoto que obtiene la imagen seleccionada de la galeria de imagenes y lo añade a la lista de fotos.

Vista

Una vez que hayamos terminado, vamos al archivo MainPage.xaml y ponemos el siguiente código dentro de la etiqueta ContentPage junto a los atributos:

xmlns:viewmodel="clr-namespace:PostImagenes.ViewModel"

Con esto mapeamos la carpeta ViewModel para poder hacer el BindingContext que es lo siguiente que insertaremos:

<ContentPage.BindingContext>
    <viewmodel:FotosViewModel />
</ContentPage.BindingContext>

Dentro del StackLayout creamos un ListView, debe de quedar de esta manera:

<ListView ItemsSource="{Binding Fotos}" HeightRequest="80" HasUnevenRows="True">
            <ListView.ItemTemplate >
                <DataTemplate>  
                    <ViewCell>
                        <StackLayout Orientation="Horizontal">
                            <Label  Text="{Binding name}" TextColor="Red"/>
                            <Image Source="{Binding Imagen.Source}"   HorizontalOptions="FillAndExpand" HeightRequest="80"/>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

Y por último creamos el botón con el comando CargarFoto:

<Button Text="Selecciona una imagen!" x:Name="botonimagen" Command="{Binding CargarFoto}"/>

El archivo tiene que quedar de esta manera:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:viewmodel="clr-namespace:PostImagenes.ViewModel"
             x:Class="PostImagenes.MainPage">
    <ContentPage.BindingContext>
        <viewmodel:FotosViewModel />
    </ContentPage.BindingContext>
    <StackLayout Margin="0,30,0,0">
        <ListView ItemsSource="{Binding Fotos}" HeightRequest="80" HasUnevenRows="True">
            <ListView.ItemTemplate >
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Orientation="Horizontal">
                            <Label  Text="{Binding name}" TextColor="Red"/>
                            <Image Source="{Binding Imagen.Source}"   HorizontalOptions="FillAndExpand" HeightRequest="80"/>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
        <Button Text="Selecciona una imagen!" x:Name="botonimagen" Command="{Binding CargarFoto}"/>
    </StackLayout>
</ContentPage>

Y con esto ya habríamos terminado, ahora lo ejecutamos y… ¡Ya tendríamos que ver los resultados!

Autor/a: David Valencia Beltrán

Curso: Microsoft MCSA Web Applications + Microsoft MCSD App Builder + Xamarin

Centro: Tajamar

Año académico: 2018-2019

Código / recursos utilizados / Otros datos de interés: https://github.com/Davos95/PostImagenes

Leave a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.