none
Xamarin(Android)でグラフィクスを再描画する方法を教えてください RRS feed

  • 質問

  • XamarinネイティブでAndroidのアプリを作成しようとしています。一度グラフィクスを描画した後、その上に上書きで再度グラフィクスを描画したいのですが、その方法がわかりません。

    下記2項のようなプログラムを作成したのですが、実行すると、まずボタンの下に「グラフィクステスト」の文字が表示されるのですが、「再描画」ボタンをタップしても、画面は何も変化しません。「再描画」ボタンをタップしたら黄色の四角形が描画されるようにしたいのですが、それができません。

    MainActivity.csの中のroot.AddView(new MyView1(this));の1行をコメントアウトすると、「再描画」ボタンをタップすると黄色い四角形が描画されます。従って、ボタンのイベントハンドラは機能していて、黄色い四角形を描画するプログラムも機能していることになります。一度グラフィクスを描画した後に、ボタンをタップして再描画しようとすると、うまくいかないのです。

    再描画するプログラム(イベントハンドラ内のプログラム、及びMyView2.csのプログラム)のどこかに問題があると思うのですが、どこが悪いかわかりません。

    グラフィクスを再描画する方法について、ご教示いただきたくよろしくお願いします。

    当方はXamarinAndroidC#のいずれも初心者ですので、わかりやすく教えていただけるとありがたいです。

    1.当方の環境

    (1)Windows 10 Pro. 64ビット バージョン1903

    (2)Visual Studio Community 2019 バージョン16.4.2

    2.当方のプログラム

    (1)MainActivity.cs

    using Android.App;

    using Android.Content;  // 追加

    using Android.OS;

    using Android.Views;    // 追加

    using Android.Support.V7.App;

    using Android.Runtime;

    using Android.Widget;

    namespace GraphicsTest

    {

        [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]

        public class MainActivity : /*AppCompat*/ Activity  // 変更

        {

            protected override void OnCreate(Bundle savedInstanceState)

            {

                base.OnCreate(savedInstanceState);

                // Xamarin.Essentials.Platform.Init(this, savedInstanceState);    削除

                // Set our view from the "main" layout resource

                SetContentView(/*Resource.Layout.activity_main*/ makeViews());  // 変更

            }

            //----------------------------------------------------------------ここから追加

            private View makeViews()

            {

                var root = new LinearLayout(this)

                {

                    Orientation = Orientation.Vertical,

                    LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent,

                                                                  ViewGroup.LayoutParams.MatchParent)

                };

                var DrawButton = new Button(this)

                {

                    Text = "再描画",

                    Gravity = GravityFlags.Center,

                    LayoutParameters = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent,

                                                                     ViewGroup.LayoutParams.WrapContent)

                };

                root.AddView(DrawButton);

                root.AddView(new MyView1(this));

                //「再描画」ボタンをクリックした時のイベント処理

                DrawButton.Click += (sender, e) => {

                    //グラフィクスを再表示

                    root.AddView(new MyView2(this));

                };

                return root;

            }

            //----------------------------------------------------------------ここまで追加

            public override void OnRequestPermissionsResult(int requestCode, string[] permissions,

                                      [GeneratedEnum] Android.Content.PM.Permission[] grantResults)

            {

                Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

                base.OnRequestPermissionsResult(requestCode, permissions, grantResults);

            }

        }

    }

    (2)MyView1.cs

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

    using Android.App;

    using Android.Content;

    using Android.OS;

    using Android.Runtime;

    using Android.Views;

    using Android.Widget;

    using Android.Graphics;     // 追加

    namespace GraphicsTest

    {

        class MyView1 : View

        {

            public MyView1(Context context) : base(context) { }

            public override void Draw(Canvas canvas)

            {

                base.Draw(canvas);

                var paint = new Paint

                {

                    Color = Color.Blue,

                    TextSize = 80,

                    AntiAlias = true

                };

                canvas.DrawText("グラフィクステスト", 200, 100, paint);

            }

        }

    }

    (3)MyView2.cs

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

    using Android.App;

    using Android.Content;

    using Android.OS;

    using Android.Runtime;

    using Android.Views;

    using Android.Widget;

    using Android.Graphics;     // 追加

    namespace GraphicsTest

    {

        class MyView2 : View

        {

            public MyView2(Context context) : base(context) { }

            public override void Draw(Canvas canvas)

            {

                base.Draw(canvas);

                var paint = new Paint

                {

                    Color = Color.Yellow,   //White,

                    AntiAlias = true

                };

                //塗りつぶしの四角

                paint.SetStyle(Paint.Style.Fill);

                canvas.DrawRect(new Rect(0, 0, 500, 300), paint);

            }

        }

    }

    (4)activity_main.xmlファイルはデフォルトのままで変更していません。

    以上よろしくお願いいたします。

    2020年1月11日 18:13

回答

  • 再描画の問題ではなく、レイアウトの問題ですね。

    LinearLayoutで並べて配置される状態で、追加したMyView1が大きく配置しているのでMyView2がはみ出てます。
    重ねたい場合はFrameLayoutにします。

    namespace GraphicsTest
    {
        using Android.App;
        using Android.Content;  // 追加
        using Android.OS;
        using Android.Runtime;
        using Android.Views;    // 追加
        using Android.Widget;
        using Android.Support.V7.App;
    
        [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
        public class MainActivity : Android.Support.V7.App.AppCompatActivity
        {
            protected override void OnCreate(Bundle savedInstanceState)
            {
                base.OnCreate(savedInstanceState);
                SetContentView(makeViews());
            }
            private View makeViews()
            {
                var root = new LinearLayout(this) //これは上から下方向または、左から右方向に順番に並べて配置していくレイアウト
                {
                    Orientation = Orientation.Vertical,//上から下方向に
                    LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent,
                                                                ViewGroup.LayoutParams.MatchParent)
                };
    
    
                var DrawButton = new Button(this)
                {
                    Text = "再描画",
                    Gravity = GravityFlags.Center,
                    LayoutParameters = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent,
                                                                     ViewGroup.LayoutParams.WrapContent)
                };
                root.AddView(DrawButton);
    
                bool flag = false;//切り替えて試す
                if (flag)
                {
                    root.AddView(new MyView1(this));//MyView1にLayoutParamsを指定していないのでrootの大きさいっぱいになってしまっている
    
                    DrawButton.Click += (sender, e) =>
                    {
                        var v2 = new MyView2(this);
                        root.AddView(v2);
                    };
                }
                else
                {
                    var sublayout = new FrameLayout(this);//これは重ねて配置されるレイアウト
                    root.AddView(sublayout);//重ねて配置されるレイアウトをLinearLayoutの子に入れる
    
                    sublayout.AddView(new MyView1(this));
    
                    DrawButton.Click += (sender, e) =>
                    {
                        if (sublayout.ChildCount < 2)
                        {
                            var v2 = new MyView2(this);
                            sublayout.AddView(v2);
                        }
                        else
                        {//クリック2回目以降は色を変えてみる
                            byte[] rgb = new byte[3];
                            new System.Random().NextBytes(rgb);
                            MyView2 v2 = sublayout.GetChildAt(1) as MyView2;
                            v2.FillColor = new Android.Graphics.Color(rgb[0], rgb[1], rgb[2]);
                            v2.Invalidate();//Drawが呼ばれるように再描画指示
                        }
                    };
    
    
                }
                return root;
            }
    
            public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
            {
                Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
                base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            }
        }
    }
    
    namespace GraphicsTest
    {
        using Android.Content;
        using Android.Graphics;
        using Android.Views;
        class MyView1 : View
        {
            public MyView1(Context context) : base(context)
            {
            }
    
            protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
            {
                //これでこのViewの大きさを決定することもできる
                var h = MeasureSpec.MakeMeasureSpec(100, MeasureSpecMode.Exactly);//といあえず100固定にしてみる。
                base.OnMeasure(widthMeasureSpec, h);
            }
    
            public override void Draw(Canvas canvas)
            {
                base.Draw(canvas);
    
                //このViewが配置されている大きさをわかりやすくするために塗りつぶし
                var w = canvas.Width;
                var h = canvas.Height;
                canvas.DrawRect(new Rect(0, 0, w, h), new Paint() { Color = Color.LightPink });
    
                var paint = new Paint
                {
                    Color = Color.Blue,
                    TextSize = 80,
                    AntiAlias = true
                };
    
                string text = "グラフィクステスト";
                Rect rec = new Rect();
                paint.GetTextBounds(text, 0, text.Length, rec);
                var textHeight = rec.Height();
    
                canvas.DrawText(text, 200, textHeight, paint);
            }
        }
    }
    
    namespace GraphicsTest
    {
        using Android.Content;
        using Android.Graphics;
        using Android.Views;
    
        class MyView2 : View
        {
            public MyView2(Context context) : base(context) { }
    
            public Color FillColor { get; set; } = Color.Yellow;
    
            public override void Draw(Canvas canvas)
            {
                base.Draw(canvas);
                var paint = new Paint
                {
                    Color = FillColor,
                    AntiAlias = true
                };
    
                //塗りつぶしの四角
                paint.SetStyle(Paint.Style.Fill);
                canvas.DrawRect(new Rect(0, 0, 500, 300), paint);
    
                canvas.DrawLine(0, 0, 500, 300, new Paint() { Color = Color.Red, StrokeWidth = 2 });
            }
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク fghck856 2020年1月12日 10:41
    2020年1月12日 7:02

すべての返信

  • 再描画の問題ではなく、レイアウトの問題ですね。

    LinearLayoutで並べて配置される状態で、追加したMyView1が大きく配置しているのでMyView2がはみ出てます。
    重ねたい場合はFrameLayoutにします。

    namespace GraphicsTest
    {
        using Android.App;
        using Android.Content;  // 追加
        using Android.OS;
        using Android.Runtime;
        using Android.Views;    // 追加
        using Android.Widget;
        using Android.Support.V7.App;
    
        [Activity(Label = "@string/app_name", Theme = "@style/AppTheme", MainLauncher = true)]
        public class MainActivity : Android.Support.V7.App.AppCompatActivity
        {
            protected override void OnCreate(Bundle savedInstanceState)
            {
                base.OnCreate(savedInstanceState);
                SetContentView(makeViews());
            }
            private View makeViews()
            {
                var root = new LinearLayout(this) //これは上から下方向または、左から右方向に順番に並べて配置していくレイアウト
                {
                    Orientation = Orientation.Vertical,//上から下方向に
                    LayoutParameters = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MatchParent,
                                                                ViewGroup.LayoutParams.MatchParent)
                };
    
    
                var DrawButton = new Button(this)
                {
                    Text = "再描画",
                    Gravity = GravityFlags.Center,
                    LayoutParameters = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent,
                                                                     ViewGroup.LayoutParams.WrapContent)
                };
                root.AddView(DrawButton);
    
                bool flag = false;//切り替えて試す
                if (flag)
                {
                    root.AddView(new MyView1(this));//MyView1にLayoutParamsを指定していないのでrootの大きさいっぱいになってしまっている
    
                    DrawButton.Click += (sender, e) =>
                    {
                        var v2 = new MyView2(this);
                        root.AddView(v2);
                    };
                }
                else
                {
                    var sublayout = new FrameLayout(this);//これは重ねて配置されるレイアウト
                    root.AddView(sublayout);//重ねて配置されるレイアウトをLinearLayoutの子に入れる
    
                    sublayout.AddView(new MyView1(this));
    
                    DrawButton.Click += (sender, e) =>
                    {
                        if (sublayout.ChildCount < 2)
                        {
                            var v2 = new MyView2(this);
                            sublayout.AddView(v2);
                        }
                        else
                        {//クリック2回目以降は色を変えてみる
                            byte[] rgb = new byte[3];
                            new System.Random().NextBytes(rgb);
                            MyView2 v2 = sublayout.GetChildAt(1) as MyView2;
                            v2.FillColor = new Android.Graphics.Color(rgb[0], rgb[1], rgb[2]);
                            v2.Invalidate();//Drawが呼ばれるように再描画指示
                        }
                    };
    
    
                }
                return root;
            }
    
            public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
            {
                Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
                base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            }
        }
    }
    
    namespace GraphicsTest
    {
        using Android.Content;
        using Android.Graphics;
        using Android.Views;
        class MyView1 : View
        {
            public MyView1(Context context) : base(context)
            {
            }
    
            protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
            {
                //これでこのViewの大きさを決定することもできる
                var h = MeasureSpec.MakeMeasureSpec(100, MeasureSpecMode.Exactly);//といあえず100固定にしてみる。
                base.OnMeasure(widthMeasureSpec, h);
            }
    
            public override void Draw(Canvas canvas)
            {
                base.Draw(canvas);
    
                //このViewが配置されている大きさをわかりやすくするために塗りつぶし
                var w = canvas.Width;
                var h = canvas.Height;
                canvas.DrawRect(new Rect(0, 0, w, h), new Paint() { Color = Color.LightPink });
    
                var paint = new Paint
                {
                    Color = Color.Blue,
                    TextSize = 80,
                    AntiAlias = true
                };
    
                string text = "グラフィクステスト";
                Rect rec = new Rect();
                paint.GetTextBounds(text, 0, text.Length, rec);
                var textHeight = rec.Height();
    
                canvas.DrawText(text, 200, textHeight, paint);
            }
        }
    }
    
    namespace GraphicsTest
    {
        using Android.Content;
        using Android.Graphics;
        using Android.Views;
    
        class MyView2 : View
        {
            public MyView2(Context context) : base(context) { }
    
            public Color FillColor { get; set; } = Color.Yellow;
    
            public override void Draw(Canvas canvas)
            {
                base.Draw(canvas);
                var paint = new Paint
                {
                    Color = FillColor,
                    AntiAlias = true
                };
    
                //塗りつぶしの四角
                paint.SetStyle(Paint.Style.Fill);
                canvas.DrawRect(new Rect(0, 0, 500, 300), paint);
    
                canvas.DrawLine(0, 0, 500, 300, new Paint() { Color = Color.Red, StrokeWidth = 2 });
            }
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク fghck856 2020年1月12日 10:41
    2020年1月12日 7:02
  • gekka様

    具体的にプログラムを示していただき、ありがとうございます。ご教示いただいたプログラムで私のやりたいことができました。

    ご教示いただいたプログラムを完全に理解しているわけではありませんが、これから勉強してみたいと思います。

    また「MICROSOFT LIMITED PUBLIC LICENSE」についても、よくわかっているわけではありませんが、ご教示いただいたプログラムや、これをを使用してできたアプリは私個人のスマホにインストールするだけで、人に配ったり、売ったり、また公開したりすることはありません。

    また、失礼ながら、ご教示いただいたプログラムに万一不具合があったとしても、gekka様やマイクロソフト様にその責任を問うことはありません。

    今回は、本当にありがとうございました。

    2020年1月12日 10:41