Saltar al contenido
Desplácese sin problemas por grandes tablas SQLite en Xamarin.Forms con poca sobrecarga de memoria

Desplácese sin problemas por grandes tablas SQLite en Xamarin.Forms con poca sobrecarga de memoria

Si ya ha trabajado con Infragistics' Xamarin.Forms/Xamarin.Android/Xamarin.iOS DataGrid (XamDataGrid), habrá descubierto que conoce algunos trucos muy interesantes.

9min read

 Puede vincularlo a un servicio OData remoto y desplazarse por las filas, y usará la velocidad de su movimiento para predecir cuándo necesita recuperar datos y cargarlos sin problemas antes de que llegue allí. Si aún no has visto este truco, definitivamente echa un vistazo a nuestro navegador de muestras para Ultimate UI for Xamarin, que lo muestra bien.

Running the Sample

Puede obtener la muestra que crearemos en este artículo aquí. Una vez que abra el ejemplo, deberá asegurarse de que tiene nuestros paquetes Nuget de prueba o RTM en el repositorio local y restaurar los paquetes Nuget.

Virtualización del acceso a otras fuentes de datos

Nuestras muestras de datos remotas en nuestro navegador de muestras y la documentación te ofrecen muchos detalles sobre cómo cargar servicios remotos de OData en la red. Pero no nos quedamos ahí, además, también puedes crear tus propias versionesVirtualDataSource personalizadas para virtualizar el acceso a otros tipos de datos remotos o incluso locales. De hecho, recientemente, los clientes nos preguntaban si era posible usar nuestra red de datos con una base de datos SQLite, sin antes cargar todos los datos de una tabla en memoria. Esto sería necesario si quisieras proporcionar alguna colección directamente a la propiedad ItemsSource en la cuadrícula, pero hay una mejor forma si extiendesVirtualDataSource. Por suerte para ti, yo ya lo hice.

Si construyes ese proyecto, acabarás con una versión específica de SQLite de los nuestrosVirtualDataSource. Esto permite enlazar a una tabla o a un conjunto conjunto de tablas unidas, y luego permitirte pasar las páginas sin esfuerzo como si navegaras por una gran colección continua e ininterrumpida. Mejor aún, puedes limitar la cantidad de páginas de datos que la fuente de datos guardará en memoria al mismo tiempo, para poder poner un límite superior al uso de memoria en tu aplicación móvil.

SQLite Database Setup

Vale, pues pongámoslo en práctica. Dado que tienes un proyecto Xamarin.Forms configurado usando elXamDataGrid, primero necesitas añadir una base de datos SQLite a la app de Android y a la app de iOS. En la app de Android, esto va en los assets:

En el caso de la aplicación de Android, esto va en los activos

ElBuild Action para la base de datos debe marcarse comoAndroidAsset:

La acción de compilación de la base de datos debe marcarse como AndroidAsset

Dado esto, esta lógica, cuando se insertaMainActivity.cs justo antes de que se inicialice Xamarin.Forms y antes de que se cree la aplicación principal, asegurará que la base de datos SQLite sea accesible para la aplicación en tiempo de ejecución:

string targetPath = 
    System.Environment.GetFolderPath(
        System.Environment.SpecialFolder.Personal
    );
var path = Path.Combine(
    targetPath, "chinook.db");

if (!File.Exists(path))
{
    using (Stream input = 
        Assets.Open("chinook.db"))
    {
        using (var fs = new FileStream(
            path, 
            FileMode.Create))
        {
            input.CopyTo(fs);
        }
    }
}

Para iOS, deberías colocar el archivo de base de datos en laResources aplicación correspondiente:

Para iOS, debe colocar el archivo de base de datos en los recursos de la aplicación

Y asegúrate de que elBuild Action esté configurado paraBundleResource:

Y asegúrese de que la acción de compilación esté establecida en BundleResource

Dado que el archivo de base de datos está correctamente incluido, esta lógica, al insertarseAppDelegate.cs justo antes de que se inicialice Xamarin.Forms y antes de crear la aplicación principal, garantizaría que sea accesible para la aplicación iOS en tiempo de ejecución:

var targetPath = Environment.GetFolderPath(
    Environment.SpecialFolder.Personal);
targetPath = Path.Combine(targetPath, "..", "Library");

var path = Path.Combine(targetPath, "chinook.db");
if (!File.Exists(path))
{
    var bundlePath = NSBundle.MainBundle.PathForResource(
        "chinook", 
        "db"
    );
    File.Copy(bundlePath, path);
} 

Para ambas plataformas, la ruta del archivo hacia la base de datos SQLite puede ahora pasarse a Xamarin.FormsApp cuando se crea:

LoadApplication(new App(path));

A continuación, la aplicación se asegurará de que la ruta esté disponible para la página que utilizaremos:

public App(string dbPath)
{
    InitializeComponent();
    MainPage = new SQLDemo.MainPage(dbPath);
}

Desplazamiento virtual en vivo a través de tablas SQLite

Para leer datos de una base de datos SQLite, primero necesitas un cliente SQLite compatible con una PCL (biblioteca de clases portátil) y/o Xamarin.Android/Xamarin.iOS, así que instalaremos elsqlite-net-pcl paquete Nuget.

por lo tanto, instalaremos el paquete Nuget sqlite-net-pcl.

La biblioteca SQLite.NET incluye una herramienta ORM ligera que utilizará para hidratar los datos que se leen en los tipos POCO, por lo que primero debemos crear un tipo POCO para la tabla que nos interesa:

using SQLite;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SQLDemo.Data
{
    [Table("tracks")]
    public class Track
    {
        [PrimaryKey, AutoIncrement]
        public int TrackId { get; set; }

        [MaxLength(200)]
        public string Name { get; set; }

        public int AlbumId { get; set; }

        [Column("Title")]
        public string AlbumTitle { get; set; }

        public int MediaTypeId { get; set; }

        public int GenreId { get; set; }

        [MaxLength(220)]
        public string Composer { get; set; }

        public int Milliseconds { get; set; }

        public int Bytes { get; set; }

        public decimal UnitPrice { get; set; }
    }
}

Este tipo se corresponde con latracks tabla de la base de datos de muestras SQLite de Chinook, que almacena datos de muestras sobre varias pistas de álbumes de música popular. Aquí hemos indicado, mediante los atributos, varias metainformaciones sobre la tabla, como la clave primaria, y las longitudes máximas de algunas columnas de cadena.

Ahora que los datos pueden cargarse desde latracks tabla, todos estamos preparados para desplazarnos sobre esa tabla en laXamDataGrid.

En primer lugar, podemos diseñar la cuadrícula en XAML:

<?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:local="clr-namespace:SQLDemo"
             x:Class="SQLDemo.MainPage"
             xmlns:igGrid="clr-namespace:Infragistics.XamarinForms.Controls.Grids;assembly=Infragistics.XF.DataGrid">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <igGrid:XamDataGrid x:Name="grid" RowHeight="90"
                        SelectionMode="MultipleRow"
                        HeaderClickAction="SortByMultipleColumnsTriState"
                        AutoGenerateColumns="False">
            <igGrid:XamDataGrid.Columns>
                <igGrid:TextColumn PropertyPath="Name" 
                                LineBreakMode="WordWrap" 
                                Width="1*"
                                />
                <igGrid:TextColumn PropertyPath="Composer" 
                                LineBreakMode="Ellipsis"
                                Width="1.25*"/>
                <igGrid:TextColumn PropertyPath="AlbumTitle" 
                        HeaderText="Album Title" 
                        LineBreakMode="WordWrap" 
                        Width="1*"/>
                <igGrid:NumericColumn PropertyPath="UnitPrice"
                        HeaderText="Unit Price"
                        MinFractionDigits="2"
                        Width="1*"/>
            </igGrid:XamDataGrid.Columns>
        </igGrid:XamDataGrid>
    </Grid>

</ContentPage>

En XAML, hemos definido yXamDataGrid configurado algunas columnas, como si estuviéramos a punto de vincular algunos datos en memoria a la cuadrícula. Podríamos haber omitido definir las columnas y permitir que se generaran automáticamente, pero hay suficientes columnas en latracks tabla como para que esto se saturara bastante.

Bien, entonces, ¿cómo vinculamos la cuadrícula con la tabla SQLite? Primero tenemos que crear una conexión para hablar con la base de datos SQLite:

_connection = new SQLiteAsyncConnection(dbPath);

Donde dbPath es la ruta de acceso del archivo a la base de datos SQLite que pasamos anteriormente. A continuación, solo tenemos que crear un SQLiteVirtualDataSource, configurarlo y asignarlo a la cuadrícula:

var dataSource = new SQLiteVirtualDataSource();
dataSource.Connection = _connection;
dataSource.TableExpression = 
    "tracks left outer join albums on tracks.AlbumId = albums.AlbumId";
dataSource.ProjectionType = typeof(Track);

grid.ItemsSource = dataSource;

Aquí:

  • Proporcione la conexión que creamos a la fuente de datos virtual.
  • Proporcione una expresión de tabla al origen de datos virtual para indicar de qué tabla extraer datos.
  • Indique el tipo de POCO que creamos para hidratar las filas de datos.

En elTableExpression simplemente podríamos haber proporcionadotracks, alternativamente, pero este ejemplo crea una unión contra la tabla de álbumes para buscar los títulos de los álbumes y así poder rellenarse en laAlbumTitle propiedad.

¡Y eso es todo! Si ejecuta la aplicación, verá que puede desplazarse por la tabla como si fuera solo un conjunto largo y contiguo de registros. Sin embargo, en realidad, solo una fracción de la tabla está en la memoria del dispositivo a la vez. Es posible que tenga problemas para desplazarse lo suficientemente rápido como para ver un escenario en el que llegue a algunos registros antes de que se hayan cargado, porque la cuadrícula en realidad los carga de manera predictiva en segundo plano. Así es como se verá:

Así es como se verá

Sin embargo, puede ver que la cuadrícula se pone al día si cambia el tipo de la cuadrícula tocando los encabezados de las columnas. Esto hace que se invaliden los datos actuales del lado del cliente y que se obtengan los nuevos datos, ordenados según lo solicitado, pero, de nuevo, solo en la cantidad necesaria.

Adición de algunos filtros

Ok, tomemos eso y hagamos las cosas un poco más elegantes, ¿de acuerdo? En primer lugar, agregue esto a la cuadrícula del XAML de la página:

<StackLayout Orientation="Horizontal" Grid.Row="1">
    <Label Text="Filter" />
    <Entry TextChanged="Entry_TextChanged" WidthRequest="300" />
</StackLayout>

Ese marcado ha agregado un campo de entrada para que podamos recopilar un valor de filtro por el cual filtrar la tabla que estamos mostrando. Un evento se activa cada vez que el texto de la entrada ha cambiado. Así que agreguemos el controlador para eso al código subyacente:

private void Entry_TextChanged(object sender, TextChangedEventArgs e)
{
    if (String.IsNullOrEmpty(e.NewTextValue))
    {
        grid.FilterExpressions.Clear();
    }
    else
    {
        grid.FilterExpressions.Clear();
        grid.FilterExpressions.Add(FilterFactory.Build(
            (f) =>
            {
                return f.Property("Name").Contains(e.NewTextValue)
                .Or(f.Property("AlbumTitle").Contains(e.NewTextValue))
                .Or(f.Property("Composer").Contains(e.NewTextValue));
            }));
    }
}

Este código borrará los filtros de cuadrícula si el campo de entrada se queda en blanco, pero de lo contrario, creará un filtro para ver si Name o AlbumTitle o Composer coinciden con la cadena proporcionada y se asegurará de que el filtro se use en las consultas pasadas a SQLite.

Este es el aspecto que tiene ahora el ejemplo:

Como puede ver, cada vez que escriba una letra, la cuadrícula local deberá actualizar su contenido con el nuevo contenido filtrado, por el que luego puede desplazarse en su totalidad.

Puedes obtener más información consultando nuestras lecciones y videos "Escribe rápido" y "Corre rápido". También querrá asegurarse de descargar una versión de prueba gratuita de Infragistics Ultimate UI for Xamarin.

Graham Murray es arquitecto de software y autor. Crea componentes de interfaz de usuario multiplataforma de alto rendimiento para Infragistics, que abarcan equipos de escritorio, web y dispositivos móviles. Síguelo en Twitter en @the_graham.

Solicitar una demostración