Udostępnij za pośrednictwem


Dostosowywanie obiektu ViewCell

Obiekt Xamarin.Forms ViewCell to komórka, którą można dodać do widoku ListView lub TableView, który zawiera widok zdefiniowany przez dewelopera. W tym artykule pokazano, jak utworzyć niestandardowy moduł renderujący dla kontrolki ViewCell hostowanej w kontrolce Xamarin.Forms ListView. Xamarin.Forms Uniemożliwia to wielokrotne wywoływanie obliczeń układu podczas przewijania elementu ListView.

Każda Xamarin.Forms komórka ma towarzyszący moduł renderujący dla każdej platformy, który tworzy wystąpienie kontrolki natywnej. Gdy element ViewCell jest renderowany przez aplikację Xamarin.Forms , w systemie iOS ViewCellRenderer klasę tworzy wystąpienie, co z kolei tworzy wystąpienie kontrolki natywnej UITableViewCell . Na platformie ViewCellRenderer Android klasa tworzy wystąpienie natywnej View kontrolki. W platforma uniwersalna systemu Windows (UWP) ViewCellRenderer klasa tworzy wystąpienie natywnej DataTemplateklasy . Aby uzyskać więcej informacji na temat klasy renderera i natywnych kontrolek mapowanych Xamarin.Forms na, zobacz Renderer Base Classes and Native Controls (Klasy bazowe modułu renderowania i kontrolki natywne).

Na poniższym diagramie przedstawiono relację między kontrolkami ViewCell macierzystymi i odpowiadającymi im kontrolkami natywnymi, które je implementują:

Relacja między kontrolką ViewCell a implementacją kontrolek natywnych

Proces renderowania można wykorzystać w celu zaimplementowania dostosowań specyficznych dla platformy przez utworzenie niestandardowego modułu renderowania dla elementu na ViewCell każdej platformie. Proces wykonywania tej czynności jest następujący:

  1. Utwórz komórkę niestandardową Xamarin.Forms .
  2. Zużyj komórkę niestandardową z Xamarin.Formspliku .
  3. Utwórz niestandardowy moduł renderowania dla komórki na każdej platformie.

Każdy element zostanie omówiony z kolei w celu zaimplementowania NativeCell modułu renderowania, który korzysta z układu specyficznego dla platformy dla każdej komórki hostowanej wewnątrz kontrolki Xamarin.FormsListView . Xamarin.Forms Uniemożliwia to wielokrotne wywoływanie obliczeń układu podczas ListView przewijania.

Tworzenie komórki niestandardowej

Niestandardową kontrolkę komórki można utworzyć przez podklasę ViewCell klasy, jak pokazano w poniższym przykładzie kodu:

public class NativeCell : ViewCell
{
  public static readonly BindableProperty NameProperty =
    BindableProperty.Create ("Name", typeof(string), typeof(NativeCell), "");

  public string Name {
    get { return (string)GetValue (NameProperty); }
    set { SetValue (NameProperty, value); }
  }

  public static readonly BindableProperty CategoryProperty =
    BindableProperty.Create ("Category", typeof(string), typeof(NativeCell), "");

  public string Category {
    get { return (string)GetValue (CategoryProperty); }
    set { SetValue (CategoryProperty, value); }
  }

  public static readonly BindableProperty ImageFilenameProperty =
    BindableProperty.Create ("ImageFilename", typeof(string), typeof(NativeCell), "");

  public string ImageFilename {
    get { return (string)GetValue (ImageFilenameProperty); }
    set { SetValue (ImageFilenameProperty, value); }
  }
}

Klasa jest tworzona NativeCell w projekcie biblioteki .NET Standard i definiuje interfejs API dla komórki niestandardowej. Komórka niestandardowa uwidacznia Namewłaściwości , Categoryi ImageFilename , które mogą być wyświetlane za pomocą powiązania danych. Aby uzyskać więcej informacji na temat powiązania danych, zobacz Podstawy powiązań danych.

Korzystanie z komórki niestandardowej

Do NativeCell komórki niestandardowej można odwoływać się w pliku Xaml w projekcie biblioteki .NET Standard, deklarując przestrzeń nazw dla swojej lokalizacji i używając prefiksu przestrzeni nazw w niestandardowym elemencie komórki. Poniższy przykład kodu przedstawia sposób NativeCell korzystania z komórki niestandardowej przez stronę XAML:

<ContentPage ...
    xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
    ...>
    ...
    <ContentPage.Content>
          <StackLayout>
              <Label Text="Xamarin.Forms native cell" HorizontalTextAlignment="Center" />
              <ListView x:Name="listView" CachingStrategy="RecycleElement" ItemSelected="OnItemSelected">
                  <ListView.ItemTemplate>
                      <DataTemplate>
                          <local:NativeCell Name="{Binding Name}" Category="{Binding Category}" ImageFilename="{Binding ImageFilename}" />
                      </DataTemplate>
                  </ListView.ItemTemplate>
              </ListView>
          </StackLayout>
      </ContentPage.Content>
</ContentPage>

Prefiks local przestrzeni nazw może mieć nazwę dowolnych elementów. clr-namespace Jednak wartości i assembly muszą być zgodne ze szczegółami kontrolki niestandardowej. Po zadeklarowaniu przestrzeni nazw prefiks jest używany do odwołwania się do komórki niestandardowej.

W poniższym przykładzie kodu pokazano, jak NativeCell można używać komórki niestandardowej na stronie języka C#:

public class NativeCellPageCS : ContentPage
{
    ListView listView;

    public NativeCellPageCS()
    {
        listView = new ListView(ListViewCachingStrategy.RecycleElement)
        {
            ItemsSource = DataSource.GetList(),
            ItemTemplate = new DataTemplate(() =>
            {
                var nativeCell = new NativeCell();
                nativeCell.SetBinding(NativeCell.NameProperty, "Name");
                nativeCell.SetBinding(NativeCell.CategoryProperty, "Category");
                nativeCell.SetBinding(NativeCell.ImageFilenameProperty, "ImageFilename");

                return nativeCell;
            })
        };

        switch (Device.RuntimePlatform)
        {
            case Device.iOS:
                Padding = new Thickness(0, 20, 0, 0);
                break;
            case Device.Android:
            case Device.UWP:
                Padding = new Thickness(0);
                break;
        }

        Content = new StackLayout
        {
            Children = {
                new Label { Text = "Xamarin.Forms native cell", HorizontalTextAlignment = TextAlignment.Center },
                listView
            }
        };
        listView.ItemSelected += OnItemSelected;
    }
    ...
}

Kontrolka Xamarin.FormsListView służy do wyświetlania listy danych, która jest wypełniana za pośrednictwem ItemSource właściwości . Strategia RecycleElement buforowania próbuje zminimalizować zużycie pamięci i szybkość ListView wykonywania przez odtworzenie komórek listy. Aby uzyskać więcej informacji, zobacz Buforowanie Strategy (Strategia Buforowanie).

Każdy wiersz na liście zawiera trzy elementy danych — nazwę, kategorię i nazwę pliku obrazu. Układ każdego wiersza na liście jest definiowany przez DataTemplate przywoływaną ListView.ItemTemplate właściwość powiązaną. Definiuje DataTemplate , że każdy wiersz danych na liście będzie NativeCell zawierać jego Namewłaściwości , Categoryi ImageFilename za pomocą powiązania danych. Aby uzyskać więcej informacji na temat kontrolki ListView , zobacz ListView.

Niestandardowy moduł renderowania można teraz dodać do każdego projektu aplikacji, aby dostosować układ specyficzny dla platformy dla każdej komórki.

Tworzenie niestandardowego modułu renderowania na każdej platformie

Proces tworzenia niestandardowej klasy renderera jest następujący:

  1. Utwórz podklasę ViewCellRenderer klasy, która renderuje komórkę niestandardową.
  2. Zastąpij metodę specyficzną dla platformy, która renderuje komórkę niestandardową i zapisuje logikę, aby ją dostosować.
  3. ExportRenderer Dodaj atrybut do niestandardowej klasy renderera, aby określić, że będzie on używany do renderowania komórki niestandardowejXamarin.Forms. Ten atrybut służy do rejestrowania niestandardowego modułu renderowania za pomocą Xamarin.Formspolecenia .

Uwaga

W przypadku większości Xamarin.Forms elementów opcjonalne jest udostępnienie niestandardowego modułu renderowania w każdym projekcie platformy. Jeśli niestandardowy moduł renderowania nie jest zarejestrowany, zostanie użyty domyślny moduł renderowania dla klasy bazowej kontrolki. Jednak niestandardowe programy renderowania są wymagane w każdym projekcie platformy podczas renderowania elementu ViewCell .

Na poniższym diagramie przedstawiono obowiązki każdego projektu w przykładowej aplikacji wraz z relacjami między nimi:

Obowiązki projektu niestandardowego renderera NativeCell

Komórka niestandardowa NativeCell jest renderowana przez klasy renderera specyficzne dla platformy, które pochodzą z ViewCellRenderer klasy dla każdej platformy. Powoduje to renderowanie każdej NativeCell komórki niestandardowej z układem specyficznym dla platformy, jak pokazano na poniższych zrzutach ekranu:

NativeCell na każdej platformie

Klasa ViewCellRenderer uwidacznia metody specyficzne dla platformy do renderowania komórki niestandardowej. Jest GetCell to metoda na platformie iOS, GetCellCore metoda na platformie Android i GetTemplate metoda w systemie UWP.

Każda niestandardowa klasa modułu renderowania jest ozdobiona atrybutem ExportRenderer , który rejestruje program renderujący za pomocą Xamarin.Formspolecenia . Atrybut przyjmuje dwa parametry — nazwę Xamarin.Forms typu renderowanej komórki i nazwę typu niestandardowego modułu renderowania. Prefiks assembly atrybutu określa, że atrybut ma zastosowanie do całego zestawu.

W poniższych sekcjach omówiono implementację poszczególnych niestandardowych klas renderer specyficznych dla platformy.

Tworzenie niestandardowego modułu renderowania w systemie iOS

Poniższy przykład kodu przedstawia niestandardowy moduł renderowania dla platformy iOS:

[assembly: ExportRenderer(typeof(NativeCell), typeof(NativeiOSCellRenderer))]
namespace CustomRenderer.iOS
{
    public class NativeiOSCellRenderer : ViewCellRenderer
    {
        NativeiOSCell cell;

        public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
        {
            var nativeCell = (NativeCell)item;

            cell = reusableCell as NativeiOSCell;
            if (cell == null)
                cell = new NativeiOSCell(item.GetType().FullName, nativeCell);
            else
                cell.NativeCell.PropertyChanged -= OnNativeCellPropertyChanged;

            nativeCell.PropertyChanged += OnNativeCellPropertyChanged;
            cell.UpdateCell(nativeCell);
            return cell;
        }
        ...
    }
}

Metoda jest wywoływana GetCell w celu skompilowania każdej komórki do wyświetlenia. Każda komórka jest wystąpieniem NativeiOSCell , które definiuje układ komórki i jej danych. Operacja GetCell metody jest zależna od ListView strategii buforowania:

  • ListView Gdy strategia buforowania to RetainElement, GetCell metoda zostanie wywołana dla każdej komórki. Wystąpienie NativeiOSCell zostanie utworzone dla każdego NativeCell wystąpienia, które jest początkowo wyświetlane na ekranie. Gdy użytkownik przewija ListViewelement , NativeiOSCell wystąpienia zostaną ponownie użyte. Aby uzyskać więcej informacji na temat ponownego użycia komórek systemu iOS, zobacz Ponowne używanie komórek.

    Uwaga

    Ten niestandardowy kod modułu renderowania będzie ponownie używać niektórych komórek nawet wtedy, gdy właściwość jest ustawiona ListView na zachowanie komórek.

    Dane wyświetlane przez każde wystąpienie, niezależnie NativeiOSCell od tego, czy nowo utworzone, czy ponownie używane, zostaną zaktualizowane przy użyciu danych z każdego NativeCell wystąpienia za pomocą UpdateCell metody .

    Uwaga

    Metoda OnNativeCellPropertyChanged nigdy nie zostanie wywołana, gdy ListView strategia buforowania zostanie ustawiona tak, aby zachować komórki.

  • ListView Gdy strategia buforowania to RecycleElement, GetCell metoda zostanie wywołana dla każdej komórki, która jest początkowo wyświetlana na ekranie. Wystąpienie NativeiOSCell zostanie utworzone dla każdego NativeCell wystąpienia, które jest początkowo wyświetlane na ekranie. Dane wyświetlane przez każde NativeiOSCell wystąpienie zostaną zaktualizowane przy użyciu danych z NativeCell wystąpienia za pomocą UpdateCell metody . Jednak metoda nie zostanie wywołana, GetCell gdy użytkownik przewija ListViewelement . NativeiOSCell Zamiast tego wystąpienia zostaną ponownie użyte. PropertyChanged zdarzenia zostaną zgłoszone w wystąpieniu NativeCell , gdy zmienią się jego dane, a OnNativeCellPropertyChanged program obsługi zdarzeń zaktualizuje dane w każdym ponownie użytym NativeiOSCell wystąpieniu.

Poniższy przykład kodu przedstawia metodę OnNativeCellPropertyChanged wywoływaną podczas wywoływanego PropertyChanged zdarzenia:

namespace CustomRenderer.iOS
{
    public class NativeiOSCellRenderer : ViewCellRenderer
    {
        ...

        void OnNativeCellPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            var nativeCell = (NativeCell)sender;
            if (e.PropertyName == NativeCell.NameProperty.PropertyName)
            {
                cell.HeadingLabel.Text = nativeCell.Name;
            }
            else if (e.PropertyName == NativeCell.CategoryProperty.PropertyName)
            {
                cell.SubheadingLabel.Text = nativeCell.Category;
            }
            else if (e.PropertyName == NativeCell.ImageFilenameProperty.PropertyName)
            {
                cell.CellImageView.Image = cell.GetImage(nativeCell.ImageFilename);
            }
        }
    }
}

Ta metoda aktualizuje dane wyświetlane przez ponownie używane NativeiOSCell wystąpienia. Jest sprawdzana zmieniona właściwość, ponieważ metoda może być wywoływana wiele razy.

Klasa NativeiOSCell definiuje układ dla każdej komórki i jest wyświetlany w poniższym przykładzie kodu:

internal class NativeiOSCell : UITableViewCell, INativeElementView
{
  public UILabel HeadingLabel { get; set; }
  public UILabel SubheadingLabel { get; set; }
  public UIImageView CellImageView { get; set; }

  public NativeCell NativeCell { get; private set; }
  public Element Element => NativeCell;

  public NativeiOSCell(string cellId, NativeCell cell) : base(UITableViewCellStyle.Default, cellId)
  {
    NativeCell = cell;

    SelectionStyle = UITableViewCellSelectionStyle.Gray;
    ContentView.BackgroundColor = UIColor.FromRGB(255, 255, 224);
    CellImageView = new UIImageView();

    HeadingLabel = new UILabel()
    {
      Font = UIFont.FromName("Cochin-BoldItalic", 22f),
      TextColor = UIColor.FromRGB(127, 51, 0),
      BackgroundColor = UIColor.Clear
    };

    SubheadingLabel = new UILabel()
    {
      Font = UIFont.FromName("AmericanTypewriter", 12f),
      TextColor = UIColor.FromRGB(38, 127, 0),
      TextAlignment = UITextAlignment.Center,
      BackgroundColor = UIColor.Clear
    };

    ContentView.Add(HeadingLabel);
    ContentView.Add(SubheadingLabel);
    ContentView.Add(CellImageView);
  }

  public void UpdateCell(NativeCell cell)
  {
    HeadingLabel.Text = cell.Name;
    SubheadingLabel.Text = cell.Category;
    CellImageView.Image = GetImage(cell.ImageFilename);
  }

  public UIImage GetImage(string filename)
  {
    return (!string.IsNullOrWhiteSpace(filename)) ? UIImage.FromFile("Images/" + filename + ".jpg") : null;
  }

  public override void LayoutSubviews()
  {
    base.LayoutSubviews();

    HeadingLabel.Frame = new CGRect(5, 4, ContentView.Bounds.Width - 63, 25);
    SubheadingLabel.Frame = new CGRect(100, 18, 100, 20);
    CellImageView.Frame = new CGRect(ContentView.Bounds.Width - 63, 5, 33, 33);
  }
}

Ta klasa definiuje kontrolki używane do renderowania zawartości komórki i ich układu. Klasa implementuje INativeElementView interfejs, który jest wymagany, gdy ListView używa RecycleElement strategii buforowania. Ten interfejs określa, że klasa musi zaimplementować Element właściwość, która powinna zwracać niestandardowe dane komórek dla komórek pochodzących z recyklingu.

Konstruktor NativeiOSCell inicjuje wygląd HeadingLabelwłaściwości , SubheadingLabeli CellImageView . Te właściwości są używane do wyświetlania danych przechowywanych w wystąpieniu NativeCell z UpdateCell wywoływaną metodą ustawiania wartości każdej właściwości. Ponadto w przypadku ListView użycia RecycleElement strategii buforowania dane wyświetlane przez HeadingLabelelement , SubheadingLabeli CellImageView właściwości mogą być aktualizowane przez metodę OnNativeCellPropertyChanged w niestandardowym module renderowania.

Układ komórek jest wykonywany przez LayoutSubviews przesłonięcia, które ustawia współrzędne HeadingLabel, SubheadingLabeli CellImageView w komórce.

Tworzenie niestandardowego modułu renderowania w systemie Android

W poniższym przykładzie kodu pokazano niestandardowy moduł renderowania dla platformy Android:

[assembly: ExportRenderer(typeof(NativeCell), typeof(NativeAndroidCellRenderer))]
namespace CustomRenderer.Droid
{
    public class NativeAndroidCellRenderer : ViewCellRenderer
    {
        NativeAndroidCell cell;

        protected override Android.Views.View GetCellCore(Cell item, Android.Views.View convertView, ViewGroup parent, Context context)
        {
            var nativeCell = (NativeCell)item;
            Console.WriteLine("\t\t" + nativeCell.Name);

            cell = convertView as NativeAndroidCell;
            if (cell == null)
            {
                cell = new NativeAndroidCell(context, nativeCell);
            }
            else
            {
                cell.NativeCell.PropertyChanged -= OnNativeCellPropertyChanged;
            }

            nativeCell.PropertyChanged += OnNativeCellPropertyChanged;

            cell.UpdateCell(nativeCell);
            return cell;
        }
        ...
    }
}

Metoda jest wywoływana GetCellCore w celu skompilowania każdej komórki do wyświetlenia. Każda komórka jest wystąpieniem NativeAndroidCell , które definiuje układ komórki i jej danych. Operacja GetCellCore metody jest zależna od ListView strategii buforowania:

  • ListView Gdy strategia buforowania to RetainElement, GetCellCore metoda zostanie wywołana dla każdej komórki. Zostanie NativeAndroidCell utworzony dla każdego NativeCell wystąpienia, które jest początkowo wyświetlane na ekranie. Gdy użytkownik przewija ListViewelement , NativeAndroidCell wystąpienia zostaną ponownie użyte. Aby uzyskać więcej informacji na temat ponownego użycia komórek systemu Android, zobacz Temat Ponowne używanie widoku wiersza.

    Uwaga

    Należy pamiętać, że ten niestandardowy kod modułu renderowania wykona ponowne użycie komórki nawet wtedy, gdy ListView ustawiono opcję zachowywania komórek.

    Dane wyświetlane przez każde wystąpienie, niezależnie NativeAndroidCell od tego, czy nowo utworzone, czy ponownie używane, zostaną zaktualizowane przy użyciu danych z każdego NativeCell wystąpienia za pomocą UpdateCell metody .

    Uwaga

    Należy pamiętać, że podczas gdy OnNativeCellPropertyChanged metoda zostanie wywołana, gdy ListView właściwość zostanie ustawiona na zachowanie komórek, nie NativeAndroidCell zaktualizuje wartości właściwości.

  • ListView Gdy strategia buforowania to RecycleElement, GetCellCore metoda zostanie wywołana dla każdej komórki, która jest początkowo wyświetlana na ekranie. Wystąpienie NativeAndroidCell zostanie utworzone dla każdego NativeCell wystąpienia, które jest początkowo wyświetlane na ekranie. Dane wyświetlane przez każde NativeAndroidCell wystąpienie zostaną zaktualizowane przy użyciu danych z NativeCell wystąpienia za pomocą UpdateCell metody . Jednak metoda nie zostanie wywołana, GetCellCore gdy użytkownik przewija ListViewelement . NativeAndroidCell Zamiast tego wystąpienia zostaną ponownie użyte. PropertyChanged zdarzenia zostaną zgłoszone w wystąpieniu NativeCell , gdy zmienią się jego dane, a OnNativeCellPropertyChanged program obsługi zdarzeń zaktualizuje dane w każdym ponownie użytym NativeAndroidCell wystąpieniu.

Poniższy przykład kodu przedstawia metodę OnNativeCellPropertyChanged wywoływaną podczas wywoływanego PropertyChanged zdarzenia:

namespace CustomRenderer.Droid
{
    public class NativeAndroidCellRenderer : ViewCellRenderer
    {
        ...

        void OnNativeCellPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            var nativeCell = (NativeCell)sender;
            if (e.PropertyName == NativeCell.NameProperty.PropertyName)
            {
                cell.HeadingTextView.Text = nativeCell.Name;
            }
            else if (e.PropertyName == NativeCell.CategoryProperty.PropertyName)
            {
                cell.SubheadingTextView.Text = nativeCell.Category;
            }
            else if (e.PropertyName == NativeCell.ImageFilenameProperty.PropertyName)
            {
                cell.SetImage(nativeCell.ImageFilename);
            }
        }
    }
}

Ta metoda aktualizuje dane wyświetlane przez ponownie używane NativeAndroidCell wystąpienia. Jest sprawdzana zmieniona właściwość, ponieważ metoda może być wywoływana wiele razy.

Klasa NativeAndroidCell definiuje układ dla każdej komórki i jest wyświetlany w poniższym przykładzie kodu:

internal class NativeAndroidCell : LinearLayout, INativeElementView
{
  public TextView HeadingTextView { get; set; }
  public TextView SubheadingTextView { get; set; }
  public ImageView ImageView { get; set; }

  public NativeCell NativeCell { get; private set; }
  public Element Element => NativeCell;

  public NativeAndroidCell(Context context, NativeCell cell) : base(context)
  {
    NativeCell = cell;

    var view = (context as Activity).LayoutInflater.Inflate(Resource.Layout.NativeAndroidCell, null);
    HeadingTextView = view.FindViewById<TextView>(Resource.Id.HeadingText);
    SubheadingTextView = view.FindViewById<TextView>(Resource.Id.SubheadingText);
    ImageView = view.FindViewById<ImageView>(Resource.Id.Image);

    AddView(view);
  }

  public void UpdateCell(NativeCell cell)
  {
    HeadingTextView.Text = cell.Name;
    SubheadingTextView.Text = cell.Category;

    // Dispose of the old image
    if (ImageView.Drawable != null)
    {
      using (var image = ImageView.Drawable as BitmapDrawable)
      {
        if (image != null)
        {
          if (image.Bitmap != null)
          {
            image.Bitmap.Dispose();
          }
        }
      }
    }

    SetImage(cell.ImageFilename);
  }

  public void SetImage(string filename)
  {
    if (!string.IsNullOrWhiteSpace(filename))
    {
      // Display new image
      Context.Resources.GetBitmapAsync(filename).ContinueWith((t) =>
      {
        var bitmap = t.Result;
        if (bitmap != null)
        {
          ImageView.SetImageBitmap(bitmap);
          bitmap.Dispose();
        }
      }, TaskScheduler.FromCurrentSynchronizationContext());
    }
    else
    {
      // Clear the image
      ImageView.SetImageBitmap(null);
    }
  }
}

Ta klasa definiuje kontrolki używane do renderowania zawartości komórki i ich układu. Klasa implementuje INativeElementView interfejs, który jest wymagany, gdy ListView używa RecycleElement strategii buforowania. Ten interfejs określa, że klasa musi zaimplementować Element właściwość, która powinna zwracać niestandardowe dane komórek dla komórek pochodzących z recyklingu.

Konstruktor NativeAndroidCell zawyża NativeAndroidCell układ i inicjuje HeadingTextViewwłaściwości , SubheadingTextViewi ImageView do kontrolek w zawyżonym układzie. Te właściwości są używane do wyświetlania danych przechowywanych w wystąpieniu NativeCell z UpdateCell wywoływaną metodą ustawiania wartości każdej właściwości. Ponadto w przypadku ListView użycia RecycleElement strategii buforowania dane wyświetlane przez HeadingTextViewelement , SubheadingTextViewi ImageView właściwości mogą być aktualizowane przez metodę OnNativeCellPropertyChanged w niestandardowym module renderowania.

Poniższy przykład kodu przedstawia definicję NativeAndroidCell.axml układu dla pliku układu:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:padding="8dp"
    android:background="@drawable/CustomSelector">
    <LinearLayout
        android:id="@+id/Text"
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="10dip">
        <TextView
            android:id="@+id/HeadingText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#FF7F3300"
            android:textSize="20dip"
            android:textStyle="italic" />
        <TextView
            android:id="@+id/SubheadingText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="14dip"
            android:textColor="#FF267F00"
            android:paddingLeft="100dip" />
    </LinearLayout>
    <ImageView
        android:id="@+id/Image"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:padding="5dp"
        android:src="@drawable/icon"
        android:layout_alignParentRight="true" />
</RelativeLayout>

Ten układ określa, że dwie TextView kontrolki i kontrolka ImageView są używane do wyświetlania zawartości komórki. Te dwie TextView kontrolki są zorientowane w pionie w obrębie LinearLayout kontrolki, a wszystkie kontrolki znajdują się w obiekcie RelativeLayout.

Tworzenie niestandardowego modułu renderowania na platformie UWP

W poniższym przykładzie kodu pokazano niestandardowy moduł renderowania dla platformy UWP:

[assembly: ExportRenderer(typeof(NativeCell), typeof(NativeUWPCellRenderer))]
namespace CustomRenderer.UWP
{
    public class NativeUWPCellRenderer : ViewCellRenderer
    {
        public override Windows.UI.Xaml.DataTemplate GetTemplate(Cell cell)
        {
            return App.Current.Resources["ListViewItemTemplate"] as Windows.UI.Xaml.DataTemplate;
        }
    }
}

Metoda GetTemplate jest wywoływana w celu zwrócenia komórki do renderowania dla każdego wiersza danych na liście. Tworzy dla DataTemplate każdego NativeCell wystąpienia, które będzie wyświetlane na ekranie z DataTemplate definiowaniem wyglądu i zawartości komórki.

Element DataTemplate jest przechowywany w słowniku zasobów na poziomie aplikacji i jest wyświetlany w poniższym przykładzie kodu:

<DataTemplate x:Key="ListViewItemTemplate">
    <Grid Background="LightYellow">
        <Grid.Resources>
            <local:ConcatImageExtensionConverter x:Name="ConcatImageExtensionConverter" />
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.40*" />
            <ColumnDefinition Width="0.40*"/>
            <ColumnDefinition Width="0.20*" />
        </Grid.ColumnDefinitions>
        <TextBlock Grid.ColumnSpan="2" Foreground="#7F3300" FontStyle="Italic" FontSize="22" VerticalAlignment="Top" Text="{Binding Name}" />
        <TextBlock Grid.RowSpan="2" Grid.Column="1" Foreground="#267F00" FontWeight="Bold" FontSize="12" VerticalAlignment="Bottom" Text="{Binding Category}" />
        <Image Grid.RowSpan="2" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Center" Source="{Binding ImageFilename, Converter={StaticResource ConcatImageExtensionConverter}}" Width="50" Height="50" />
        <Line Grid.Row="1" Grid.ColumnSpan="3" X1="0" X2="1" Margin="30,20,0,0" StrokeThickness="1" Stroke="LightGray" Stretch="Fill" VerticalAlignment="Bottom" />
    </Grid>
</DataTemplate>

Określa DataTemplate kontrolki używane do wyświetlania zawartości komórki oraz ich układu i wyglądu. Dwie TextBlock kontrolki i kontrolka Image są używane do wyświetlania zawartości komórki za pomocą powiązania danych. Ponadto wystąpienie obiektu ConcatImageExtensionConverter służy do łączenia .jpg rozszerzenia pliku z każdą nazwą pliku obrazu. Dzięki temu kontrolka Image może ładować i renderować obraz po ustawieniu jej Source właściwości.

Podsumowanie

W tym artykule pokazano, jak utworzyć niestandardowy moduł renderujący dla kontrolki ViewCell hostowanej w kontrolce Xamarin.FormsListView . Xamarin.Forms Uniemożliwia to wielokrotne wywoływanie obliczeń układu podczas ListView przewijania.