사용자 지정 레이아웃 만들기Creating a Custom Layout

샘플 다운로드 샘플 다운로드Download Sample Download the sample

Xamarin.Forms 4 레이아웃 클래스 – StackLayout, AbsoluteLayout, RelativeLayout, 및 그리드를 정의 하 고 자식을 다른 방식으로 정렬 하는 각 키를 누릅니다. 그러나 경우에 Xamarin.Forms에서 제공 하지 않는 레이아웃을 사용 하 여 페이지 콘텐츠를 구성 해야 합니다. 이 문서는 사용자 지정 레이아웃 클래스를 작성 하는 방법에 설명 하 고 페이지에 걸쳐 가로로 자식을 정렬 하 고 그런 다음 후속 자식이 추가 행을 표시 하는 방향에 민감한 WrapLayout 클래스를 보여 줍니다.Xamarin.Forms defines four layout classes – StackLayout, AbsoluteLayout, RelativeLayout, and Grid, and each arranges its children in a different way. However, sometimes it's necessary to organize page content using a layout not provided by Xamarin.Forms. This article explains how to write a custom layout class, and demonstrates an orientation-sensitive WrapLayout class that arranges its children horizontally across the page, and then wraps the display of subsequent children to additional rows.

개요Overview

Xamarin.forms에 모든 레이아웃 클래스에서 파생 된 Layout<T> 클래스 및 제네릭 형식 제약 View 및 파생된 형식입니다.In Xamarin.Forms, all layout classes derive from the Layout<T> class and constrain the generic type to View and its derived types. 차례로 합니다 Layout<T> 클래스에서 파생 되는 Layout 요소 배치 및 크기 조정 자식 메커니즘을 제공 하는 클래스입니다.In turn, the Layout<T> class derives from the Layout class, which provides the mechanism for positioning and sizing child elements.

모든 시각적 요소는 이라고 하는 자체 기본 크기를 결정 하는 일을 담당 합니다 요청한 크기.Every visual element is responsible for determining its own preferred size, which is known as the requested size. Page하십시오 Layout , 및 Layout<View> 파생 형식은 위치와 해당 자식 또는 하위 항목 자체를 기준으로 크기를 결정 합니다.Page, Layout, and Layout<View> derived types are responsible for determining the location and size of their child, or children, relative to themselves. 레이아웃 위치 부모 결정 해야 자식의 크기를 부모-자식 관계를 포함 하는 따라서 하지만 자식 요청된 된 크기에 맞게 시도 됩니다.Therefore, layout involves a parent-child relationship, where the parent determines what the size of its children should be, but will attempt to accommodate the requested size of the child.

Xamarin.Forms 레이아웃 및 무효화 순환의 철저 한 이해를 사용자 지정 레이아웃을 만들 필요 합니다.A thorough understanding of the Xamarin.Forms layout and invalidation cycles is required to create a custom layout. 이제 이러한 주기를 설명 합니다.These cycles will now be discussed.

레이아웃Layout

레이아웃 페이지를 사용 하 여 시각적 트리의 맨 위에 있는 시작 및 페이지의 모든 시각적 요소를 포함 하도록 시각적 트리의 모든 분기를 진행 합니다.Layout begins at the top of the visual tree with a page, and it proceeds through all branches of the visual tree to encompass every visual element on a page. 다른 요소에는 부모 요소는 자체를 기준으로 자식을 배치 및 크기 조정 하는 일을 담당 합니다.Elements that are parents to other elements are responsible for sizing and positioning their children relative to themselves.

합니다 VisualElement 클래스 정의 Measure 레이아웃 작업에 대 한 요소를 측정 하는 방법으로 Layout 지정 하는 방법 요소는 사각형 영역 내에서 렌더링 됩니다.The VisualElement class defines a Measure method that measures an element for layout operations, and a Layout method that specifies the rectangular area the element will be rendered within. 응용 프로그램이 시작 될 때 첫 번째 페이지가 표시 됩니다는 레이아웃 주기가 중 구성 된 첫 번째 Measure 호출 차례로 Layout 호출을 시작에는 Page 개체:When an application starts and the first page is displayed, a layout cycle consisting first of Measure calls, and then Layout calls, starts on the Page object:

  1. 레이아웃 주기 동안 모든 부모 요소는 호출을 담당 합니다 Measure 자식 요소에는 메서드.During the layout cycle, every parent element is responsible for calling the Measure method on its children.
  2. 모든 부모 요소는 호출에 대 한 자식 항목을 측정 한 후의 Layout 자식 요소에는 메서드.After the children have been measured, every parent element is responsible for calling the Layout method on its children.

이 주기 페이지의 모든 시각적 요소에 대 한 호출 수신 되는지 확인 합니다 MeasureLayout 메서드.This cycle ensures that every visual element on the page receives calls to the Measure and Layout methods. 프로세스는 다음 다이어그램에 표시 됩니다.The process is shown in the following diagram:

참고

레이아웃에 영향을 변경 하는 경우 레이아웃 주기 시각적 트리의 하위 집합에서 발생할 수 있습니다 note 합니다.Note that layout cycles can also occur on a subset of the visual tree if something changes to affect the layout. 여기에 추가 되거나 에서처럼 이러한 컬렉션에서 제거할 항목을 StackLayout , 변경 합니다 IsVisible 요소 또는 요소의 크기를 변경 하는 속성입니다.This includes items being added or removed from a collection such as in a StackLayout, a change in the IsVisible property of an element, or a change in the size of an element.

에 있는 모든 Xamarin.Forms 클래스는 Content 또는 Children 속성에는 재정의 가능한 LayoutChildren 메서드.Every Xamarin.Forms class that has a Content or a Children property has an overridable LayoutChildren method. 파생 된 사용자 지정 레이아웃 클래스 Layout<View> 이 메서드를 재정의 하 고 있는지 확인 해야 합니다 Measure 하 고 Layout 메서드는 원하는 사용자 지정 레이아웃을 제공 하려면 요소의 모든 자식에서 호출 됩니다.Custom layout classes that derive from Layout<View> must override this method and ensure that the Measure and Layout methods are called on all the element's children, to provide the desired custom layout.

또한 모든 클래스에서 파생 되는 Layout 또는 Layout<View> 재정의 해야 합니다 OnMeasure 되는 경우 레이아웃 클래스는 메서드를 호출 하 여 하는 데 필요한 크기를 결정 합니다 Measure 자식의 메서드.In addition, every class that derives from Layout or Layout<View> must override the OnMeasure method, which is where a layout class determines the size that it needs to be by making calls to the Measure methods of its children.

참고

요소에 따라 해당 크기를 결정 제약 조건, 공간 요소의 부모 내에서 요소 수를 나타내는입니다.Elements determine their size based on constraints, which indicate how much space is available for an element within the element's parent. 제약 조건에 전달 합니다 Measure 하 고 OnMeasure 메서드 범위는 0에서 Double.PositiveInfinity.Constraints passed to the Measure and OnMeasure methods can range from 0 to Double.PositiveInfinity. 요소가 제한, 또는 완벽 하 게 제한에 대 한 호출을 받으면, 해당 Measure 메서드 유한 인수-를 사용 하 여 요소가 제한 됩니다 특정 크기입니다.An element is constrained, or fully constrained, when it receives a call to its Measure method with non-infinite arguments - the element is constrained to a particular size. 요소가 비제한, 또는 부분적으로 제한에 대 한 호출을 받으면, 해당 Measure 같음 하나 이상의 인수를 사용 하 여 메서드 Double.PositiveInfinity – 무한 제약 조건 수 나타내는 크기 자동 조정으로 간주 합니다.An element is unconstrained, or partially constrained, when it receives a call to its Measure method with at least one argument equal to Double.PositiveInfinity – the infinite constraint can be thought of as indicating autosizing.

무효화Invalidation

무효화는 변경 페이지에서 요소에는 새 레이아웃 주기를 트리거하는 프로세스입니다.Invalidation is the process by which a change in an element on a page triggers a new layout cycle. 요소는 올바른 크기 또는 위치를 더 이상 없을 때에 잘못 된 간주 됩니다.Elements are considered invalid when they no longer have the correct size or position. 예를 들어 경우는 FontSize 의 속성을 Button 변경은 Button 이 더 이상 올바른 크기 때문에 유효 하지 않은 것 이라고 합니다.For example, if the FontSize property of a Button changes, the Button is said to be invalid because it will no longer have the correct size. 크기 조정 된 Button 변경의 영향을 미치기 레이아웃 페이지의 나머지 부분에 있을 수 있습니다.Resizing the Button may then have a ripple effect of changes in layout through the rest of a page.

요소는 자체를 호출 하 여 무효화 합니다 InvalidateMeasure 메서드를 일반적으로 변경 될 때 요소의 속성 있는 요소의 새 크기입니다.Elements invalidate themselves by invoking the InvalidateMeasure method, generally when a property of the element changes that might result in a new size of the element. 이 메서드가 발생 합니다 MeasureInvalidated 새 레이아웃 주기를 트리거하는 요소의 부모를 처리 하는 이벤트입니다.This method fires the MeasureInvalidated event, which the element's parent handles to trigger a new layout cycle.

Layout 클래스에 대 한 처리기를 설정 합니다.는 MeasureInvalidated 이벤트에 추가 하는 모든 자식에 해당 Content 속성 또는 Children 컬렉션 처리기를 분리 및 경우를 자식 제거 됩니다.The Layout class sets a handler for the MeasureInvalidated event on every child added to its Content property or Children collection, and detaches the handler when the child is removed. 따라서 자식이 있는 시각적 트리의 모든 요소 크기 자식 중 하나가 변경 될 때마다 경고가 표시 됩니다.Therefore, every element in the visual tree that has children is alerted whenever one of its children changes size. 다음 다이어그램에서는 시각적 트리에 있는 요소의 크기 변경 트리 ripple 변경 발생할 수 있습니다 하는 방법을 보여 줍니다.The following diagram illustrates how a change in the size of an element in the visual tree can cause changes that ripple up the tree:

그러나는 Layout 클래스는 페이지의 레이아웃에서 자식의 크기 변경의 영향을 제한 하려고 합니다.However, the Layout class attempts to restrict the impact of a change in a child's size on the layout of a page. 레이아웃 크기 제한 된 경우 다음 자식 크기 변경 영향을 주지 않습니다 시각적 트리의 상위 레이아웃 보다 높습니다.If the layout is size constrained, then a child size change does not affect anything higher than the parent layout in the visual tree. 그러나 일반적으로 레이아웃의 크기 변경에 영향을 줍니다 레이아웃 자식을 정렬 하는 방법.However, usually a change in the size of a layout affects how the layout arranges its children. 따라서 레이아웃의 크기 변경은 레이아웃에 대 한 레이아웃 주기를 시작 하 고 레이아웃에 대 한 호출을 받을 해당 OnMeasure 하 고 LayoutChildren 메서드.Therefore, any change in a layout's size will start a layout cycle for the layout, and the layout will receive calls to its OnMeasure and LayoutChildren methods.

합니다 Layout 클래스도 정의 InvalidateLayout 와 유사한 있는 메서드를 InvalidateMeasure 메서드.The Layout class also defines an InvalidateLayout method that has a similar purpose to the InvalidateMeasure method. InvalidateLayout 레이아웃을 배치 하 고 자식의 크기를 조정 하는 방법에 영향을 주는 변경 내용이 때마다 메서드를 호출 해야 합니다.The InvalidateLayout method should be invoked whenever a change is made that affects how the layout positions and sizes its children. 예를 들어 합니다 Layout 클래스를 호출 하는 InvalidateLayout 자식을 레이아웃에서 제거 또는 추가할 때마다 메서드.For example, the Layout class invokes the InvalidateLayout method whenever a child is added to or removed from a layout.

합니다 InvalidateLayout 반복 해 서 호출을 최소화 하기 위해 캐시를 구현 하도록 재정의할 수 있습니다 합니다 Measure 레이아웃의 자식의 메서드.The InvalidateLayout can be overridden to implement a cache to minimize repetitive invocations of the Measure methods of the layout's children. 재정의 InvalidateLayout 메서드 자식이 추가 되거나 레이아웃에서 제거 시기에 대 한 알림을 제공 합니다.Overriding the InvalidateLayout method will provide a notification of when children are added to or removed from the layout. 마찬가지로, 합니다 OnChildMeasureInvalidated 메서드를 재정의 하 여 레이아웃의 자식 중 하나가 크기를 변경 하는 경우 알림을 제공할 수 있습니다.Similarly, the OnChildMeasureInvalidated method can be overridden to provide a notification when one of the layout's children changes size. 두 메서드 재정의 대 한 사용자 지정 레이아웃 캐시를 지우면 응답 해야 합니다.For both method overrides, a custom layout should respond by clearing the cache. 자세한 내용은 계산 및 데이터 캐싱합니다.For more information, see Calculating and Caching Data.

사용자 지정 레이아웃 만들기Creating a Custom Layout

사용자 지정 레이아웃을 만드는 프로세스는 다음과 같습니다.The process for creating a custom layout is as follows:

  1. Layout<View> 클래스에서 파생되는 클래스를 만듭니다.Create a class that derives from the Layout<View> class. 자세한 내용은 는 WrapLayout 만들기합니다.For more information, see Creating a WrapLayout.
  2. [선택적] 레이아웃 클래스에 설정 해야 하는 모든 매개 변수에 대 한 바인딩 가능한 속성에서 지 원하는 속성을 추가 합니다.[optional] Add properties, backed by bindable properties, for any parameters that should be set on the layout class. 자세한 내용은 추가 바인딩 가능한 속성에서 지 원하는 속성합니다.For more information, see Adding Properties Backed by Bindable Properties.
  3. 재정의 OnMeasure 메서드를 호출 하는 Measure 레이아웃에 대 한 메서드는 레이아웃의 모든 자식에 요청 된 크기를 반환 합니다.Override the OnMeasure method to invoke the Measure method on all the layout's children, and return a requested size for the layout. 자세한 내용은 OnMeasure 메서드 재정의합니다.For more information, see Overriding the OnMeasure Method.
  4. 재정의 된 LayoutChildren 메서드를 호출 하는 Layout 레이아웃의 모든 자식에서 메서드.Override the LayoutChildren method to invoke the Layout method on all the layout's children. 호출에 실패 합니다 Layout 메서드는 레이아웃에 있는 각 자식에 올바른 크기 또는 위치를 수신 하지 자식 하면 이며 따라서 자식 페이지에 표시 있게 됩니다.Failure to invoke the Layout method on each child in a layout will result in the child never receiving a correct size or position, and hence the child will not become visible on the page. 자세한 내용은 LayoutChildren 메서드 재정의합니다.For more information, see Overriding the LayoutChildren Method.

참고

자식을 열거 하는 경우는 OnMeasure 하 고 LayoutChildren 재정의 건너뛸 모든 자식입니다 IsVisible 속성false.When enumerating children in the OnMeasure and LayoutChildren overrides, skip any child whose IsVisible property is set to false. 이렇게 하면 사용자 지정 레이아웃 보이지 않는 자식에 대 한 공간을 유지 하지 않습니다는 있습니다.This will ensure that the custom layout won't leave space for invisible children.

  1. [선택적] 재정의 InvalidateLayout 메서드 자식이 추가 되거나 레이아웃에서 제거 하는 경우 알림을 받을 수 있습니다.[optional] Override the InvalidateLayout method to be notified when children are added to or removed from the layout. 자세한 내용은 InvalidateLayout 메서드 재정의합니다.For more information, see Overriding the InvalidateLayout Method.
  2. [선택적] 재정의 OnChildMeasureInvalidated 메서드 레이아웃의 자식 중 하나가 크기를 변경 하는 경우 알림을 받을 수 있습니다.[optional] Override the OnChildMeasureInvalidated method to be notified when one of the layout's children changes size. 자세한 내용은 OnChildMeasureInvalidated 메서드 재정의합니다.For more information, see Overriding the OnChildMeasureInvalidated Method.

참고

이때 합니다 OnMeasure 레이아웃의 크기는 자식 보다는 해당 부모에 의해 제어 됩니다 하는 경우 재정의 호출할 수 없습니다.Note that the OnMeasure override won't be invoked if the size of the layout is governed by its parent, rather than its children. 레이아웃 클래스에 기본이 아닌 경우 또는 제약 조건 중 하나 이상이 한정 된 경우 재정의 호출할 수 있지만 HorizontalOptions 하거나 VerticalOptions 속성 값입니다.However, the override will be invoked if one or both of the constraints are infinite, or if the layout class has non-default HorizontalOptions or VerticalOptions property values. 이러한 이유로 합니다 LayoutChildren 재정의 하는 동안 얻은 자식 크기를 사용할 수 없게 합니다 OnMeasure 메서드 호출 합니다.For this reason, the LayoutChildren override can't rely on child sizes obtained during the OnMeasure method call. 대신 LayoutChildren 를 호출 해야 합니다는 Measure 메서드를 호출 하기 전에 레이아웃의 자식에는 Layout 메서드.Instead, LayoutChildren must invoke the Measure method on the layout's children, before invoking the Layout method. 자식의 크기를 얻은 또는 합니다 OnMeasure 나중 방지 하려면 재정의 캐시할 수 있습니다 Measure 호출을 LayoutChildren 재정의 하지만 레이아웃 클래스 크기를 다시 얻을 수 해야 하는 시기를 알고 있어야 합니다.Alternatively, the size of the children obtained in the OnMeasure override can be cached to avoid later Measure invocations in the LayoutChildren override, but the layout class will need to know when the sizes need to be obtained again. 자세한 내용은 계산 및 레이아웃 데이터 캐싱합니다.For more information, see Calculating and Caching Layout Data.

레이아웃 클래스를 추가 하 여 사용할 수 있습니다는 Page , 자식을 레이아웃에 추가 하 여 합니다.The layout class can then be consumed by adding it to a Page, and by adding children to the layout. 자세한 내용은 사용 하 여 WrapLayout합니다.For more information, see Consuming the WrapLayout.

WrapLayout 만들기Creating a WrapLayout

샘플 응용 프로그램을 보여 줍니다는 방향에 민감한 WrapLayout 페이지에 걸쳐 가로로 자식을 정렬 하 고 그런 다음 후속 자식이 추가 행을 표시 하는 클래스입니다.The sample application demonstrates an orientation-sensitive WrapLayout class that arranges its children horizontally across the page, and then wraps the display of subsequent children to additional rows.

WrapLayout 클래스 라고 하는 각 자식에 대 한 같은 크기의 공간을 할당 합니다 셀 크기자식 컨트롤의 최대 크기에 따라 합니다.The WrapLayout class allocates the same amount of space for each child, known as the cell size, based on the maximum size of the children. 자식 셀 크기를 셀 안에 배치 될 수 있습니다 보다 작은 기준으로 해당 HorizontalOptions 하 고 VerticalOptions 속성 값입니다.Children smaller than the cell size can be positioned within the cell based on their HorizontalOptions and VerticalOptions property values.

WrapLayout 클래스 정의 다음 코드 예제에 표시 됩니다.The WrapLayout class definition is shown in the following code example:

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

계산 및 레이아웃 데이터 캐싱Calculating and Caching Layout Data

LayoutData 구조는 다양 한 속성에서에서 자식 컬렉션에 대 한 데이터를 저장 합니다.The LayoutData structure stores data about a collection of children in a number of properties:

  • VisibleChildCount – 레이아웃에 표시 되는 자식의 수입니다.VisibleChildCount – the number of children that are visible in the layout.
  • CellSize – 모든 자식이 있는 레이아웃의 크기 조정의 최대 크기입니다.CellSize – the maximum size of all the children, adjusted to the size of the layout.
  • Rows – 행의 수입니다.Rows – the number of rows.
  • Columns -열 수 있습니다.Columns – the number of columns.

합니다 layoutDataCache 필드는 여러 LayoutData 값입니다.The layoutDataCache field is used to store multiple LayoutData values. 응용 프로그램이 시작 되 면 두 LayoutData 개체에 캐시 됩니다 합니다 layoutDataCache 사전에 대 한 제약 조건 인수에 대 한 현재 방향 –를 OnMeasure 재정의 및에 대 한를 widthheight 인수 에 LayoutChildren 재정의 합니다.When the application starts, two LayoutData objects will be cached into the layoutDataCache dictionary for the current orientation – one for the constraint arguments to the OnMeasure override, and one for the width and height arguments to the LayoutChildren override. 장치 가로 방향으로 회전 하는 경우는 OnMeasure 재정의 하며 LayoutChildren 재정의 다시 호출할 다른 두 결과 LayoutData 사전에 캐시 되는 개체입니다.When rotating the device into landscape orientation, the OnMeasure override and the LayoutChildren override will again be invoked, which will result in another two LayoutData objects being cached into the dictionary. 그러나 세로 방향으로 장치를 반환할 때 없는 추가 계산 되므로 layoutDataCache 이미는 필수 데이터를 포함 합니다.However, when returning the device to portrait orientation, no further calculations are required because the layoutDataCache already has the required data.

다음 코드 예제는 GetLayoutData 의 속성을 계산 하는 메서드는 LayoutData 구조화 된 특정 크기에 따라:The following code example shows the GetLayoutData method, which calculates the properties of the LayoutData structured based on a particular size:

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

GetLayoutData 메서드는 다음 작업을 수행 합니다.The GetLayoutData method performs the following operations:

  • 계산 여부를 결정 LayoutData 값 캐시에 이미 있고 사용 가능한 경우 사용자에 게 돌려보냅니다.It determines whether a calculated LayoutData value is already in the cache and returns it if it's available.
  • 호출 모든 자식을 열거이 고, 그렇지는 Measure 무한대의 너비 및 높이 사용 하 여 각 자식 메서드 고 자식 최대 크기를 결정 합니다.Otherwise, it enumerates through all the children, invoking the Measure method on each child with an infinite width and height, and determines the maximum child size.
  • 표시 되는 하나 이상의 자식이 제공한 행과 열 필요한 개수를 계산 하 고 다음의 크기에 따라 자식에 대 한 셀 크기를 계산 합니다 WrapLayout합니다.Provided that there's at least one visible child, it calculates the number of rows and columns required, and then calculates a cell size for the children based on the dimensions of the WrapLayout. 일반적으로 셀 크기는 최대 자식 크기 보다 약간 더 있지만 작은 일 수도 있습니다 경우는 WrapLayout 넓게 가장 넓은 자식 또는 충분히 설치에 대 한 가장 높은 자식 되지 않습니다.Note that the cell size is usually slightly wider than the maximum child size, but that it could also be smaller if the WrapLayout isn't wide enough for the widest child or tall enough for the tallest child.
  • 새 저장 LayoutData 캐시에는 값입니다.It stores the new LayoutData value in the cache.

바인딩 가능한 속성에서 지 원하는 속성 추가Adding Properties Backed by Bindable Properties

WrapLayout 클래스 정의 ColumnSpacingRowSpacing 속성 값이 포함 된 행과 열 레이아웃에서 구분 하는 데와 바인딩 가능한 속성에 의해 지원 됩니다.The WrapLayout class defines ColumnSpacing and RowSpacing properties, whose values are used to separate the rows and columns in the layout, and which are backed by bindable properties. 바인딩 가능한 속성은 다음 코드 예제에 나와 있습니다.The bindable properties are shown in the following code example:

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

각 바인딩 가능한 속성의 속성 변경 처리기를 호출 하는 InvalidateLayout 메서드 재정의를 트리거하는 새 레이아웃 전달 된 WrapLayout.The property-changed handler of each bindable property invokes the InvalidateLayout method override to trigger a new layout pass on the WrapLayout. 자세한 내용은 InvalidateLayout 메서드를 재정의 하 고 OnChildMeasureInvalidated 메서드를 재정의합니다.For more information, see Overriding the InvalidateLayout Method and Overriding the OnChildMeasureInvalidated Method.

OnMeasure 메서드 재정의Overriding the OnMeasure Method

OnMeasure 재정의 다음 코드 예제에 표시 됩니다.The OnMeasure override is shown in the following code example:

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

재정의 호출 합니다 GetLayoutData 메서드 및 생성자는 SizeRequest 도 고려 하는 동안 반환된 된 데이터에서 개체를 RowSpacingColumnSpacing 속성 값입니다.The override invokes the GetLayoutData method and constructs a SizeRequest object from the returned data, while also taking into account the RowSpacing and ColumnSpacing property values. 에 대 한 자세한 내용은 합니다 GetLayoutData 메서드를 참조 하세요 계산 및 데이터 캐싱합니다.For more information about the GetLayoutData method, see Calculating and Caching Data.

중요

합니다 Measure 하 고 OnMeasure 메서드를 반환 하 여 무한 차원에 있는 요청 되지 해야를 SizeRequest 값으로 설정 된 속성을 사용 하 여 Double.PositiveInfinity.The Measure and OnMeasure methods should never request an infinite dimension by returning a SizeRequest value with a property set to Double.PositiveInfinity. 그러나 하나 이상의 제약 조건 인수 OnMeasureDouble.PositiveInfinity입니다.However, at least one of the constraint arguments to OnMeasure can be Double.PositiveInfinity.

LayoutChildren 메서드 재정의Overriding the LayoutChildren Method

LayoutChildren 재정의 다음 코드 예제에 표시 됩니다.The LayoutChildren override is shown in the following code example:

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

재정의 호출 하 여 시작 된 GetLayoutData 메서드를 모든 자식의 크기와 각 자식 셀 내에서 해당 위치를 열거 합니다.The override begins with a call to the GetLayoutData method, and then enumerates all of the children to size and position them within each child's cell. 호출 하 여 이렇게 합니다 LayoutChildIntoBoundingRegion 메서드를 기반으로 하는 사각형 내에서 자식 위치에 사용 되는 해당 HorizontalOptions VerticalOptions 속성 값입니다.This is achieved by invoking the LayoutChildIntoBoundingRegion method, which is used to position a child within a rectangle based on its HorizontalOptions and VerticalOptions property values. 자식의 호출 같습니다 Layout 메서드.This is equivalent to making a call to the child's Layout method.

참고

에 전달 된 사각형을 LayoutChildIntoBoundingRegion 메서드 자식 있을 수 있는 전체 영역을 포함 합니다.Note that the rectangle passed to the LayoutChildIntoBoundingRegion method includes the whole area in which the child can reside.

에 대 한 자세한 내용은 합니다 GetLayoutData 메서드를 참조 하세요 계산 및 데이터 캐싱합니다.For more information about the GetLayoutData method, see Calculating and Caching Data.

InvalidateLayout 메서드 재정의Overriding the InvalidateLayout Method

합니다 InvalidateLayout 재정의 자식이 추가 되거나 하나 또는 레이아웃에서 제거 될 때 호출 되의 WrapLayout 속성 값을 변경, 다음 코드 예제 에서처럼:The InvalidateLayout override is invoked when children are added to or removed from the layout, or when one of the WrapLayout properties changes value, as shown in the following code example:

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

재정의 레이아웃을 무효화 하 고 모든 캐시 된 레이아웃 정보를 삭제 합니다.The override invalidates the layout and discards all the cached layout information.

참고

중지 하는 Layout 클래스를 호출 하는 InvalidateLayout 자식을를 레이아웃에서 제거 또는 추가할 때마다 메서드 재정의 ShouldInvalidateOnChildAdded ShouldInvalidateOnChildRemoved 메서드와 반환 false합니다.To stop the Layout class invoking the InvalidateLayout method whenever a child is added to or removed from a layout, override the ShouldInvalidateOnChildAdded and ShouldInvalidateOnChildRemoved methods, and return false. 자식이 추가 되거나 제거 되 면 레이아웃 클래스 사용자 지정 프로세스를 구현할 수 있습니다.The layout class can then implement a custom process when children are added or removed.

OnChildMeasureInvalidated 메서드 재정의Overriding the OnChildMeasureInvalidated Method

합니다 OnChildMeasureInvalidated 재정의 레이아웃의 자식 중 크기를 변경 하 고 다음 코드 예제에 표시 되 면 호출 됩니다.The OnChildMeasureInvalidated override is invoked when one of the layout's children changes size, and is shown in the following code example:

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

재정의 자식 레이아웃이 무효화 하 고 모든 캐시 된 레이아웃 정보를 삭제 합니다.The override invalidates the child layout, and discards all of the cached layout information.

WrapLayout 사용Consuming the WrapLayout

합니다 WrapLayout 클래스에 배치 하 여 사용할 수는 Page 파생 형식에 다음 XAML 코드 예제에서 설명한 것 처럼:The WrapLayout class can be consumed by placing it on a Page derived type, as demonstrated in the following XAML code example:

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

해당 하는 C# 코드는 다음과 같습니다.The equivalent C# code is shown below:

public class ImageWrapLayoutPageCS : ContentPage
{
  WrapLayout wrapLayout;

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

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

자식에 추가할 수 있습니다는 WrapLayout 필요에 따라 합니다.Children can then be added to the WrapLayout as required. 다음 코드 예와 Image 요소에 추가 되는 WrapLayout:The following code example shows Image elements being added to the 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;
}

때 포함 하는 페이지의 WrapLayout 비동기적으로 원격 사진 목록이 포함 된 JSON 파일에 액세스를 만들고 샘플 응용 프로그램 표시 되는 Image 합니다 에추가하는요소각각에대한사진,WrapLayout.When the page containing the WrapLayout appears, the sample application asynchronously accesses a remote JSON file containing a list of photos, creates an Image element for each photo, and adds it to the WrapLayout. 이 인해 다음 스크린샷에 표시 된 모양:This results in the appearance shown in the following screenshots:

다음 스크린샷에서 표시 된 WrapLayout 가로 방향으로 회전 된 후:The following screenshots show the WrapLayout after it's been rotated to landscape orientation:

각 행의 열 수가 사진 크기, 화면 너비를 픽셀 장치 독립적 단위 수에 따라 달라 집니다.The number of columns in each row depends on the photo size, the screen width, and the number of pixels per device-independent unit. Image 사진, 비동기적으로 로드 하는 요소 및 합니다 WrapLayout 클래스를 자주 호출할 받을 해당 LayoutChildren 각 메서드 Image 요소에는 로드 된 사진에 따라 새 크기를 받습니다.The Image elements asynchronously load the photos, and therefore the WrapLayout class will receive frequent calls to its LayoutChildren method as each Image element receives a new size based on the loaded photo.