Crear un diseño personalizado en Xamarin.Forms

Ejemplo de descarga Descarga del ejemplo

Xamarin.Forms define cinco clases de diseño: StackLayout, AbsoluteLayout, RelativeLayout, Grid y FlexLayout, y cada una organiza sus elementos secundarios de una manera diferente. Sin embargo, a veces es necesario organizar el contenido de la página mediante un diseño no proporcionado por Xamarin.FormsXamarin.Forms . En este artículo se explica cómo escribir una clase de diseño personalizada y se muestra una clase WrapLayout sensible a la orientación que organiza horizontalmente sus elementos secundarios en la página y, a continuación, ajusta la presentación de los elementos secundarios posteriores en filas adicionales.

En Xamarin.Forms , todas las clases de diseño derivan de la clase y restringen el tipo genérico a y Layout<T> sus tipos View derivados. A su vez, la clase se deriva de la clase , que proporciona el mecanismo para colocar y cambiar Layout<T> el tamaño de los elementos Layout secundarios.

Cada elemento visual es responsable de determinar su propio tamaño preferido, que se conoce como tamaño solicitado. PageLos tipos derivados de , y son responsables de determinar la ubicación y el tamaño de sus elementos secundarios Layout o secundarios en relación con ellos Layout<View> mismos. Por lo tanto, el diseño implica una relación de elementos primarios y secundarios, donde el elemento primario determina cuál debe ser el tamaño de sus elementos secundarios, pero intentará acomodar el tamaño solicitado del elemento secundario.

Para crear un diseño personalizado, se necesita una comprensión exhaustiva del diseño y los ciclos Xamarin.Forms de invalidación. Ahora se tratarán estos ciclos.

Diseño

El diseño comienza en la parte superior del árbol visual con una página y continúa por todas las ramas del árbol visual para abarcar todos los elementos visuales de una página. Los elementos que son elementos principales de otros elementos son responsables de dimensionar y colocar sus elementos secundarios en relación con ellos mismos.

La clase define un método VisualElementVisualElement Xamarin_Forms _VisualElement_Measure_System_Double_System_Double_ _MeasureFlags_" data-linktype="absolute-path">que mide un elemento para las operaciones de diseño y un método Xamarin_Forms Xamarin_Forms _VisualElement_Layout_ MeasureXamarin_Forms _Rectangle_" data-linktype="absolute-path">Layout que especifica el área rectangular en la que se representará el elemento. Cuando se inicia una aplicación y se muestra la primera página, se inicia en el objeto un ciclo de diseño que consta primero de llamadas y, a continuación, llamadas a LayoutPage :

  1. Durante el ciclo de diseño, cada elemento primario es responsable de llamar al Measure método en sus elementos secundarios.
  2. Una vez medidos los elementos secundarios, cada elemento primario es responsable de llamar al Layout método en sus elementos secundarios.

Este ciclo garantiza que todos los elementos visuales de la página reciben llamadas a los MeasureLayout métodos y . El proceso se muestra en el diagrama siguiente:

<span class= Ciclo de diseño de Xamarin.Forms"  data-linktype=

Nota:

Tenga en cuenta que los ciclos de diseño también pueden producirse en un subconjunto del árbol visual si algo cambia para afectar al diseño. Esto incluye los elementos que se agregan o quitan de una colección, como en , un cambio en la propiedad StackLayoutStackLayout data-linktype="absolute-path">de un elemento de Xamarin_Forms _VisualElement_IsVisible" o un cambio en el tamaño de IsVisible un elemento.

Todas las clases que tienen una propiedad o tienen una propiedad Xamarin_Forms Xamarin.FormsContentChildrenXamarin.Forms _Layout_LayoutChildren_System_Double_System_Double_System_Double_System_Double_" data-linktype="absolute-path">LayoutChildren método. Las clases de diseño personalizadas que derivan de deben invalidar este método y asegurarse de que se llama a los métodos Layout<View>Layout<View>Xamarin_FormsMeasureXamarin_Forms data-linktype="absolute-path">y Xamarin_Forms _VisualElement_Layout_ _Rectangle_" data-linktype="absolute-path">Layout en todos los elementos secundarios del elemento para proporcionar el diseño personalizado deseado. _VisualElement_Measure_System_Double_System_Double_ Xamarin_Forms

Además, cada clase que deriva de o debe invalidar el método data-linktype="absolute-path">de Xamarin_Forms _VisualElement_OnMeasure_System_Double_System_Double_", que es donde una clase de diseño determina el tamaño que debe tener realizando llamadas a los LayoutLayout<View>LayoutOnMeasure métodos Xamarin_Forms Layout<View> _VisualElement_Measure_System_Double_System_Double_ Xamarin_Forms _MeasureFlags_" data-linktype="absolute-path">Measure de sus secundarios.

Nota:

Los elementos determinan su tamaño en función de las restricciones, que indican cuánto espacio está disponible para un elemento dentro del elemento primario del elemento. Las restricciones pasadas a Xamarin_Forms _VisualElement_Measure_System_Double_System_Double_ Xamarin_Forms _MeasureFlags_" data-linktype="absolute-path">Measure and Xamarin_Forms Xamarin_Forms _VisualElement_OnMeasure_System_Double_System_Double_" data-linktype="absolute-path">methods pueden OnMeasureDouble.PositiveInfinity oscilar entre 0 y . Un elemento está restringido ototalmente restringido cuandorecibe una llamada a su método Xamarin_Forms con argumentos no infinitos: el elemento está restringido a un tamaño determinado. Un elemento está sin restricciones,o parcialmente restringido,cuando recibe una llamada a su método con al menos un argumento igual a : la restricción infinita se puede pensar en que indica el cambio automático.

Invalidación

La invalidación es el proceso por el que un cambio en un elemento de una página desencadena un nuevo ciclo de diseño. Los elementos se consideran no válidos cuando ya no tienen el tamaño o la posición correctos. Por ejemplo, si la propiedad Xamarin_Forms _Button_FontSize" data-linktype="absolute-path">de un cambio, se dice que no es válida porque ya no tendrá el FontSize tamaño ButtonButton correcto. El cambio de tamaño puede tener un efecto dominó de cambios en el Button diseño en el resto de una página.

Los elementos se invalidan invocando el método Xamarin_Forms _VisualElement_InvalidateMeasure" data-linktype="absolute-path">, generalmente cuando cambia una propiedad del elemento que podría dar lugar a un nuevo tamaño InvalidateMeasure del elemento. Este método activa el evento , que el elemento primario MeasureInvalidated controla para desencadenar un nuevo ciclo de diseño.

La clase establece un controlador para el evento en cada elemento secundario agregado a su propiedad o colección, y desasocula el controlador cuando Layout se quita el elemento MeasureInvalidatedContentChildren secundario. Por lo tanto, cada elemento del árbol visual que tiene elementos secundarios se alerta cada vez que uno de sus elementos secundarios cambia de tamaño. En el diagrama siguiente se muestra cómo un cambio en el tamaño de un elemento del árbol visual puede provocar cambios que ondee el árbol:

Invalidación en el árbol visual

Sin embargo, la clase intenta restringir el impacto de un cambio en el tamaño de un elemento Layout secundario en el diseño de una página. Si el diseño tiene un tamaño restringido, un cambio de tamaño secundario no afecta a nada mayor que el diseño primario del árbol visual. Sin embargo, normalmente un cambio en el tamaño de un diseño afecta a la forma en que el diseño organiza sus secundarios. Por lo tanto, cualquier cambio en el tamaño de un diseño iniciará un ciclo de diseño para el diseño y el diseño recibirá llamadas a sus métodos Xamarin_Forms _VisualElement_OnMeasure_System_Double_System_Double_" data-linktype="absolute-path">OnMeasure y Xamarin_Forms OnMeasure _Layout_LayoutChildren_System_Double_System_Double_System_Double_System_Double_" data-linktype="absolute-path">. LayoutChildren

La clase también define un método Xamarin_Forms _Layout_InvalidateLayout" data-linktype="absolute-path">que tiene un propósito similar al del método LayoutLayoutInvalidateLayout Xamarin_Forms _VisualElement_InvalidateMeasure" data-linktype="absolute-path">. InvalidateMeasure El método se debe invocar siempre que se realiza un cambio que afecta a cómo el diseño InvalidateLayout coloca y cambia el tamaño de sus secundarios. Por ejemplo, la clase invoca el método cada vez que se agrega o quita un elemento LayoutInvalidateLayout secundario de un diseño.

El Xamarin_Forms _Layout_InvalidateLayout" data-linktype="absolute-path">se puede invalidar para implementar una caché para minimizar las invocaciones repetitivas de los métodos de Xamarin_Forms _VisualElement_Measure_System_Double_System_Double_ InvalidateLayoutInvalidateLayoutXamarin_Forms _MeasureFlags_" data-linktype="absolute-path">Measure de los elementos secundarios del diseño. Al invalidar el método se proporcionará una notificación de cuándo se agregan o quitan los secundarios InvalidateLayout del diseño. Del mismo modo, el método Xamarin_Forms _Layout_OnChildMeasureInvalidated" data-linktype="absolute-path">se puede invalidar para proporcionar una notificación cuando uno de los secundarios del diseño cambia OnChildMeasureInvalidated de tamaño. Para ambas invalidaciones de método, un diseño personalizado debe responder borrando la memoria caché. Para obtener más información, vea Calculate and Cache Layout Data.

Crear un diseño personalizado

El proceso para crear un diseño personalizado es el siguiente:

  1. Cree una clase que se derive de la clase Layout<View>. Para obtener más información, vea Create a WrapLayout.

  2. [opcional] Agregue propiedades, con el respaldo de propiedades enlazables, para cualquier parámetro que se deba establecer en la clase de diseño. Para obtener más información, vea Agregar propiedades con el respaldo de propiedades enlazables.

  3. Invalide el método Xamarin_Forms _VisualElement_OnMeasure_System_Double_System_Double_" data-linktype="absolute-path">para invocar el método OnMeasure Xamarin_Forms _VisualElement_Measure_System_Double_System_Double_ OnMeasureXamarin_Forms _MeasureFlags_" data-linktype="absolute-path">en Measure todos los secundarios del diseño y devolver un tamaño solicitado para el diseño. Para obtener más información, vea Invalidar el método OnMeasure.

  4. Invalide el método Xamarin_Forms _Layout_LayoutChildren_System_Double_System_Double_System_Double_System_Double_" data-linktype="absolute-path">para invocar el método LayoutChildren Xamarin_Forms _VisualElement_Layout_ LayoutChildrenXamarin_Forms _Rectangle_" data-linktype="absolute-path">Layout en todos los secundarios del diseño. Si no se invoca el método Xamarin_Forms _VisualElement_Layout_ Xamarin_Forms _Rectangle_" data-linktype="absolute-path">en cada elemento secundario de un diseño, el elemento secundario nunca recibirá un tamaño o una posición correctos y, por Layout tanto, el elemento secundario no estará visible en la página. Para obtener más información, vea Invalidar el método LayoutChildren.

    Nota:

    Al enumerar los elementos secundarios en las invalidaciones de _VisualElement_OnMeasure_System_Double_System_Double_ Xamarin_Forms data-linktype="absolute-path">OnMeasure and Xamarin_Forms OnMeasure _Layout_LayoutChildren_System_Double_System_Double_System_Double_System_Double_" data-linktype="absolute-path">overrides", omita cualquier elemento secundario cuya propiedad LayoutChildren data-linktype="absolute-path">de Xamarin_Forms _VisualElement_IsVisible" IsVisiblefalse esté establecida en . Esto garantizará que el diseño personalizado no deje espacio para los elementos secundarios invisibles.

  5. [opcional] Invalide el Xamarin_Forms para recibir una notificación cuando se agregan o quitan los secundarios del diseño. Para obtener más información, vea Invalidar el método InvalidateLayout.

  6. [opcional] Invalide el Xamarin_Forms _Layout_OnChildMeasureInvalidated" data-linktype="absolute-path">para recibir una notificación cuando uno de los secundarios del diseño cambie de tamaño. Para obtener más información, vea Invalidar el método OnChildMeasureInvalidated.

Nota:

Tenga en cuenta que la invalidación Xamarin_Forms _VisualElement_OnMeasure_System_Double_System_Double_" data-linktype="absolute-path">no se invocará si el tamaño del diseño se rige por su elemento primario, en lugar de por sus elementos OnMeasure secundarios. Sin embargo, se invocará la invalidación si una o ambas restricciones son infinitas, o si la clase de diseño tiene valores de propiedad Xamarin_Forms _View_HorizontalOptions" data-linktype="absolute-path">o HorizontalOptionsHorizontalOptions Xamarin_Forms _View_VerticalOptions" data-linktype="absolute-path">. VerticalOptions Por este motivo, la invalidación de Xamarin_Forms _Layout_LayoutChildren_System_Double_System_Double_System_Double_System_Double_" data-linktype="absolute-path">no puede basarse en los tamaños secundarios obtenidos durante la llamada al método LayoutChildren Xamarin_Forms LayoutChildren _VisualElement_OnMeasure_System_Double_System_Double_" data-linktype="absolute-path">. OnMeasure En su lugar, debe invocar el método LayoutChildrenLayoutChildren Xamarin_Forms _VisualElement_Measure_System_Double_System_Double_ _MeasureFlags_" data-linktype="absolute-path">en los secundarios del diseño, antes de invocar el método Xamarin_Forms Xamarin_Forms _VisualElement_Layout_ MeasureXamarin_Forms _Rectangle_" data-linktype="absolute-path">. Layout Como alternativa, el tamaño de los secundarios obtenidos en la invalidación se puede almacenar en caché para evitar invocaciones posteriores en la invalidación, pero la clase de diseño deberá saber cuándo deben obtenerse los tamaños de OnMeasureMeasureLayoutChildren nuevo. Para obtener más información, vea Calculate and Cache Layout Data.

La clase de diseño se puede consumir agregándole a y agregando Page elementos secundarios al diseño. Para obtener más información, vea Consume the WrapLayout.

Crear un wraplayout

La aplicación de ejemplo muestra una clase sensible a la orientación que organiza horizontalmente sus secundarios en la página y, a continuación, ajusta la presentación de los siguientes secundarios en WrapLayout filas adicionales.

La clase asigna la misma cantidad de espacio para cada elemento secundario, conocido como tamaño de celda , en función del WrapLayout tamaño máximo de los elementos secundarios. WrapLayout _View_HorizontalOptions Xamarin_Forms Los secundarios menores que el tamaño de celda se pueden colocar dentro de la celda en función de sus valores de propiedad data-linktype="absolute-path">y HorizontalOptionsHorizontalOptions Xamarin_Forms _View_VerticalOptions" data-linktype="absolute-path">. VerticalOptions

La WrapLayout definición de clase se muestra en el ejemplo de código siguiente:

public class WrapLayout : Layout<View>
{
  Dictionary<Size, LayoutData> layoutDataCache = new Dictionary<Size, LayoutData>();
  ...
}

Cálculo y almacenamiento en caché de datos de diseño

La LayoutData estructura almacena datos sobre una colección de elementos secundarios en varias propiedades:

  • VisibleChildCount : el número de secundarios que están visibles en el diseño.
  • CellSize : el tamaño máximo de todos los secundarios, ajustado al tamaño del diseño.
  • Rows : el número de filas.
  • Columns : el número de columnas.

El layoutDataCache campo se usa para almacenar varios LayoutData valores. Cuando se inicia la aplicación, dos objetos se almacenarán en caché en el diccionario para la orientación actual: uno para los argumentos de restricción para la invalidación y otro para los argumentos y para la LayoutDatalayoutDataCacheOnMeasurewidthheightLayoutChildren invalidación. Al girar el dispositivo en orientación horizontal, se invocará de nuevo la invalidación y la invalidación, lo que dará lugar a que otros dos objetos se almacenen en caché OnMeasureLayoutChildren en el LayoutData diccionario. Sin embargo, al devolver el dispositivo a la orientación vertical, no se requieren cálculos adicionales porque ya layoutDataCache tiene los datos necesarios.

En el ejemplo de código siguiente se GetLayoutData muestra el método , que calcula las propiedades del estructurado en función de un tamaño LayoutData determinado:

LayoutData GetLayoutData(double width, double height)
{
  Size size = new Size(width, height);

  // Check if cached information is available.
  if (layoutDataCache.ContainsKey(size))
  {
    return layoutDataCache[size];
  }

  int visibleChildCount = 0;
  Size maxChildSize = new Size();
  int rows = 0;
  int columns = 0;
  LayoutData layoutData = new LayoutData();

  // Enumerate through all the children.
  foreach (View child in Children)
  {
    // Skip invisible children.
    if (!child.IsVisible)
      continue;

    // Count the visible children.
    visibleChildCount++;

    // Get the child's requested size.
    SizeRequest childSizeRequest = child.Measure(Double.PositiveInfinity, Double.PositiveInfinity);

    // Accumulate the maximum child size.
    maxChildSize.Width = Math.Max(maxChildSize.Width, childSizeRequest.Request.Width);
    maxChildSize.Height = Math.Max(maxChildSize.Height, childSizeRequest.Request.Height);
  }

  if (visibleChildCount != 0)
  {
    // Calculate the number of rows and columns.
    if (Double.IsPositiveInfinity(width))
    {
      columns = visibleChildCount;
      rows = 1;
    }
    else
    {
      columns = (int)((width + ColumnSpacing) / (maxChildSize.Width + ColumnSpacing));
      columns = Math.Max(1, columns);
      rows = (visibleChildCount + columns - 1) / columns;
    }

    // Now maximize the cell size based on the layout size.
    Size cellSize = new Size();

    if (Double.IsPositiveInfinity(width))
      cellSize.Width = maxChildSize.Width;
    else
      cellSize.Width = (width - ColumnSpacing * (columns - 1)) / columns;

    if (Double.IsPositiveInfinity(height))
      cellSize.Height = maxChildSize.Height;
    else
      cellSize.Height = (height - RowSpacing * (rows - 1)) / rows;

    layoutData = new LayoutData(visibleChildCount, cellSize, rows, columns);
  }

  layoutDataCache.Add(size, layoutData);
  return layoutData;
}

El GetLayoutData método realiza las siguientes operaciones:

  • Determina si un valor calculado ya está en la memoria caché y LayoutData lo devuelve si está disponible.
  • De lo contrario, enumera todos los elementos secundarios, invocando el método en cada elemento secundario con un ancho y alto infinitos, y determina el Measure tamaño secundario máximo.
  • Siempre que haya al menos un elemento secundario visible, calcula el número de filas y columnas necesarias y, a continuación, calcula un tamaño de celda para los elementos secundarios en función de las dimensiones de WrapLayout . Tenga en cuenta que el tamaño de la celda suele ser ligeramente más ancho que el tamaño secundario máximo, pero que también podría ser menor si no es lo suficientemente ancho para el elemento secundario más ancho o lo suficientemente alto para el elemento secundario más WrapLayout alto.
  • Almacena el nuevo LayoutData valor en la memoria caché.

Agregar propiedades con el respaldo de propiedades enlazables

La clase define las propiedades y , cuyos valores se usan para separar las filas y columnas del diseño, y que están copiadas WrapLayoutColumnSpacing por propiedades RowSpacing enlazables. Las propiedades enlazables se muestran en el ejemplo de código siguiente:

public static readonly BindableProperty ColumnSpacingProperty = BindableProperty.Create(
  "ColumnSpacing",
  typeof(double),
  typeof(WrapLayout),
  5.0,
  propertyChanged: (bindable, oldvalue, newvalue) =>
  {
    ((WrapLayout)bindable).InvalidateLayout();
  });

public static readonly BindableProperty RowSpacingProperty = BindableProperty.Create(
  "RowSpacing",
  typeof(double),
  typeof(WrapLayout),
  5.0,
  propertyChanged: (bindable, oldvalue, newvalue) =>
  {
    ((WrapLayout)bindable).InvalidateLayout();
  });

El controlador de cambio de propiedad de cada propiedad enlazable invoca la invalidación del método para desencadenar un nuevo paso InvalidateLayout de diseño en WrapLayout . Para obtener más información, vea Invalidar el método InvalidateLayout e Invalidar el método OnChildMeasureInvalidated.

Invalidar el método OnMeasure

La OnMeasure invalidación se muestra en el ejemplo de código siguiente:

protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
  LayoutData layoutData = GetLayoutData(widthConstraint, heightConstraint);
  if (layoutData.VisibleChildCount == 0)
  {
    return new SizeRequest();
  }

  Size totalSize = new Size(layoutData.CellSize.Width * layoutData.Columns + ColumnSpacing * (layoutData.Columns - 1),
                layoutData.CellSize.Height * layoutData.Rows + RowSpacing * (layoutData.Rows - 1));
  return new SizeRequest(totalSize);
}

La invalidación invoca el método y construye un objeto a partir de los datos devueltos, al tiempo que tiene en cuenta los GetLayoutData valores de propiedad y SizeRequestRowSpacingColumnSpacing . Para obtener más información sobre el GetLayoutData método , vea Calculate and Cache Layout GetLayoutData.

Importante

Los Xamarin_Forms _VisualElement_Measure_System_Double_System_Double_ Xamarin_Forms _MeasureFlags_" data-linktype="absolute-path">Measure and Xamarin_Forms Xamarin_Forms _VisualElement_OnMeasure_System_Double_System_Double_" data-linktype="absolute-path">methods should never request OnMeasure an infinite dimension by returning a SizeRequest value with a property set to Double.PositiveInfinity . Sin embargo, al menos uno de los argumentos de restricción para OnMeasure puede ser Double.PositiveInfinity .

Invalidar el método LayoutChildren

La LayoutChildren invalidación se muestra en el ejemplo de código siguiente:

protected override void LayoutChildren(double x, double y, double width, double height)
{
  LayoutData layoutData = GetLayoutData(width, height);

  if (layoutData.VisibleChildCount == 0)
  {
    return;
  }

  double xChild = x;
  double yChild = y;
  int row = 0;
  int column = 0;

  foreach (View child in Children)
  {
    if (!child.IsVisible)
    {
      continue;
    }

    LayoutChildIntoBoundingRegion(child, new Rectangle(new Point(xChild, yChild), layoutData.CellSize));
    if (++column == layoutData.Columns)
    {
      column = 0;
      row++;
      xChild = x;
      yChild += RowSpacing + layoutData.CellSize.Height;
    }
    else
    {
      xChild += ColumnSpacing + layoutData.CellSize.Width;
    }
  }
}

La invalidación comienza con una llamada al método y, a continuación, enumera todos los elementos secundarios para cambiar su tamaño y colocarlos dentro de la GetLayoutData celda de cada elemento secundario. Esto se consigue invocando el método Xamarin_Forms _Layout_LayoutChildIntoBoundingRegion_ _VisualElement_ _Rectangle_" data-linktype="absolute-path">_View_HorizontalOptions Xamarin_Forms, que se usa para colocar un elemento secundario dentro de un rectángulo en función de sus valores de propiedad Xamarin_FormsXamarin_FormsLayoutChildIntoBoundingRegionXamarin_Forms data-linktype="absolute-path">y HorizontalOptions Xamarin_Forms Xamarin_Forms _View_VerticalOptions" data-linktype="absolute-path">. VerticalOptions Esto equivale a realizar una llamada al método Xamarin_Forms _VisualElement_Layout_ Xamarin_Forms _Rectangle_" data-linktype="absolute-path">Layout secundario.

Nota:

Tenga en cuenta que el rectángulo pasado al LayoutChildIntoBoundingRegion método incluye todo el área en la que puede residir el elemento secundario.

Para obtener más información sobre el GetLayoutData método , vea Calculate and Cache Layout GetLayoutData.

Invalidar el método InvalidateLayout

La invalidación Xamarin_Forms _Layout_InvalidateLayout" data-linktype="absolute-path">se invoca cuando se agregan elementos secundarios al diseño o se quitan del diseño, o cuando una de las propiedades cambia el valor, como se muestra en el ejemplo de código InvalidateLayoutWrapLayout siguiente:

protected override void InvalidateLayout()
{
  base.InvalidateLayout();
  layoutInfoCache.Clear();
}

La invalidación invalida el diseño y descarta toda la información de diseño almacenada en caché.

Nota:

Para detener la clase que invoca el método Xamarin_Forms _Layout_InvalidateLayout" data-linktype="absolute-path">cada vez que se agrega o quita un elemento secundario de un diseño, invalide los métodos LayoutLayoutInvalidateLayout Xamarin_Forms Xamarin_Forms data-linktype="absolute-path">_Layout_ShouldInvalidateOnChildAdded_ y ShouldInvalidateOnChildAdded Xamarin_Forms InvalidateLayout _Layout_ShouldInvalidateOnChildRemoved_ Xamarin_Forms _View_" data-linktype="absolute-path">ShouldInvalidateOnChildRemoved y devuelva false . La clase de diseño puede implementar un proceso personalizado cuando se agregan o quitan elementos secundarios.

Invalidar el método OnChildMeasureInvalidated

El Xamarin_Forms _Layout_OnChildMeasureInvalidated" data-linktype="absolute-path">override se invoca cuando uno de los elementos secundarios del diseño cambia de tamaño y se muestra en el ejemplo de código OnChildMeasureInvalidated siguiente:

protected override void OnChildMeasureInvalidated()
{
  base.OnChildMeasureInvalidated();
  layoutInfoCache.Clear();
}

La invalidación invalida el diseño secundario y descarta toda la información de diseño almacenada en caché.

Consumo de WrapLayout

La clase se puede consumir si se coloca en un tipo derivado, como se muestra WrapLayout en el siguiente ejemplo de código Page XAML:

<ContentPage ... xmlns:local="clr-namespace:ImageWrapLayout">
    <ScrollView Margin="0,20,0,20">
        <local:WrapLayout x:Name="wrapLayout" />
    </ScrollView>
</ContentPage>

El código de C# equivalente se muestra a continuación:

public class ImageWrapLayoutPageCS : ContentPage
{
  WrapLayout wrapLayout;

  public ImageWrapLayoutPageCS()
  {
    wrapLayout = new WrapLayout();

    Content = new ScrollView
    {
      Margin = new Thickness(0, 20, 0, 20),
      Content = wrapLayout
    };
  }
  ...
}

A continuación, los secundarios se pueden agregar a WrapLayout según sea necesario. En el ejemplo de código siguiente Image se muestran los elementos que se agregan a WrapLayout :

protected override async void OnAppearing()
{
    base.OnAppearing();

    var images = await GetImageListAsync();
    if (images != null)
    {
        foreach (var photo in images.Photos)
        {
            var image = new Image
            {
                Source = ImageSource.FromUri(new Uri(photo))
            };
            wrapLayout.Children.Add(image);
        }
    }
}

async Task<ImageList> GetImageListAsync()
{
    try
    {
        string requestUri = "https://raw.githubusercontent.com/xamarin/docs-archive/master/Images/stock/small/stock.json";
        string result = await _client.GetStringAsync(requestUri);
        return JsonConvert.DeserializeObject<ImageList>(result);
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"\tERROR: {ex.Message}");
    }

    return null;
}

Cuando aparece la página que contiene , la aplicación de ejemplo accede de forma asincrónica a un archivo JSON remoto que contiene una lista de fotos, crea un elemento para cada foto y lo agrega WrapLayout a ImageWrapLayout . El resultado es el aspecto que se muestra en las capturas de pantalla siguientes:

Capturas de pantalla verticales de aplicación de ejemplo

En las capturas de pantalla siguientes se WrapLayout muestra una vez girado a la orientación horizontal:

Captura de pantalla horizontal de aplicación de iOS de ejemploDe ejemplo de aplicación de Android Capturade pantalla de ejemplo de aplicación paraUWP Captura de pantalla horizontal de la aplicación

El número de columnas de cada fila depende del tamaño de la foto, el ancho de pantalla y el número de píxeles por unidad independiente del dispositivo. Los elementos cargan de forma asincrónica las fotos y, por tanto, la clase recibirá llamadas frecuentes a su método ImageWrapLayout Xamarin_Forms Image _Layout_LayoutChildren_System_Double_System_Double_System_Double_System_Double_" data-linktype="absolute-path">, LayoutChildren ya Image que cada elemento recibe un nuevo tamaño en función de la foto cargada.