[Xamarin.Android] 自訂控制項

摘要:[Xamarin.Android] 自訂控制項

[Xamarin.Android] 自訂控制項

前言

軟體專案開發的過程中,免不了遇到一些無法使用內建控制項就能滿足的客戶需求,例如:時速表、折線圖...等等。這時開發人員可以透過自訂控制項的方式,為專案量身打造控制項,來提供更加貼近使用者需求的使用介面。本篇文章介紹在開發Xamarin.Android專案的時候,如何建立自訂控制項,為自己留個紀錄也希望能幫助到有需要的開發人員。

前言01

建立自訂控制項

在Xamarin.Android專案中,有許多種方式可以建立自訂控制項,本篇文章的範例採用繼承View、覆寫OnDraw的方式來實作自訂控制項。

  1. 首先在Xamarin.Android專案中,加入一個類別:「CountMeter」,並且讓CountMeter繼承Android.Views.View以及實作對應Android.Views.View的建構子。

    public sealed class CountMeter : View
    {
        // Constructors
        public CountMeter(Context context) : base(context) { }
    
        public CountMeter(Context context, IAttributeSet attributeSet) : base(context, attributeSet) { }
    
        public CountMeter(Context context, IAttributeSet attributeSet, int defaultStyle) : base(context, attributeSet, defaultStyle) { }        
    
        // ......
    }
    
  2. 接著在CountMeter類別中,覆寫Android.Views.View的OnMeasure方法,讓自訂控制項能夠正確顯示android:layoutwidth、android:layoutheight...等等尺寸設定。

    public sealed class CountMeter : View
    {
        // Fields
        private readonly int _defaultWidth = 400;
    
        private readonly int _defaultHeight = 210;
    
    
        // Methods
        protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
        {
            // Base
            base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
    
            // Size
            this.SetMeasuredDimension(this.MeasureSize(widthMeasureSpec, _defaultWidth), this.MeasureSize(heightMeasureSpec, _defaultHeight));
        }
    
        private int MeasureSize(int measureSpec, int defaultSize)
        {
            // Size
            var specSize = MeasureSpec.GetSize(measureSpec);
    
            // Measure
            switch (MeasureSpec.GetMode(measureSpec))
            {
                case MeasureSpecMode.AtMost: return Math.Min(specSize, defaultSize);
                case MeasureSpecMode.Exactly: return specSize;
                default: return defaultSize;
            }
        }
    
        // ......
    }
    

    建立01

  3. 接著在CountMeter類別中,覆寫Android.Views.View的OnDraw方法,使用程式碼的方式來描繪自訂控制項呈現在介面上的顯示外觀。而針對如何描繪控制項外觀,開發人員可以參考下列資料,學習如何透過Xamarin.Android所提供的繪圖類別來使用圖形描繪功能:「Xamarin>Android>Other UX>Drawing」。

    public sealed class CountMeter : View
    {
        // Fields
        private readonly int _defaultWidth = 400;
    
        private readonly int _defaultHeight = 210;
    
    
        // Methods
        protected override void OnDraw(Canvas canvas)
        {
            // Base
            base.OnDraw(canvas);
    
            // Background
            canvas.DrawColor(Color.White);
    
            // Paint   
            var paint = new Paint();
            paint.Color = Color.Red;
            paint.StrokeWidth = 10;
    
            // Size
            var x = 0;
            var y = 0;
            var width = this.Width;
            var height = this.Height - 10;
            var ellipseWidth = width;
            var ellipseHeight = height * 2;
            var scaleLength = 20;
            var spaceLength = 10;
    
            // Scale   
            paint.Color = Color.Red;
            for (int scaleCount = 0; scaleCount <= 100; scaleCount += 10)
            {
                var scaleAngle = 180f / 100f * scaleCount;
                var scaleOffset = scaleLength;
                var scalePoint1 = this.GetEllipsePoint(x, y, ellipseWidth, ellipseHeight, scaleAngle);
                var scalePoint2 = this.GetEllipsePoint(x + scaleOffset, y + scaleOffset, ellipseWidth - scaleOffset * 2, ellipseHeight - scaleOffset * 2, scaleAngle);
                canvas.DrawLine(scalePoint1.X, scalePoint1.Y, scalePoint2.X, scalePoint2.Y, paint);
            }
        }
    
        // ......
    }
    

    建立02

  4. 自訂控制項除了呈現靜態資料之外,更大的功用是用來呈現動態資料,例如:目前時速、目前溫度、載貨量...等等。要完成呈現動態資料的功能,開發人員必須要在自訂控制項中加入物件屬性、物件方法來提供外部程式輸入動態資料。而控制項內部程式,在更新資料之後,就可以依照資料內容來在畫面上描繪出對應的顯示圖形。

    public sealed class CountMeter : View
    {
        // Fields
        private int _count = 0;
    
    
        // Properties
        public int Count
        {
            get
            {
                // Get
                return _count;
            }
            set
            {
                // Set
                _count = value;
    
                // Refresh
                this.Invalidate();
            }
        }
    
    
        // Methods
        protected override void OnDraw(Canvas canvas)
        {
            // Base
            base.OnDraw(canvas);
    
            // Background
            canvas.DrawColor(Color.White);
    
            // Paint   
            var paint = new Paint();
            paint.Color = Color.Red;
            paint.StrokeWidth = 10;
    
            // Size
            var x = 0;
            var y = 0;
            var width = this.Width;
            var height = this.Height - 10;
            var ellipseWidth = width;
            var ellipseHeight = height * 2;
            var scaleLength = 20;
            var spaceLength = 10;
    
            // Needle
            paint.Color = Color.Gold;
            var needleAngle = 180f / 100f * _count;
            var needleOffset = scaleLength + spaceLength;
            var needlePoint1 = this.GetEllipsePoint(x + needleOffset, y + needleOffset, ellipseWidth - needleOffset * 2, ellipseHeight - needleOffset * 2, needleAngle);
            var needlePoint2 = new PointF(width / 2, height);
            canvas.DrawLine(needlePoint1.X, needlePoint1.Y, needlePoint2.X, needlePoint2.Y, paint);
        }
    
        // ......
    }
    

    建立03

使用自訂控制項

完成建立自訂控制項的開發步驟後,接下來就是將自訂控制項加入到專案之中。在Xamarin.Android專案中,有許多種方式可以將自訂控制項,加入到實際處理使用者介面Activity類別之中,本篇文章的範例採用直接加入axml檔案的方式,在專案中使用自訂控制項。

  • Main.axml

    <CustomControlSample.CountMeter
        android:id="@+id/MyCountMeter1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp" />
    

透過加入axml檔案方式將自訂控制向加入專案之後,在實際處理使用者介面Activity類別中,就可以跟內建控制項一樣透過FindViewById方法來取得控制項,並且操作控制項所提供方法、屬性、事件,來提供更加貼近使用者需求的使用介面。

  • MainActivity.cs

    [Activity(Label = "CustomControlSample", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {
        // Fields
        private int _count = 0;
    
    
        // Methods
        protected override void OnCreate(Bundle bundle)
        {
            // Base
            base.OnCreate(bundle);
    
            // View
            this.SetContentView(Resource.Layout.Main);
    
            // CountMeter
            var countMeter1 = FindViewById<CountMeter>(Resource.Id.MyCountMeter1);
    
            // UpButton
            var upButton = FindViewById<Button>(Resource.Id.UpButton);
            upButton.Click += delegate
            {
                _count += 10;
                countMeter1.Count = _count;
            };
    
            // DownButton
            var downButton = FindViewById<Button>(Resource.Id.DownButton);
            downButton.Click += delegate
            {
                _count -= 10;
                countMeter1.Count = _count;
            };
        }
    }
    

    使用01

範例下載

範例程式碼:點此下載

期許自己
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。