スキップしてメイン コンテンツに移動

.NET MAUI PanGestureRecognizerで出来た

前回の記事で、やりたい事が出来ねーよって諦めてたけど
やり方わかった


PanGestureRecognizerを使う・・・のだけど、AndroidとWindowsで違うコードが必要になるのはなぁ・・・・
 

xamlのソースがこれ↓

  • PanGestureRecognizerをGridに追加(Windows用)
  • PanGestureRecognizerをImageに追加(Android用)
  • Gridは好きなように・・・

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="PanGestureTest.MainPage">
    <Grid x:Name="baseGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <!-- GridのPANはWindows専用(Androidではイベントが上がらない) -->
        <Grid.GestureRecognizers>
            <PanGestureRecognizer PanUpdated="PanGestureRecognizer_PanUpdated" />
        </Grid.GestureRecognizers>

        <Image x:Name="bot"
            Source="dotnet_bot.png" Grid.Column="0" Grid.Row="0" ClassId="image1"
            SemanticProperties.Description="Cute dot net bot waving hi to you!"
            HeightRequest="200"
            HorizontalOptions="Center" >
            <!--    PANジェスチャー    -->
            <Image.GestureRecognizers>
                <PanGestureRecognizer PanUpdated="OnPanImage" />
            </Image.GestureRecognizers>

        </Image>

        <Label  Grid.Column="1" Grid.Row="1" x:Name="label1"
            Text="Hello, World!"
            SemanticProperties.HeadingLevel="Level1"
            FontSize="32"
            HorizontalOptions="Center" >
            <!--    PANジェスチャー    -->
            <Label.GestureRecognizers x:Uid="3">
                <PanGestureRecognizer PanUpdated="OnPanLabel" />
            </Label.GestureRecognizers>
        </Label>

        <Label  Grid.Column="2" Grid.Row="2" ClassId="image2"
            Text="Welcome to .NET Multi-platform App UI"
            SemanticProperties.HeadingLevel="Level2"
            SemanticProperties.Description="Welcome to dot net Multi platform App U I"
            FontSize="18"
            HorizontalOptions="Center" />

        <Button  Grid.Column="3" Grid.Row="3" ClassId="image3"
            x:Name="CounterBtn"
            Text="Click me"
            SemanticProperties.Hint="Counts the number of times you click"
            Clicked="OnCounterClicked"
            HorizontalOptions="Center" />
    </Grid>
</ContentPage>

 

C#のソースは↓

  • changePosition アニメーション移動をした後にGridを入れ替えて固定
  • OnPanImage ImageのPan動作(Android用)
  • PanGestureRecognizer_PanUpdated GridのPan動作(Windows用)

ImageとGridのPan動作は似たような動作だけど 移動距離の計算が

Windows用は

            bot.TranslationX = e.TotalX;
            bot.TranslationY = e.TotalY;

Android用は

            bot.TranslationX += e.TotalX;
            bot.TranslationY += e.TotalY;

と言うように若干異なってます
そこはMAUIで吸収してほしかった
実装時は、ImageやGridのコードは関数化するかクラス化するけど、要素技術の調査だから汚いソースでいいのだ


using System.Diagnostics;

namespace PanGestureTest;

public partial class MainPage : ContentPage
{
    int count = 0;
    bool _IsImage = false;

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public MainPage()
    {
        InitializeComponent();
    }

    /// <summary>
    /// グリッド上の位置を入れ替え
    /// </summary>
    private void changePosition()
    {
        //イメージコントロールの元々あったGrid上の位置
        int botColumn = baseGrid.GetColumn(bot);
        int botRow = baseGrid.GetRow(bot);

        //ラベルコントロールの元々あったGrid上の位置
        int lblColumn = baseGrid.GetColumn(label1);
        int lblRow = baseGrid.GetRow(label1);

        //Grid上の位置を書換(イメージコントロール)
        baseGrid.SetColumn(bot, lblColumn);
        baseGrid.SetRow(bot, lblRow);
        bot.TranslationX = 0;                       //移動量は0に戻しておく
        bot.TranslationY = 0;

        //Grid上の位置を書換(ラベルコントロール)
        baseGrid.SetColumn(label1, botColumn);
        baseGrid.SetRow(label1, botRow);
        label1.TranslationX = 0;                       //移動量は0に戻しておく
        label1.TranslationY = 0;
    }

    private void OnCounterClicked(object sender, EventArgs e)
    {
        count++;

        if (count == 1)
            CounterBtn.Text = $"Clicked {count} time";
        else
            CounterBtn.Text = $"Clicked {count} times";

        SemanticScreenReader.Announce(CounterBtn.Text);

        //元の場所に戻す
        baseGrid.SetColumn(bot, 0);
        baseGrid.SetRow(bot, 0);
        bot.TranslationX = 0;                       //移動量は0に戻しておく
        bot.TranslationY = 0;

        baseGrid.SetColumn(label1, 1);
        baseGrid.SetRow(label1, 1);
        label1.TranslationX = 0;                       //移動量は0に戻しておく
        label1.TranslationY = 0;

    }

    /// <summary>
    /// イメージコントロールのPanGestureRecognizer(Android用)
    /// </summary>
    async private void OnPanImage(object sender, PanUpdatedEventArgs e)
    {
        Debug.WriteLine(e.StatusType.ToString());
        Debug.WriteLine($"image={e.TotalX}");

        _IsImage = true;//イメージでPanが発生した事の判定

        if (DeviceInfo.Current.Platform == DevicePlatform.WinUI)
        {
            //Windowsで入ってきたら捨てる
            return;
        }
        if (e.StatusType == GestureStatus.Completed)
        {
            //移動完了

            //ここで、他のコントロールと場所を入れ替えたい
            //1.どのコントロールの上にかぶったのか
            //2.被さったコントロールがGrid上のcolmとrowのそれぞれ何処の位置にあるのか
            //イメージの位置情報
            var botPosX = bot.X;
            var botPosY = bot.Y;
            var botH = bot.Height;
            var botW = bot.Width;

            //文字ラベルの位置情報
            var lblPosX = label1.X;
            var lblPosY = label1.Y;
            var lblH = label1.Height;
            var lblW = label1.Width;

            //イメージコントロールの終了処理
            var botNewX = botPosX + bot.TranslationX;
            var botNewY = botPosY + bot.TranslationY;

            if ((botNewX > lblPosX - 30 && botNewX < (lblPosX + lblW))
             && (botNewY > lblPosY - 30 && botNewY < (lblPosY + lblH)))
            {
                //botがラベルの上にかぶったので、まずアニメ移動
                await bot.TranslateTo(lblPosX - botPosX, lblPosY - botPosY, 50);
                await label1.TranslateTo(-(lblPosX - botPosX), -(lblPosY - botPosY), 50);

                await Task.Delay(50);   //アニメ移動が終わるまで待機
                changePosition();       //Grid上のポジションを入れ替える
            }
            else
            {
                //ラベルの上に居るので無ければ元の位置に戻す
                await bot.TranslateTo(0, 0, 50);
            }
        }
        else
        {
            if (DeviceInfo.Current.Platform == DevicePlatform.Android)
            {
                bot.TranslationX += e.TotalX;
                bot.TranslationY += e.TotalY;
            }
            else
            {
                bot.TranslationX = e.TotalX;
                bot.TranslationY = e.TotalY;
            }
        }
    }

    /// <summary>
    /// ラベルコントロールのPanGestureRecognizer
    /// </summary>
    private void OnPanLabel(object sender, PanUpdatedEventArgs e)
    {
        _IsImage = false; // ラベルでPanが発生した事の判定
        Debug.WriteLine($"label={e.TotalX}");
    }

    /// <summary>
    /// Gridに貼り付けたPanGestureRecognizer(Windows用)
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    async private void PanGestureRecognizer_PanUpdated(object sender, PanUpdatedEventArgs e)
    {
        if (DeviceInfo.Current.Platform == DevicePlatform.Android)
        {
            //Androidで入ってきたら捨てる
            return;
        }

        if (e.StatusType == GestureStatus.Completed)
        {
            //移動完了

            //ここで、他のコントロールと場所を入れ替えたい
            //1.どのコントロールの上にかぶったのか
            //2.被さったコントロールがGrid上のcolmとrowのそれぞれ何処の位置にあるのか
            //イメージの位置情報
            var botPosX = bot.X;
            var botPosY = bot.Y;
            var botH = bot.Height;
            var botW = bot.Width;

            //文字ラベルの位置情報
            var lblPosX = label1.X;
            var lblPosY = label1.Y;
            var lblH = label1.Height;
            var lblW = label1.Width;

            //イメージコントロールの終了処理
            var botNewX = botPosX + bot.TranslationX;
            var botNewY = botPosY + bot.TranslationY;

            if ((botNewX > lblPosX - 30 && botNewX < (lblPosX + lblW))
             && (botNewY > lblPosY - 30 && botNewY < (lblPosY + lblH)))
            {
                //botがラベルの上にかぶったので、まずアニメ移動
                await bot.TranslateTo(lblPosX - botPosX, lblPosY - botPosY, 50);
                await label1.TranslateTo(-(lblPosX - botPosX), -(lblPosY - botPosY), 50);

                await Task.Delay(50);
                changePosition();
            }
            else
            {
                //ラベルの上に居るので無ければ元の位置に戻す
                await bot.TranslateTo(0, 0, 50);
            }
        }
        else
        {
            bot.TranslationX = e.TotalX;
            bot.TranslationY = e.TotalY;
        }
    }
}

英語版のstack overflow検索してたら、やっぱり悩んでいる人もいて解決策が出ていた
まさか、こんな力任せな解決方法だとは・・・

ちょっと補足 、位置判定のところのif文に突如30なんて数字が出てきてるけど、使い勝手の調整用
本番ではどうすっかね

            if ((botNewX > lblPosX - 30 && botNewX < (lblPosX + lblW))
             && (botNewY > lblPosY - 30 && botNewY < (lblPosY + lblH)))

ここまで来たら後はBLEが動くかの確認だけだぜ

コメント

このブログの人気の投稿

RAM DISKを使ってみた(使ったのはImDisk)

GWだし、まあちょっとラムディスクを入れてみました うちのPCはWindows11 使ったのはImDiskというRAM Disk。 まあ、この辺のインストールとかはあちこちで解説してる人がいるので適当にぐぐってくださいな で、とりあえずベンチマーク なかなかいいスピードだ で、大抵の人はブラウザのキャッシュをRAMディスクにするといいよ・・・と言うけど そもそもメインドライブがNVMeのSSDを使っている状態で、体感速度なんか上がらない(使い終わったキャッシュを再起動したら綺麗さっぱり捨て去ってくれるという利点はある)  うちで一番効果があるのは Adobe Audition というアプリ これが結構高速化する(キャッシュをちゃんとRAMディスクにしたら・・・だけど) ハイレゾ音源だと、1時間の音源が何かする度に4GBのファイルを作られてしまう なので、RAM DISKにすると、結構編集時間を短縮できる Premiere Rushも出力先をRam Diskにしておいて終わったら、SSDにコピーすると言う事をやるとかなりスピードアップになる 実はうちのPCは普段は99%のパワーで動作していて、CPUのターボブーストが掛からないようになっている 大体3.6GHz当たりで安定してるのだけど、これを100%にするとターボブースト機能がONになって一部のコアが4.5とか4.8GHzまで上がる まあ、毎回電源オプションをいじる事になるのだけどさ・・・ そうしてベンチを取ると こんな感じ とは言え、ブーストしてるからと行ってRam Diskのスピードの差を体感する事はさすがに無理 ・・・と言うかフォトショでもRAM DISKにしてよかった・・・と言うほど変わらない SSDの性能が上がってきたしもし次にPCを買い換えたらRAM DISKよりSSDの方が速いかもね  

.NET MAUI BLE(Bluetooth Low Energy)も上手く行った

2023/10/11更新 ↓こちらの記事で更新しれました。 .NET MAUI PLUGIN BLEがWindows(10/11)に対応してた Windows対応 イヤッッホォォォオオォオウ Bluetooth LE plugin for Xamarin &amp; MAUI がWindowsに対応してた~ nugetでver3.00をみんな早速ゲットだ。   ちなみにこちらはAndroid版のサンプルアプリ   GitHub...   [ブログカード風リンクタグ作成] ------------------------------------------------------------------------------ 昔、スマホとESP32の接続確認用に作ったプログラムをXamarinからMAUIに移植したら動いちゃった   まあ、ESP32からスマホへの一方通行なアプリなんだけど 面倒くさいので github に公開した 下手くそなコード書きやがってとか思われそう 「間違ってるぞこのやろう」というのを見つけたら教えてくれると嬉しいです 要素技術の調査はこれで完了かな

.NET MAUIでスプラッシュスクリーン Android12で地獄を見る

まずは起動するところから・・・・  スプラッシュスクリーンだぬ マイクロソフトのサイト 見てると簡単そう(実際簡単で細かい事を気にしなければsvgファイルを用意して1行だけ書き換えておしまい)   なんかプロジェクトファイルに自動で記述されるらしい よし、プロジェクト作った                     つーか、もうスプラッシュがあるんだけど・・・・ そして自作のsvgファイル くうっ・・・デザインセンスない・・・・   それはともかく、このSVGファイルを Resources\Images にドラッグ&ドラッグ プロジェクトを右クリックして「プロジェクトファイルの編集」 して、編集できるようになったプロジェクトファイルを 自分のプロジェクトファイルに書き換える <MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="168,168" /> ↓ <MauiSplashScreen Include="Resources\Splash\ splashtestmaui .svg" Color="#512BD4" BaseSize="168,168" />   とりあえずAndroidで実行 お、おう・・・・ まあ最初はこんなもんよね 最初に紹介したマイクロソフトのサイトでもBaseSizeを書き換えてくださいって言ってるし 言われたとおりに <MauiSplashScreen Include="Resources\Splash\splashtestmaui.svg" Color="#512BD4" BaseSize=" 320,600 " /> 書き換えてみると いい感じじゃーん じゃあアンドロイドのバージョン毎に試してみよう   Android 7 Android 11 Android 12 Android 13