Android自定义圆角矩形

本文介绍如何在Android中自定义一个圆角矩形控件,避免大量XML资源文件。通过继承TextView,调整onDraw()方法,设置边框颜色、宽度和圆角大小,以及处理不同宽高比例下的显示。此外,还增加了控制四个圆角大小、形状选择(矩形、椭圆、圆形)以及背景图的设置和展示方式等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android自定义圆角矩形

在开发中,UI动不动就丢过来一个圆角矩形,圆角矩形,一般做法就是写一个shape的xml,设置一下Radius和宽高,使高度等于两倍的圆角半径。当有很多这种宽高及radius不同的圆角矩形时,需要写很多shape的xml,再加上有selector的话感觉很不爽,(或者有更好的办法我不知道?)于是决定还是写个控件吧,省得麻烦。

目录

思路

为了用上文字,简单的继承TextView,在onDraw()方法中根据宽高,求得圆角半径,先画上边框,再画上文字,搞定。

开搞

首先,添加一个边框颜色和宽度的自定义属性,然后在onDraw()方法中:

RectF strokeRect = new RectF(0.5f * stroke_width, 0.5f * stroke_width, getWidth() - 0.5f * stroke_width, getHeight() - 0.5f * stroke_width);
if (stroke_width > 0) {
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(stroke_width);
    mPaint.setColor(stroke_color);
    canvas.drawRoundRect(strokeRect, getHeight()/2, getHeight()/2, mPaint);
}
super.onDraw(canvas);

于是,一个简单的自定义圆角矩形就这么搞定了。

完善

但是,还有些东西要处理:

  • 宽度和高度的关系

    当控件的高度大于宽度时,该怎么显示?注意到drawRoundRect()方法有两个参数是设置x轴和y轴分别的radius,那么,yRadius就设为高度的一半了。而对于xRadius,当宽度大于高度时,属于正常情况,为高度的一半;当宽度小于高度时,设为宽度的一半。

  • 描边线的宽度
    当描边的宽度大于半径时,显然不合理,宽度最大等于半径,同时要大于零。
    于是:
float yRadius = 0.5f * getHeight();
float xRadius = 0.5f * Math.min(getWidth(),getHeight());
stroke_width = Math.min(xRadius , stroke_width);
stroke_width = stroke_width < 0 ? 0 : stroke_width;

增加功能

控制四个圆角大小

在shape的xml里,可以灵活地设置四个角不同的radius,我们当然也要加上,那就加一个属性来控制显示正常的圆角还是自定义圆角半径吧。

<attr name="selfRoundCorner" format="boolean" />
<attr name="radius" format="dimension" />
<attr name="radius_left_top" format="dimension" />
<attr name="radius_right_top" format="dimension" />
<attr name="radius_right_bottom" format="dimension" />
<attr name="radius_left_bottom" format="dimension" />

radius和其他四个值的用法和普通的xml使用效果相同。

增加形状

上边在宽度小于高度时候,改了xRadius和yRadius,感觉画出来就是个椭圆嘛,嗯,我们可以画个椭圆,不,还可以画个圆!好吧,引入一个形状的属性,把圆角矩形妥妥的扩展成矩形和圆两种形状。当然,画圆和椭圆就是一句代码的事情。

canvas.drawOval(strokeRect, mPaint);

填充色

只有了描边,万一填充色不是白的,或者说是实心的,实心按钮,该咋整,又需要一个属性来设置填充色了。
正当我想用paint画出一个填充色的圆角矩形时,突然想到,也可以设置个图片哦,这不就成了个圆形头像的控件了吗。

背景图的设置

完成图片的圆角显示,有三种方式可以实现,BitmapShader,ClipPath,Xfermode。很Happy的使用BitmapShader后发现如果使用填充颜色,这玩意就不适合了,需要Bitmap的。ClipPath使用后,background就没了,所以就使用Xfermode来遮罩,实现想要的效果吧,至于具体实现代码在这里先不细说了,大家可以去看看Xfermode的使用。

起初,我只是定义了个属性,加载进来图片,Xfermode进行遮罩,没毛病,然而,当我为控件设置了一个background的时候,悲剧了,被遮挡的背景显示黑色!整了半天去不掉,去掉background却OK,我总不能不让别人设置background吧,得了,那就用android的background来设置填充图片或者颜色,我再偷偷的去掉。为了方便,加个属性直接设置边框内的填充颜色,图片的话就用background去设置吧,OK妥妥的,获取background作为填充图,绘制完成之后把原来的background干掉,set为null,然而呢,控件宽高为0了,显示不出来。那就重新计算控件宽高好了。

当我写onMeasure()时,觉得还差点什么,嗯,圆形图片的宽高是原图的宽高,那么再加上个边框,宽高是不是该加上个描边宽度,倘若又不想加呢,又或者就是单纯的设置一个背景图片,不想切成圆的呢?另外,图片是不是也可以像ImageView那样设置不同的scaleType呢?好吧,一一解决了它。

背景图展示方式

对于背景图切不切的问题,给图片设置了三种显示方式:

  1. 只做背景图显示,不裁剪
  2. 图片按照边框的大小裁剪
  3. 图片放到边框之内

    属性如下:

<attr name="backgroundFitType" format="enum">
    <enum name="back" value="0"/>
    <enum name="crop" value="1"/>
    <enum name="inside" value="2"/>
</attr>

对于scaleType功能,瞅了一眼ImageView的实现方式,主要在configureBounds()方法里使用矩阵变换了,先复制过来再说。

由于我设置了backgroundFitType这三种展示模式,对应的宽高及矩阵变换都会稍稍不同。

  1. back : 就是简单的设置背景图,直接用super方法即可
  2. crop:贴边切,就是设置背景图之后,按照圆角矩形的形状把圆角之外的部分切除掉,所以宽高不变,要做的是在onDraw()方法中使用Xfermode裁剪掉圆角外的部分。
  3. inside:把图片放到边框线之内,也就是最当初的想法——设置填充图片。此时,若为wrap_content,需要计算图片宽高,和文字宽高作比较,较大的宽高加上描边线宽(乘以2),去和onMeasure()中的宽高比较,取较小值。在矩阵变换时,需要先将矩阵缩小到边框线之内再裁剪。

后续

其实,既然把图片加进来了,已经相当于一个自定义的ImageView了,要想写自定义ImageView大致思路就是上边的这些,继承自ImageView就行了,不再赘述。

到这里,理论部分大致就这么说完了,上代码?本来是想的,但是放上来一眼也看不清楚,所以在这里讲了讲我的思路,源码放到GitHub上了,点这里:https://github.com/CShawn/CommonViews

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值