shuhelohelo’s blog

Xamarin.Forms多めです.

Xamarin.Forms : Sharpnadoでドラッグアンドドロップ可能なリストを作成するメモ

github.com

SharpnadoのGitHubリポジトリにあるサンプル動画でわかるように,このライブラリが提供する機能の一つとしてリスト状に表示した各アイテムをドラッグアンドドロップで位置を入れ替えることができるHorizontalListViewというコントロールがあります.

HorizontalListViewというコントロールですが,名前にHorizontalとついているので水平方向のリスト表示だけなのかと思いますが,縦方向も表示できます.

このメモは,このコントロールを使うための手順です.

Sharpnado.Presentation.Formsをインストールする

NugetでSharpnado.Presentation.Formsをインストールします.

f:id:shuhelohelo:20200425215522p:plain

プラットフォームごとの準備

Sharpnadoを使うために,各プラットフォームのプロジェクトでほんの少し準備があります.

Android

AndroidプロジェクトのMainActivity.csOnCreateメソッド内で,以下のようにSharpnadoInitializer.Initialize()を追加します.

        protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(savedInstanceState);

            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

            SharpnadoInitializer.Initialize();//これ
            
            LoadApplication(new App());
        }

iOS

iOSではAppDelegate.csFinishedLaunchingメソッド内に以下のように追加します.

        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();

            SharpnadoInitializer.Initialize();//これ

            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }

ContentPage内での使い方

まずは名前空間を追加します.

<ContentPage
...
    xmlns:renderedViews="clr-namespace:Sharpnado.Presentation.Forms.RenderedViews;assembly=Sharpnado.Presentation.Forms"
...
>

これでHorizontalListViewをContentPage内で使用する準備ができました.

HorizontalListViewを使う場合の基本的なVisual Treeの構成は以下のとおりです. 構造がわかりやすいように要素名だけにしています

        <renderedViews:HorizontalListView>
            <renderedViews:HorizontalListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>

                        <!-- ここにアイテムの見た目を定義する -->

                    </ViewCell>
                </DataTemplate>
            </renderedViews:HorizontalListView.ItemTemplate>
        </renderedViews:HorizontalListView>

これに様々なプロパティを追加し,アイテムの見た目の定義を入れると以下のようになります.

        <renderedViews:HorizontalListView
            CollectionPadding="10"
            ColumnCount="1"
            EnableDragAndDrop="True"
            ItemHeight="160"
            ItemSpacing="30"
            ItemsSource="{Binding People}"
            ListLayout="Grid"
            VerticalOptions="FillAndExpand">
            <renderedViews:HorizontalListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <Grid>
                            <Frame
                                Margin="10,0"
                                BorderColor="Green"
                                HasShadow="True">
                                <StackLayout Orientation="Horizontal">
                                    <Image
                                        Aspect="AspectFill"
                                        Source="{Binding ImageSrc}"
                                        WidthRequest="100" />
                                    <Label
                                        Margin="5"
                                        FontSize="Large"
                                        Text="{Binding Name}" />
                                </StackLayout>
                            </Frame>
                        </Grid>
                    </ViewCell>
                </DataTemplate>
            </renderedViews:HorizontalListView.ItemTemplate>
        </renderedViews:HorizontalListView>

この状態で実行すると,以下のようにアイテムをドラッグアンドドロップで入れ替えることができます. f:id:shuhelohelo:20200425223320g:plain

HorizontalListViewでデータを表示するために最低限必要なプロパティは以下のとおりです.(ItemsSourceは前提として)

もう一つ前提として,このHorizontalListViewは名前のとおり,デフォルトではHorizontalなリストです.

  • ColumnCount
    • 画面の幅にいくつのアイテムが収まるように表示するかを指定する.なので,少なくとも1以上が指定されていないと,何も表示されないので注意.

以下はColumnCount="2"の場合 f:id:shuhelohelo:20200425224947p:plain

  • ItemHeight
    • 名前のとおり,アイテムの高さを指定します.これを指定しないと,ListLayout="Grid"としたときに表示されません.
  • ListLayout
    • Linear 横1列の表示(デフォルト)
    • Carousel 1画面に1アイテム
    • Grid 格子状に上から下へ.列数はColumnCountで指定.
    • EnableDragAndDrop ドラッグアンドドロップを有効にする

ところで,縦並びの一般的なListViewのような表示をするにはどうしたらよいかというと,ColumnCount="1",ListLayout="Grid"という設定をすれば以下のようにオーソドックスな見た目になります.

f:id:shuhelohelo:20200425231252p:plain

ColumnCount="2",ListLayout="Grid"という設定にすれば,以下のように2列の格子状に表示されます.

もちろん,この状態でドラッグアンドドロップできます.

f:id:shuhelohelo:20200425231925g:plain

ドラッグアンドドロップ中の見た目を変える

ここまでで素晴らしく便利なのですが,ViewCellDraggableViewCellに変更すると,更に便利になります.

このコントロールはアイテムがドラッグアンドドロップ中かを示すIsDragAndDroppingというプロパティを持っているので,それをDataTriggerで拾うことで,ドラッグアンドドロップ中の見た目を変更することができます.

        <renderedViews:HorizontalListView
            ColumnCount="2"
            EnableDragAndDrop="True"
            ItemHeight="160"
            ItemsSource="{Binding People}"
            ListLayout="Grid">
            <renderedViews:HorizontalListView.ItemTemplate>
                <DataTemplate>
                    <renderedViews:DraggableViewCell x:Name="DraggableViewCell" IsDraggable="True">
                        <Grid>
                            <Frame
                                Margin="10,0"
                                BorderColor="Green"
                                HasShadow="True">

                                <Frame.Triggers>
                                    <DataTrigger
                                        Binding="{Binding Source={x:Reference DraggableViewCell}, Path=IsDragAndDropping}"
                                        TargetType="Frame"
                                        Value="True">
                                        <Setter Property="BackgroundColor" Value="LightPink" />
                                        <Setter Property="ScaleX" Value="1.05" />
                                    </DataTrigger>
                                </Frame.Triggers>

                                <StackLayout Orientation="Horizontal">
                                    <Image
                                        Aspect="AspectFill"
                                        Source="{Binding ImageSrc}"
                                        WidthRequest="100" />
                                    <Label
                                        Margin="5"
                                        FontSize="Large"
                                        Text="{Binding Name}" />
                                </StackLayout>
                            </Frame>
                        </Grid>
                    </renderedViews:DraggableViewCell>
                </DataTemplate>
            </renderedViews:HorizontalListView.ItemTemplate>
        </renderedViews:HorizontalListView>

f:id:shuhelohelo:20200425234209g:plain

今回のソースコード

github.com