[Xamarin.Android] 自定义控件
[Xamarin.Android] 自定义控件
前言
软件项目开发的过程中,免不了遇到一些无法使用内建控件就能满足的客户需求,例如:时速表、折线图...等等。这时开发人员可以透过自定义控件的方式,为项目量身打造控件,来提供更加贴近用户需求的使用界面。本篇文章介绍在开发Xamarin.Android项目的时候,如何建立自定义控件,为自己留个纪录也希望能帮助到有需要的开发人员。
建立自定义控件
在Xamarin.Android项目中,有许多种方式可以建立自定义控件,本篇文章的范例采用继承View、覆写OnDraw的方式来实作自定义控件。
-
首先在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) { } // ...... }
-
接着在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; } } // ...... }
-
接着在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); } } // ...... }
-
自定义控件除了呈现静态数据之外,更大的功用是用来呈现动态数据,例如:目前时速、目前温度、载货量...等等。要完成呈现动态数据的功能,开发人员必须要在自定义控件中加入对象属性、对象方法来提供外部程序输入动态数据。而控件内部程序,在更新数据之后,就可以依照资料内容来在画面上描绘出对应的显示图形。
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); } // ...... }
使用自定义控件
完成建立自定义控件的开发步骤后,接下来就是将自定义控件加入到项目之中。在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; }; } }
范例下载
范例程序代码:点此下载
期許自己~
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。