Windows Phone 7.1 簡単なメーター表示


MSC2011 D1-401セッションのセンサー系フォロー最終投稿です。ここではデモでお見せしたコンパス(Visual系)を紹介します。

コードは、http://mangosensorchecker.codeplex.com/SourceControl/list/changesets のPageCompassVisual.xamlと、PageCompassVisual.xaml.csを見てください。

図のコンパスみたいな絵がCompassセンサーのTrueHeadingの値に従って回転します。

ファイル名は、Compass.pngとしておきます。

この画像はPowerPointを使ってちょろっと作って、PNGファイルとして保存したものです。これをWindows Phone 7 Silverlightアプリケーションプロジェクトのフォルダーにコピーして、ソリューションエクスプローラーで既存のファイルの追加を使って、プロジェクトに追加します。

そして、この画像を、以下のXAMLコードでPageに貼り付けます。

            <Canvas Grid.Row=”1″ Width=”400″ Height=”400″>
                <Image x:Name=”imageCompass” Canvas.Left=”20″ Canvas.Top=”20″ Source=”Compass.png”>
                    <Image.RenderTransform>
                        <TransformGroup>
                            <RotateTransform x:Name=”compassRotation” CenterX=”180″ CenterY=”180″ Angle=”0″/>
                        </TransformGroup>
                    </Image.RenderTransform>
                </Image>
            </Canvas>

太文字で示した部分が、Imageをはめ込む為の定義。図を描くためのキャンバスを用意して、図を置く左と上の位置を指定して、ソースファイルを指定すればOK。
そして、後でこの図を回転するために、赤字で示したRotateTransformを定義しておきます。図は360×360ピクセルで作ってあるので、CenterX/CenterYをその中心に指定してAngleの値を変えていけばこの図画回転するという寸法。

コードビハインド(PageCompassVisual.xaml.cs)側では、センサーの使い方基礎で説明したようにCompassクラスのインスタンス(名前はmySensorとしておきます)を使うために必要なコードを書いておいて、CurrentValueChangedイベントのハンドラーの中で、

            Deployment.Current.Dispatcher.BeginInvoke(delegate()
            {
                double trueHeading = e.SensorReading.TrueHeading;
                tbTrueHeading.Text = String.Format(“{0:0.000}”, trueHeading);
                compassRotation.Angle = trueHeading;
            });

XAMLで用意しておいたRotateTransform要素(名前はcompassRotation)のAngleに値をぶち込めば、図のNは計測された真の北極の方向に合わせて、図が回転します。

以上、これだけで回転しちゃうのですが、これだと、Compassセンサーの値が変わるたびにカクカク動いてしまいあまり格好良くないんです。

で、単に値を更新するのではなく、BeginInvokeブロックの中身を、以下のようなコードにすると、

                double durationUnit = 500;
                Duration duration = new Duration(TimeSpan.FromMilliseconds(durationUnit));

                double trueHeading = e.SensorReading.TrueHeading;

                tbTrueHeading.Text = String.Format(“{0:0.000}”, trueHeading);

                DoubleAnimation animation = new DoubleAnimation();
                animation.To = trueHeading;
                animation.Duration = duration;

                Storyboard sb = new Storyboard();
                sb.Duration = duration;
                sb.Children.Add(animation);
                Storyboard.SetTarget(animation, compassRotation);
                Storyboard.SetTargetProperty(animation, new PropertyPath(RotateTransform.AngleProperty));

                sb.Begin();

前の値から今の値の角度にアニメーションで、スルッと動くようになります。DoubleAnimationで新しい角度とその角度に至る時間(500ms)を指定し、StoryBoardにDoubleAnimationを追加し、図の回転角度を表すcompassRotationの角度をターゲットとして指定し、Begin()メソッドをコールしてアニメーションを起動します。
本格的にしたいなら、実際の磁石を使ったコンパスが、少しずつ動き出して加速して新しい方向付近になると減速してちょっと行き過ぎて戻って止まる、のようなアニメーションも工夫をすれば可能です。
しかし実は一つだけ問題があります。例えば

  • 前のTrueHeadingの値が360度よりちょっと小さくて、新しい値が0度付近
  • 前のTrueHeadingの値が0付近で、新しい値が360度よりちょっと小さい

では、直感的には前者はN極が右にちょっとだけ動き、後者は左にちょっとだけ動くのが自然に思えるのに、このアニメーションでは、ぐるっと大きく逆の方向に動いてしまいます。
色々悩んで時間切れで対策が思い浮かびません(笑)。Good Idea募集中です。

 デモアプリでは、MotionのAttitudeのYaw、Pitch、Rollも同じような方法でメータ表示をしています。そのソースコードは、PageMotionVisual.xaml/.cs を参照してください。2011/10/13現在で公開されているコードはアニメーションなしのものが公開されています。

Comments (0)

Skip to main content