내용으로 건너뛰기
메모리 오버헤드가 적은 Xamarin.Forms의 큰 SQLite 테이블을 원활하게 스크롤합니다.

메모리 오버헤드가 적은 Xamarin.Forms의 큰 SQLite 테이블을 원활하게 스크롤합니다.

이미 Infragistics' Xamarin.Forms/Xamarin.Android/Xamarin.iOS DataGrid(XamDataGrid)로 작업한 적이 있다면 몇 가지 매우 깔끔한 트릭을 알고 있음을 알게 될 것입니다.

9min read

 원격 OData 서비스에 바인딩하고 행을 스크롤할 수 있으며, 이동 속도를 사용하여 데이터를 가져와야 할 시기를 예측하고 도달하기 전에 원활하게 로드해야 합니다. 아직 이 트릭을 못했다면 이것을 잘 보여주는 Ultimate UI for Xamarin 용 샘플 브라우저를 확인하십시오.

Running the Sample

이 문서에서 빌드할 샘플을 여기에서 가져올 수 있습니다. 샘플을 열면 로컬 리포지토리에 평가판 또는 RTM Nuget 패키지가 있는지 확인하고 Nuget 패키지를 복원해야 합니다.

다른 데이터 소스에 대한 액세스 가상화

샘플 브라우저와 문서에 포함된 원격 데이터 샘플은 원격 OData 서비스를 그리드에 불러오는 방법에 대한 많은 세부 정보를 제공합니다. 하지만 거기서 멈추지 않았고, 추가로 다른 유형의 원격 또는 로컬 데이터에 대한 가상화 버전을 직접 만들VirtualDataSource 수도 있습니다. 사실 최근 고객들이 테이블의 모든 데이터를 메모리에 불러오지 않고도 SQLite 데이터베이스와 함께 데이터 그리드를 사용할 수 있는지 문의했습니다. 그리드의 ItemsSource 속성에 직접 컬렉션을 제공하고 싶다면 이 방법이 필요하지만, 확장VirtualDataSource 하면 더 나은 방법이 있습니다. 다행히도 난 이미 했 어.

그 프로젝트를 만들면 SQLite 전용 버전VirtualDataSource이 만들어지게 됩니다. 이 기능은 테이블이나 연결된 테이블 집합에 링크를 걸고, 마치 크고 끊기지 않은 연속된 컬렉션을 스크롤하듯 매끄럽게 페이지를 넘길 수 있게 해줍니다. 더 나아가, 데이터 소스가 한 번에 메모리에 저장할 데이터 페이지 수를 제한할 수 있어, 모바일 애플리케이션에서 메모리 사용량에 상한선을 설정할 수 있습니다.

SQLite Database Setup

좋아요, 그럼 실천해 봅시다. Xamarin.Forms 프로젝트가 설정되어 있다면XamDataGrid, 먼저 Android와 iOS 앱에 SQLite 데이터베이스를 추가해야 합니다. 안드로이드 앱의 경우, 이 내용은 자산에 포함됩니다:

Android 앱의 경우 자산에 포함됩니다.

데이터베이스 값은Build Action 다음과AndroidAsset 같이 표시해야 합니다:

데이터베이스의 빌드 작업은 AndroidAsset으로 표시되어야 합니다.

이 논리를 Xamarin.Forms 초기화 직전, 메인 앱이 생성되기 전에 넣MainActivity.cs 으면, 런타임에 SQLite 데이터베이스가 애플리케이션에 접근할 수 있도록 보장합니다:

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);
        }
    }
}

iOS의 경우, 데이터베이스 파일을 애플리케이션에Resources 넣어야 합니다:

iOS의 경우 데이터베이스 파일을 애플리케이션의 리소스에 배치해야 합니다

그리고 다음Build Action 설정BundleResource이 있는지 확인하세요:

빌드 작업이 BundleResource로 설정되어 있는지 확인합니다

데이터베이스 파일이 제대로 포함되어 있다면, 이 로직을 Xamarin.Forms 초기화 직전, 메인 앱이 생성되기 전에 배치AppDelegate.cs 하면 실행 시 iOS 애플리케이션에 접근할 수 있도록 보장합니다:

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);
} 

두 플랫폼 모두에서 SQLite 데이터베이스의 파일 경로는 Xamarin.FormsApp가 생성될 때 전달될 수 있습니다:

LoadApplication(new App(path));

그런 다음 앱은 사용할 페이지에서 경로를 사용할 수 있는지 확인합니다.

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

SQLite 테이블을 통한 실시간 가상 스크롤

SQLite 데이터베이스에서 데이터를 읽으려면, 먼저 PCL(휴대용 클래스 라이브러리)과/또는 Xamarin.Android/Xamarin.iOS와 호환되는 SQLite 클라이언트가 필요합니다. 그래서 다음 파일을 설치하겠습니다.sqlite-net-pcl Nuget 패키지.

그래서 sqlite-net-pcl Nuget 패키지를 설치할 것입니다.

SQLite.NET 라이브러리에는 POCO 유형으로 읽는 데이터를 하이드레이션하는 데 사용할 경량 ORM 도구가 포함되어 있으므로 먼저 관심 있는 테이블에 대한 POCO 유형을 만들어야 합니다.

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; }
    }
}

이 유형은 인기 음악 앨범의 다양한 트랙에 대한 샘플 데이터를 저장하는 치누크 SQLite 샘플 데이터베이스의 테이블에 매핑tracks 됩니다. 여기서는 속성(attribute)을 통해 기본 키, 일부 문자열 열의 최대 길이 등 테이블에 관한 다양한 메타 정보를 표시했습니다.

이제 테이블에서tracks 데이터를 로드할 수 있으니, 우리는 그 테이블XamDataGrid 위를 스크롤할 수 있도록 모두 설정되었습니다.

먼저 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>

XAML에서는 AXamDataGrid를 정의하고 몇 개의 열을 설정했는데, 마치 메모리 내 데이터를 그리드에 바인딩하려는 것과 같습니다. 열을 정의하지 않고 자동 생성하도록 할 수도 있었지만, 테이블에tracks 열이 너무 많아 매우 복잡해질 것입니다.

그렇다면, SQLite 테이블에 그리드를 어떻게 바인딩할까요? 먼저 SQLite 데이터베이스와 통신하기 위한 연결을 만들어야 합니다.

_connection = new SQLiteAsyncConnection(dbPath);

여기서 dbPath는 이전에 전달한 SQLite 데이터베이스의 파일 경로입니다. 그런 다음 SQLiteVirtualDataSource를 만들고 구성한 다음 그리드에 할당하기만 하면 됩니다.

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;

여기 우리가:

  • 가상 데이터 소스에 대해 만든 연결을 제공합니다.
  • 가상 데이터 소스에 테이블 표현식을 제공하여 데이터를 가져올 테이블을 나타냅니다.
  • 데이터 행을 하이드레이션하기 위해 만든 POCO 형식을 나타냅니다.

TableExpression또는 'We Had Provide'tracks를 제공했을 수도 있지만, 이 예시는 앨범 테이블에 조인을 만들어 앨범 제목을 찾아 속성에AlbumTitle 채울 수 있게 합니다.

그리고 그게 다야! 앱을 실행하면 테이블을 하나의 긴 연속 레코드 집합인 것처럼 스크롤할 수 있음을 알 수 있습니다. 그러나 실제로는 테이블의 일부만 한 번에 장치의 메모리에 있습니다. 로드되기 전에 일부 레코드에 도달하는 시나리오를 볼 수 있을 만큼 빠르게 스크롤하는 데 문제가 있을 수 있는데, 그리드가 실제로 커버 아래에서 레코드를 예측적으로 로드하기 때문입니다. 다음과 같습니다.

다음과 같습니다.

그리드가 따라잡는 것을 볼 수 있지만 열 헤더를 탭하여 그리드 종류를 변경하면 됩니다. 이로 인해 현재 클라이언트 측 데이터가 무효화되고 요청에 따라 정렬 된 새 데이터를 가져 오지만 다시 필요한만큼만 가져옵니다.

일부 필터링 추가

좋아, 그걸 가지고 좀 더 멋지게 만들어 볼까요? 먼저 페이지에 대한 XAML의 그리드에 다음을 추가합니다.

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

이 태그에는 입력 필드가 추가되어 표시되는 테이블을 필터링하는 데 사용할 필터 값을 수집할 수 있습니다. 항목의 텍스트가 변경될 때마다 이벤트가 발생합니다. 이제 코드 숨김에 이에 대한 핸들러를 추가해 보겠습니다.

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));
            }));
    }
}

이 코드는 입력 필드가 비어 있으면 그리드 필터를 지우지만, 그렇지 않으면 Name 또는 AlbumTitle 또는 Composer 가 제공된 문자열과 일치하는지 확인하고 SQLite에 전달된 쿼리에서 필터가 사용되는지 확인하는 필터를 빌드합니다.

이제 샘플의 모양은 다음과 같습니다.

보시다시피 문자를 입력할 때마다 로컬 그리드는 새로 필터링된 콘텐츠로 콘텐츠를 새로 고쳐야 하며, 그런 다음 전체를 스크롤할 수 있습니다.

"빨리 쓰기" 및 "빨리 실행" 레슨과 비디오를 확인하여 더 자세히 알아볼 수 있습니다. 또한 Infragistics Ultimate UI for Xamarin의 무료 평가판을 다운로드하고 싶을 것입니다.

Graham Murray는 소프트웨어 아키텍트이자 작가입니다. 그는 데스크톱, 웹 및 모바일을 아우르는 Infragistics를 위한 고성능 크로스 플랫폼 UI 구성 요소를 구축합니다. Twitter에서 그를 팔로우하세요 @the_graham.

데모 요청