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

.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が動くかの確認だけだぜ

コメント

このブログの人気の投稿

ImDiskの後継アプリのAIM toolkitを試してみた

ImDiskの開発辞めちゃったらしい というわけで、じゃあ後継ソフトはあるのかと思ったらあった 遅いと言われているけど・・・ とインストールしてみた ImDiskのこの間の結果はこちら 再びRAM DISK(ImDisk)   blog.mazepin-led.com    そして今回のAIM Toolkit なんか、無茶苦茶遅くなってるな 下手するとSSDの方が速いじゃん CPUの使用状態はこんな感じ PIO転送なところは変わって無さそう まあ、遅いからと言ってその速度が体感できるのかというと出来ないんだけどね と思ったところで、なんか設定変えてみたらどうなるのだろう Allocate Memory Dynamicallyというのは、メモリを必要に応じてってことだからと思って以前試したことがあったけど、なんかImDiskのときは不安定だったんだよな  AdvancedのとこにあるUse AWE Physical Memoryというのが良くわからないけど、チェックしてみたら速くなった おお、大分速くなった。なったけど・・・・なんで? これだとImDiskよりちょっと遅いくらいになるのか もしやQuickFormatととかでも変わるのか?と思ったけどそこまでやる気も無かったので放置。速くなるわけないよね。 後は圧縮とか、取り外しメディアとかだし。   最近の性能の良いSSDのおかげで RAM DISKというものの存在意義が薄れちゃったね まあそれでとにかく速度を稼ぎたいぜって人は SoftPerfect RamDiskでも使ってください 昔はPrimo Ramdisk使ってました。当時使ってたけどなかなか良かった。 当時SSD無茶苦茶高かったし。 今はほぼ無用になってしまったが・・・ 未だにスタンダード版は8GBまでなんだな キャッシュ領域にしたいなら大人しく余ってるSSDを使った方がいいんじゃ無いかって気がするけど  おしまい

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の方が速いかもね  

QCC dongle proを買った→Windows11で接続までのみちのり

何があった  買ったけど最初の接続に手間取ったので そのメモ QuestyleのQCC dongle proを買ったのだけど、使っているイヤホンと接続できない なんでー? ペアリングしてるのに 説明書 説明書には外装のQRコードを読むように書いてあるのだけど そもそもpage not found 404になる   Windowsには・・・ ちゃんと  ヘッドホンのところには QCC Dongle Pro って出てるのよね しかし音が出ない つーか、新しいペアリングはどうやるのだ  スマホが必要でした 何だそりゃっていいたくなるがどうやらまず、スマホにQCC dongle Pro接続して専用アプリで ドングルとイヤホンをペアリングする必要があった 何だそりゃー Google PlayでQuestyleで検索かけるか こちら でまずはインストール そしてスマホにドングルを刺して、このアプリ内からイヤホンをペアリング そしてそのドングルをWindows11の空いてるUSB TYPE-Cポートに突き刺す   音が出た! やったね  結局音が良くなった?  どうなんだろうね 正直言ってよくわからん ただ、マイケルジャクソンのThe Jamのオープニングのガラスの割れる音は 今までよりも細かくパリパリ聞こえるようになったから効果はあったんだと思う それと遅延が少なくなった  finalのZE3000 SVというイヤホンを使っていたのだけど今までのただのUSBドングルで YouTubeを見てると明らかに音と画面のタイミングがずれてて、 Bluetoothイヤホンってこんなもんかと表他のだけどそれが亡くなったのはとても快適 LDACは遅延が酷いとか書いてあったからビビってたんだけどね(でも有線イヤホン使うと、あ、これが合ってる状態なんだってなるので遅延はあるみたい)   あと、たまにイヤホンと繋がらなくなるねぇ 相性問題というやつなのだろうか    今までのUSBドングルが捨てられない 最期に罠が一つ そりゃ無いぜって言いたくなるのが今までのUSBドングルが捨てられないこと このQCC dongle pro はWindowsからはイヤホンとして認識されるので他のBluetoo...