Android自定义控件(一)

Android自定义控件有三种


  • 自定义View
  • 自定义ViewGroup
  • 继承重写系统的

Android打造自定义控件,大体的思路有以下5点:


  • 创建自定义属性,在res/values目录下创建attrs.xml文件,声明自定义控件的属性
  • 创建自定义View类,并继承View类,重写自定义View类的三个构造方法
  • 通过TypedArray获得各个自定义的属性,并将paint设置为这些属性的内容
  • 重写onMeasure方法,设置好视图在界面上所显示的大小
  • 重写onDraw方法,通过paint和Canvas渲染出自定义的控件

实操

1、创建自定义属性,在res/values目录下创建attrs.xml文件,声明自定义控件的属性

这里写图片描述

声明自定义属性有两个作用,一方面可以让我们在布局文件中直接使用这些属性,另一方面,在xml中声明属性之后会在R类中生成对应的资源ID,方便到时候TypeArray的使用(关于TypeArray见下文) 每个attr标签表示声明一个属性,name是属性的名字,format是属性的格式,比如string表示这个属性必须为字符串,color表示这个属性必须为颜色类型,dimension表示这个属性必须为像素即dp、sp之类的

2、创建自定义View类,并继承View类,重写自定义View类的三个构造方法

这里写图片描述

可以看到三个构造方法的区别在于参数 public MyTextView(Context context) 【通过传入上下文对象来创建view】 public MyTextView(Context context, AttributeSet attrs) 【AttributeSet类型是用来获得我们声明的各个属性,当我们在xml布局文件中声明自定义View时,就会调用此构造方法】 public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) 【多了一个defStyleAttr属性,当我们需要为view设定style时才会用到】 另外,我们对前两个构造方法都调用了this(),学过java的都知道,this表示调用本类的构造方法,在第一个构造方法中,我们调用了 this(context,null);其实是调用了第二个构造方法,在第二个构造方法中调用了this(context,attrs,0);其实是调用了第三个构造方法,所以这样写的好处是无论我们使用哪个构造方法,最终都会进到第三个构造方法,所以接下来我们要做的便是在第三个构造方法中获得我们的属性。

3、通过TypeArray获得各个自定义属性,并将Paint设置为这些属性的内容

这里写图片描述

刚才在上文中已说过,此构造方法的AttributeSet参数可以得到我们在attr中声明的属性,那为什么此处还要通过TypeArray来获得呢?TypeArray有什么用?其实如果我们是直接通过AttributeSet获得属性的话,还需要解析才能获得我们需要的各个属性的值以及格式,而TypeArray帮我们把这些操作都封装好了,所以通过TypeArray可以很方便的获取到我们的属性。

4、重写onMeasure方法,设置好视图在界面上所显示的大小

这里写图片描述

5、重写onDraw方法,通过paint和canvas渲染出自定义控件

这里写图片描述

我们在这里通过canvas调用了两次drawCircle,即绘制了两个圆形,注意到第二个圆形的半径为getMeasuredWidth()/3,比第一个圆的半径小了,而圆心又与第一个圆一致,所以待会儿绘制出来的效果就是一个小圆叠在一个大圆前面,在每次绘制圆之前都调用了paint设置颜色,是为了两个圆形的颜色不一样。

最后再次设置paint颜色为mytextColor,即我们的文字内容的颜色,然后通过canvas.drawText进行文字的绘制,这里之所以getWidth() / 2 - rect.width() / 2是因为将View的宽度的一半减去文字内容的宽度的一半,得到的就是文字内容的左上角的横坐标(可以自行画图理解),纵坐标也是同理。

至此,我们完成了基本的定义,接下来就是在布局文件中使用它了:

这里写图片描述

xmlns:mytext=”http://schemas.android.com/apk/res/com.example.myview”是命名空间的声明,等下我们声明自定义属性的时候需要用到,这里的路径是http://scheam…..res/+项目包名

运行:
这里写图片描述

运行之后,发现在屏幕成功出现了一个圆形背景中间带有文字内容,这里我们设置的宽和高都是200dp,如果将layout_width和layout_height都设置为wrap_content,会发现它并不像我们平时那样会自动有一个默认大小,而是像fill_parent一样的填充屏幕,很明显这不是我们愿意看到的,这个时候就需要用到onMeasure方法了:

这里写图片描述

其中,有两个关键的方法,getMode和getSize,getSize是获得用户所设定的view的宽高,getMode是获得这个属性的模式,Mode有三种:

1、 MeasureSpec.EXACTLY【设置了明确的值或者是MATCH_PARENT】

2、MeasureSpec.AT_MOST【表示子布局限制在一个最大值内,一般为WARP_CONTENT】

3、MeasureSpec.UNSPECIFIED【表示子布局想要多大就多大,很少使用】

 对模式进行判断,如果模式是EXACTLY,说明用户在布局文件中对宽高设定了具体数值或者match_parent,如果是具体数值,那么就将这个具体数值作为最终宽高,如果是match_parent,就将其填充父布局。如果是AT_MOST,说明用户在布局文件中设定为了wrap_content,那么就应该将我们的默认宽度或者高度作为最终的宽高
通过以上测量,就能够在设定为wrap_content时依然能够限定在一定的大小中。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值