Xamarin.Android 自定义 View
来源:互联网 发布:java线程安全集合类 编辑:程序博客网 时间:2024/06/03 21:54
1. 重要步骤
绘图,通过重写OnDraw方法控制View的渲染效果
交互,重写OnTouchEvent方法实现与用户的交互
测量,重写OnMeasure测量控件显示位置
属性,attrs.xml中自定义控件的属性,通过TypedArray读取属性
保存状态,避免配置改变时丢失View的状态,重写OnSaveInstanceState和OnRestoreInstanceState方法保存、恢复状态
接下来通过一个例子详细的介绍一下如何自定义View,实现一个图片➕文字说明的控件
2. 构造方法
class TitleImageView:View{ public TitleImageView (Context context) : this (context, null) { } public TitleImageView (Context context, IAttributeSet attrs) : this (context, attrs, 0) { } public TitleImageView (Context context, IAttributeSet attrs, int defStyle) : base (context, attrs, defStyle) { }}
3.通过 Xml 自定义 View 属性
在Resources/values
下新建attrs.xml
文件,在attrs
中定义属性和声明样式。确定我们能在xml中定义的属性,然后写如下定义:
<?xml version="1.0" encoding="utf-8"?> <resources> <attr name="titleText" format="string" /> <attr name="titleTextSize" format="dimension" /> <attr name="titleTextColor" format="color" /> <attr name="image" format="reference" /> <attr name="imageScaleType"> <enum name="fillXY" value="0" /> <enum name="center" value="1" /> </attr> <declare-styleable name="TitleImageView"> <attr name="titleText" /> <attr name="titleTextSize" /> <attr name="titleTextColor" /> <attr name="image" /> <attr name="imageScaleType" /> </declare-styleable> </resources>
另外一种写法:
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="TitleImageView"> <attr name="titleText" format="string" /> <attr name="titleTextSize" format="dimension" /> <attr name="titleTextColor" format="color" /> <attr name="image" format="reference" /> <attr name="imageScaleType"> <enum name="fillXY" value="0" /> <enum name="center" value="1" /> </attr> </declare-styleable></resources>
定义了自定义属性,我们就可以在xml中进行使用,不同的是我们自定义属性的命名空间是不同的,我们需要在布局的根节点或自定义View中加上定义命名空间才能使用自定义属性
xmlns:app="http://schemas.android.com/apk/res-auto"
format是指该属性的取值类型:string,color,demension,integer,enum,reference,float,boolean,fraction,flag
具体介绍参考:http://www.jb51.net/article/40069.htm
4.借助TypedArray类提取我们定义的属性,编写类属性可以通过代码设置View属性
class TitleImageView:View{ private Bitmap image; private ImageScale imageScaleType; private string titleText; private Color titleTextColor; private int titleTextSize; public Bitmap Image { get { return image; } set { image = value; Invalidate (); RequestLayout (); } } public ImageScale ImageScaleType { get { return imageScaleType; } set { imageScaleType = value; Invalidate (); } } public string TitleText { get{ return titleText; } set{ titleText = value; Invalidate (); RequestLayout (); } } public Color TitleTextColor { get { return titleTextColor; } set { titleTextColor = value; Invalidate (); } } public int TitleTextSize { get{ return titleTextSize; } set{ titleTextSize = value; Invalidate (); RequestLayout (); } } public enum ImageScale { FillXY, Center } public TitleImageView (Context context) : this (context, null) { } public TitleImageView (Context context, IAttributeSet attrs) : this (context, attrs, 0) { } public TitleImageView (Context context, IAttributeSet attrs, int defStyle) : base (context, attrs, defStyle) { TypedArray typedArray = Context.Theme.ObtainStyledAttributes (attrs, Resource.Styleable.TitleImageView, defStyle, 0); int count = typedArray.IndexCount; try { for (int i = 0; i < count; i++) { int index = typedArray.GetIndex (i); switch (index) { case Resource.Styleable.TitleImageView_image: image = BitmapFactory.DecodeResource (Resources, typedArray.GetResourceId (index, 0)); break; case Resource.Styleable.TitleImageView_imageScaleType: imageScaleType = (ImageScale)typedArray.GetInt (index, 0); break; case Resource.Styleable.TitleImageView_titleText: titleText = typedArray.GetString (index); break; case Resource.Styleable.TitleImageView_titleTextColor: titleTextColor = typedArray.GetColor (index, Color.Black); break; case Resource.Styleable.TitleImageView_titleTextSize: //获取尺寸三个方法的介绍:http://my.oschina.net/ldhy/blog/496420 titleTextSize = typedArray.GetDimensionPixelSize (index, (int)TypedValue.ApplyDimension (ComplexUnitType.Sp, 16, Resources.DisplayMetrics)); break; default: break; } } } catch (System.Exception ex) { throw ex; } finally { typedArray.Recycle (); } }}
代码中View的属性发生改变时我们需要进行重绘和重新布局。所以在属性赋值时调用了Invalidate
(重新绘制OnDraw
)和RequestLayout
(重新布局OnLayout
)方法.
5. 计算视图宽高
重写OnMeasure
方法,按照用户定义的宽度高度进行绘制,View
会先做一次测量,计算出自己占用多大的面积
protected override void OnMeasure (int widthMeasureSpec, int heightMeasureSpec){ base.OnMeasure (widthMeasureSpec, heightMeasureSpec); //计算宽度 以图片宽度作控件宽度 int minWidth = PaddingLeft + PaddingRight + image.Width; var width = ResolveSizeAndState (minWidth, widthMeasureSpec, 0); //计算高度 int minHeight = PaddingBottom + PaddingTop + image.Height + textBound.Height (); var height = ResolveSizeAndState (minHeight, heightMeasureSpec, 0); // 测量完成后必须调用setMeasuredDimension方法 SetMeasuredDimension (width, height);}
ResolveSizeAndState
方法返回一个合适的尺寸,只要将测量模式和我们计算的宽度高度传进去即可,该方法在新的api中才有,无法兼容3.0以下,我们可以根据源码定义自己的ResolveSizeAndState
方法:
private int ResolveSizeAndState(int size, int measureSpec, int childMeasuredState) { int result = size; int specMode = MeasureSpec.GetMode(measureSpec); int specSize = MeasureSpec.GetSize(measureSpec); switch (specMode) { case MeasureSpecMode.Unspecified: result = size; break; case MeasureSpecMode.AtMost: if (specSize < size) { result = specSize | View.MeasuredStateTooSmall; } else { result = size; } break; case MeasureSpecMode.Exactly: result = specSize; break; } return result | (childMeasuredState&View.MeasuredStateMask);}
6. 初始化画笔
在构造函数中初始化一个Paint和两个Rect
rect = new Rect (); //图片位置paint = new Paint ();paint.TextSize = TitleTextSize;paint.Color = titleTextColor;textBound = new Rect ();//底部说明文字位置 // 计算了描绘字体需要的范围 paint.GetTextBounds (titleText, 0, titleText.Length, textBound);
7. OnDraw
重写OnDraw方法,根据定义的属性绘制图形。在参数canvas上绘制我们希望的View样式
protected override void OnDraw (Canvas canvas){ base.OnDraw (canvas); rect.Left = PaddingLeft; rect.Right = Width - PaddingRight; rect.Top = PaddingTop; rect.Bottom = Height - PaddingBottom; paint.TextSize = TitleTextSize; paint.Color = titleTextColor; paint.SetStyle (Paint.Style.Fill); //当前设置的宽度小于字体需要的宽度,将字体改为xxx... if (textBound.Width () > Width) { TextPaint paint = new TextPaint (this.paint); string msg = TextUtils.Ellipsize (titleText, paint, (float)Width - PaddingLeft - PaddingRight, TextUtils.TruncateAt.End); canvas.DrawText (msg, PaddingLeft, Height - PaddingBottom, paint); } else { canvas.DrawText (titleText, Width / 2 - textBound.Width () / 2, Height - PaddingBottom, paint); } //取消使用掉的部分 rect.Bottom -= textBound.Height (); if (imageScaleType == ImageScale.FillXY) { canvas.DrawBitmap (image, null, rect, paint); } else { rect.Left = Width / 2 - image.Width / 2; rect.Right = Width / 2 + image.Width / 2; rect.Top = (Height - textBound.Height ()) / 2 - image.Height / 2; rect.Bottom = (Height - textBound.Height ()) / 2 + image.Height / 2; canvas.DrawBitmap (image, null, rect, paint); }}
8. 使用
<view.TitleImageView xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="100dp" android:layout_height="200dp" android:layout_margin="10dp" android:padding="10dp" myue:image="@mipmap/icon" myue:imageScaleType="fillXY" myue:titleText="hello andorid ! " myue:titleTextColor="#ff0000" myue:titleTextSize="30sp" />
参考链接:http://www.xamarin.xyz/2016/05/26/custom-view/
- Xamarin.Android 自定义 View
- Xamarin Android自定义文本框
- xamarin android自定义spinner
- xamarin android checkbox自定义样式
- Xamarin.Android 自定义VideoView 进度条
- Android View---自定义View
- Android View---自定义View
- xamarin android下SearchView自定义样式
- xamarin android自定义标题栏(自定义属性、回调事件)
- Android 自定义View 之 自定义View属性
- 【自定义View系列】android自定义View概述
- Android自定义view自定义属性
- Android自定义控件 -- 自定义View
- android自定义view(自定义数字键盘)
- Android自定义View-自定义属性
- Android自定义View-自定义属性
- Android 自定义View
- Android 自定义 View
- Mac React Native环境搭建,一路下来踩过的坑
- lintcode(81)数据流中位数
- Java虚拟机初步解析
- 单片机C语言实现数码管控制
- android--获取系统时间的几种方式
- Xamarin.Android 自定义 View
- Ice基本概念及术语
- 遍历-算法的万能钥匙
- 在CFile中读取里面的内容
- 数据结构--向量
- 致ERP行业的同行们——谈一谈我们逝去已久的梦想
- 图像预处理
- 时间类型转换
- bootstrap框架学习(keep update)