使用 FlexLayout 配合 BindableLayout 建立一個動態產生與自動配置的效果
建立一個 使使用 FlexLayout 專案
- 開啟 Visual Studio 2019 程式
- 當 Visual Studio 2019 開始 視窗 出現之後,請點選左下角的 [建立新專案] 選項
- 當 [建立新專案] 對話窗出現之後,請在中間最上方的搜尋文字輸入盒中輸入 [prism] 關鍵字,搜尋所有與 Prism 有關的專案樣板
- 請選擇 [Prism Blank App (Xamarin.Forms)] 這個專案樣板
- 當出現 [設定新的專案] 對話窗,請在 [專案名稱] 輸入 [DynamicFlexLayout]
- 最後點選該對話窗右下方的 [建立] 按鈕
- 現在將會看到 [PRISM PROJECT WIZARD] 對話窗,請勾選 ANDROID, iOS, UWP 三個行動裝置平台,接著在底下 [Container] 下拉選單,選擇 Unity 項目
- 最後,點選 [CREATE PROJECT] 按鈕,以便產生 Xamarin.Forms 專案
安裝需要用到的 PropertyChanged.Fody NuGet 套件
- 當這個 Xamarin.Forms 專案建立成功之後,請在該方案中,找到 Xamarin.Forms 使用的專案(這是一個 .NET Standard 類別庫,簡稱為 SCL ),請在該專案中,使用滑鼠右擊 [相依性] 節點,選擇 [管理 NuGet 套件] 選項
- 在 [NuGet: XXX] 視窗中,點選 [瀏覽] 標籤頁次,並且在下方的搜尋文字輸入盒中,輸入 [propertychanged.fody] 關鍵字,搜尋出這個 NuGet 套件
- 當出現 [PropertyChanged.Fody] NuGet 套件,請點選該套件,並且點選右方的 [安裝] 按鈕,將這個套件安裝到 Xamarin.Forms 專案內
- 請查看 Xamarin.Forms 專案內,並沒有 [FodyWeavers.xml] 這個檔案,因此,使用滑鼠右擊 Xamarin.Forms 專案節點,選擇 [建置] 選項
- 當建置完成之後,在這個 Xamarin.Forms 專案內將會出現 [FodyWeavers.xml] 檔案
安裝需要用到的 Xamarin.Essentials NuGet 套件
- 在 [NuGet: XXX] 視窗中,搜尋文字輸入盒中,輸入 [Xamarin.Essentials] 關鍵字,搜尋出這個 NuGet 套件
- 當出現 [Xamarin.Essentials] NuGet 套件,請點選該套件,並且點選右方的 [安裝] 按鈕,將這個套件安裝到 Xamarin.Forms 專案內
修正因為安裝 Xamarin.Essentials 帶來的錯誤
NU1107 偵測到 Xamarin.Android.Support.Compat 有版本衝突。請將 Xamarin.Android.Support.Compat 28.0.0.1 直接安裝/參考到專案 DynamicFlexLayout.Android 來解決此問題。
DynamicFlexLayout.Android -> DynamicFlexLayout -> Xamarin.Essentials 1.1.0 -> Xamarin.Android.Support.Compat (>= 28.0.0.1)
DynamicFlexLayout.Android -> Xamarin.Android.Support.Design 27.0.2.1 -> Xamarin.Android.Support.Compat (= 27.0.2.1). DynamicFlexLayout.Android D:\Vulcan\GitHub\Xamarin2019\DynamicFlexLayout\DynamicFlexLayout\DynamicFlexLayout.Android\DynamicFlexLayout.Android.csproj 1
- 使用滑鼠右擊方案節點(方案總管最上方的那個節點),選擇 [管理方案的 NuGet 套件]
- 點選 [更新] 標籤頁次
- 勾選該標籤頁次內的所有項目
-
建立資料模型
- 滑鼠右擊 Xamarin.Forms 專案,選擇 [加入] > [新增資料夾]
- 將新增資料夾的名稱設定為 [Models]
- 滑鼠右擊剛剛建立的 [Models] 資料夾,選擇 [加入] > [類別]
- 在 [新增項目] 對話窗下方的 [名稱] 欄位中,輸入 [ItemBlock]
- 點選右下方的 [新增] 按鈕
- 將底下程式碼填入到這個新建立的類別檔案內
public class ItemBlock
{
public double Width { get; set; }
public double Height { get; set; }
public Color Color { get; set; }
public bool ShowLabel { get; set; }
public bool ShowBoxView { get; set; } = true;
}
建立支援方法類別
- 滑鼠右擊 Xamarin.Forms 專案,選擇 [加入] > [新增資料夾]
- 將新增資料夾的名稱設定為 [Helpers]
- 滑鼠右擊剛剛建立的 [Helpers] 資料夾,選擇 [加入] > [類別]
- 在 [新增項目] 對話窗下方的 [名稱] 欄位中,輸入 [ScreenInfo]
- 點選右下方的 [新增] 按鈕
- 將底下程式碼填入到這個新建立的類別檔案內
public class ScreenInfo
{
public static double ScreenPixelWidth { get; set; }
public static double ScreenPixelHeight { get; set; }
public static double DesignScreenWidth { get; set; }
public static double DesignScreenHeight { get; set; }
public static double DesignTimeScreenWidth { get; set; } = 360;
public static double DesignScalar { get; set; }
public static double Density { get; set; }
public static double GetNewDesingSize(double value)
{
return DesignScalar * value;
}
}
建立一個啟動頁面
- 在 SCL 專案中,滑鼠右擊 [Views] 這個節點,選擇 [加入] > [新增項目]
- 在 [新增項目] 對話窗的左方,分別點選 [已安裝] > [Visual C# 項目] > [Prism] > [Xamarin.Forms]
- 在 [新增項目] 對話窗的中間,點選 [Prism ContentPage (Xamarin.Forms)] 這個項目
- 在 [新增項目] 對話窗的最下方的名稱欄位,輸入 [SplashPage]
- 最後點選 [新增] 按鈕,完成建立這個新頁面 View 與 檢視模型 ViewModel
- 在 [ViewModels] 資料夾內,找到 [SplashPageViewModel.cs] 節點,並開啟這個節點
- 使用底下程式碼覆寫這個這個檔案內容
using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
namespace DynamicFlexLayout.ViewModels
{
using System.ComponentModel;
using DynamicFlexLayout.Helpers;
using Prism.Events;
using Prism.Navigation;
using Prism.Services;
using Xamarin.Essentials;
public class SplashPageViewModel : INotifyPropertyChanged, INavigationAware
{
public event PropertyChangedEventHandler PropertyChanged;
private readonly INavigationService navigationService;
public SplashPageViewModel(INavigationService navigationService)
{
this.navigationService = navigationService;
}
public void OnNavigatedFrom(INavigationParameters parameters)
{
}
public void OnNavigatedTo(INavigationParameters parameters)
{
var mainDisplayInfo = DeviceDisplay.MainDisplayInfo;
ScreenInfo.Density = mainDisplayInfo.Density;
ScreenInfo.ScreenPixelWidth = mainDisplayInfo.Width;
ScreenInfo.ScreenPixelHeight = mainDisplayInfo.Height;
ScreenInfo.DesignScreenWidth = ScreenInfo.ScreenPixelWidth/ScreenInfo.Density;
ScreenInfo.DesignScreenHeight = ScreenInfo.ScreenPixelHeight / ScreenInfo.Density;
ScreenInfo.DesignScalar = ScreenInfo.DesignScreenWidth / ScreenInfo.DesignTimeScreenWidth;
navigationService.NavigateAsync("/NavigationPage/MainPage");
}
public void OnNavigatingTo(INavigationParameters parameters)
{
}
}
}
- 打開 [App.xaml.cs] 檔案
- 將第 26 行修正為
await NavigationService.NavigateAsync("SplashPage");
建立使用 FlexLayout 的頁面與商業邏輯
- 在 [Views] 資料夾內,打開 [MainPage.xaml] 檔案
- 修正使用底下的 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"
x:Class="DynamicFlexLayout.Views.MainPage"
Title="FlexLayout 的動態調適">
<Grid>
<ScrollView>
<Grid
HorizontalOptions="Center">
<FlexLayout
Wrap="Wrap"
Direction="Row"
AlignItems="Start"
AlignContent="Start"
JustifyContent="Start"
BindableLayout.ItemsSource="{Binding myItemList}"
>
<BindableLayout.ItemTemplate>
<DataTemplate>
<Grid
WidthRequest="{Binding Width}" HeightRequest="{Binding Height}">
<Label
Text="Logo"
FontSize="30"
HorizontalTextAlignment="Center"
WidthRequest="{Binding Width}" HeightRequest="{Binding Height}"
IsVisible="{Binding ShowLabel}"/>
<BoxView
Color="{Binding Color}"
WidthRequest="{Binding Width}" HeightRequest="{Binding Height}"
IsVisible="{Binding ShowBoxView}"/>
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</FlexLayout>
</Grid>
</ScrollView>
</Grid>
</ContentPage>
- 在 [ViewModels] 資料夾內,打開 [MainPageViewModel.xaml] 檔案
- 修正使用底下的 C# 程式碼
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DynamicFlexLayout.ViewModels
{
using System.Collections.ObjectModel;
using System.ComponentModel;
using DynamicFlexLayout.Helpers;
using DynamicFlexLayout.Models;
using Prism.Events;
using Prism.Navigation;
using Prism.Services;
using Xamarin.Forms;
public class MainPageViewModel : INotifyPropertyChanged, INavigationAware
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<ItemBlock> myItemList { get; set; } = new ObservableCollection<ItemBlock>();
private readonly INavigationService navigationService;
public MainPageViewModel(INavigationService navigationService)
{
this.navigationService = navigationService;
}
public void OnNavigatedFrom(INavigationParameters parameters)
{
}
public void OnNavigatedTo(INavigationParameters parameters)
{
Random rnd = new Random();
ItemBlock fooItem;
for (int i = 0; i < 3; i++)
{
fooItem = new ItemBlock()
{
Width = ScreenInfo.GetNewDesingSize(100),
Height = ScreenInfo.GetNewDesingSize(100),
Color = Color.FromRgba(rnd.Next(256), rnd.Next(256), rnd.Next(256), rnd.Next(256)),
ShowLabel = false,
};
myItemList.Add(fooItem);
}
fooItem = new ItemBlock()
{
Width = ScreenInfo.GetNewDesingSize(300),
Height = ScreenInfo.GetNewDesingSize(50),
Color = Color.FromRgba(rnd.Next(256), rnd.Next(256), rnd.Next(256), rnd.Next(256)),
ShowLabel = true,
ShowBoxView = false
};
myItemList.Add(fooItem);
fooItem = new ItemBlock()
{
Width = ScreenInfo.GetNewDesingSize(301),
Height = ScreenInfo.GetNewDesingSize(200),
Color = Color.FromRgba(rnd.Next(256), rnd.Next(256), rnd.Next(256), rnd.Next(256)),
ShowLabel = false,
};
myItemList.Add(fooItem);
for (int i = 0; i < 31; i++)
{
fooItem = new ItemBlock()
{
Width = ScreenInfo.GetNewDesingSize(100),
Height = ScreenInfo.GetNewDesingSize(100),
Color = Color.FromRgba(rnd.Next(256), rnd.Next(256), rnd.Next(256), rnd.Next(256)),
ShowLabel = false,
};
myItemList.Add(fooItem);
}
}
public void OnNavigatingTo(INavigationParameters parameters)
{
}
}
}
執行結果