前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >几行代码撸一个圆角ImageView

几行代码撸一个圆角ImageView

作者头像
Android扫地僧
发布2020-03-19 15:56:55
9120
发布2020-03-19 15:56:55
举报
文章被收录于专栏:Android进阶Android进阶

今日防疫提示

房间应每日开窗通风,上下午各1次,每次通风时间30分钟以上,可选择阳光充足的时间段进行,保持空气清新。

今天项目需要用到一个圆角ImageView,本来已经打开了百度搜索“Android圆角Image...”,还是打消了这个念头,本着一个热(xian)爱(de)学(dan)习(teng)的态度,这个轮子还是自己动手造一下吧

由于只是需要实现圆角效果,直接继承ImageView即可,这里为了兼容,选中继承androidx下的AppcompatImageView.

1.自定义属性

简单粗暴,一般来说,我们是需要四个角都是统一半径的圆角,所以定义一个radius就可以了。但是不排除万能的PM可能要你明天只要左上圆角和右下圆角,所以四个圆角半径也分别定义一下。

代码语言:javascript
复制
    <declare-styleable name="RoundImageView">
        <!--一步到位,四个圆角统一半径-->
        <attr name="radius" format="dimension"/>
        <!--左上圆角半径-->
        <attr name="left_top_radius" format="dimension"/>
        <!--右上圆角半径-->
        <attr name="right_top_radius" format="dimension"/>
        <!--左下圆角半径-->
        <attr name="left_bottom_radius" format="dimension"/>
        <!--右下圆角半径-->
        <attr name="right_bottom_radius" format="dimension"/>
    </declare-styleable>

2. 实现方法

实现圆角ImageView基本上有两种思路:

  1. 直接操作Canvas, 通过clipPath裁剪,去掉画布的四个直角,剩下的就得到了圆角的ImageView, 所以只需要绘制一个圆角矩形的路径即可 优点:实现简单,效率较高 缺点:由于操作的是canvas, 所以如果scaleType不能撑满整个控件,图片是无法实现圆角效果的
  2. 在图片绘制前,将图片本身圆角化。 优点:不受控件scaleType约束 缺点:需要对drawable进行额外处理,耗时肯定也会高一些

综合实际考虑,实际项目中,基本都会将ImageView的scaleType设为center_crop或者scaleXY, 一般是不会出现图片比实际控件小的情况,所以本文实现第一种方式,简简单单,需要的变量如下:

代码语言:javascript
复制
public class RoundImageView extends AppCompatImageView {
    //控件宽度,需要用来检验圆角半径合法性
    private float width;
    //控件高度,需要用来检验圆角半径合法性
    private float height;
    //统一圆角半径
    private int mRadius;
    //左上圆角半径-
    private int mLeftTopRadius;
    //右上圆角半径-
    private int mRightTopRadius;
    //右下圆角半径-
    private int mRightBottomRadius;
    //左下圆角半径-
    private int mLeftBottomRadius;
    //带圆角的Path
    private Path mRoundPath;

圆角实现

1、 获取自定义属性

代码语言:javascript
复制
    public RoundImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mRoundPath = new Path();
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RoundImageView, defStyleAttr, 0);
        mRadius = typedArray.getDimensionPixelSize(R.styleable.RoundImageView_radius, 0);
        mLeftTopRadius = typedArray.getDimensionPixelSize(R.styleable.RoundImageView_left_top_radius, 0);
        mLeftBottomRadius = typedArray.getDimensionPixelSize(R.styleable.RoundImageView_left_bottom_radius, 0);
        mRightTopRadius = typedArray.getDimensionPixelSize(R.styleable.RoundImageView_right_top_radius, 0);
        mRightBottomRadius = typedArray.getDimensionPixelSize(R.styleable.RoundImageView_right_bottom_radius, 0);
        //别忘了释放资源
        typedArray.recycle();
    }

2、绘制前,校验参数合法性

为了增强健壮性,如果用户设置一个无穷大的圆角半径,那画出来就是个妖怪了,所以要进行参数校验,圆角半径不能超过边长的一半

代码语言:javascript
复制
private void checkRadius() {
    //如果未分别设置四个原画半径,都使用统一圆角半径
    if (mLeftTopRadius == 0) {
        mLeftTopRadius = mRadius;
    }
    if (mLeftBottomRadius == 0) {
        mLeftBottomRadius = mRadius;
    }
    if (mRightTopRadius == 0) {
        mRightTopRadius = mRadius;
    }
    if (mRightBottomRadius == 0) {
        mRightBottomRadius = mRadius;
    }
    //获取控件较短的一条边
    int minSize = (int) (Math.min(width, height) / 2);
    //如果用户任意设置一个很大的半径,将radius修正为较短边的一半
    //显示效果就是一个圆或者两端呈圆形的矩形
    if (mLeftTopRadius > minSize) {
        mLeftTopRadius = minSize;
    }
    if (mLeftBottomRadius > minSize) {
        mLeftBottomRadius = minSize;
    }
    if (mRightTopRadius > minSize) {
        mRightTopRadius = minSize;
    }
    if (mRightBottomRadius > minSize) {
        mRightBottomRadius = minSize;
    }
 }

3、绘制圆角路径

这里使用二阶贝塞尔曲线来绘制圆角弧度,当然用arcTo( ) 也是可以的,同时由于没有设置默认圆角半径,减少不必要的绘制,如果用户没有设置圆角半径,直接按原图绘制即可

四个圆角分别绘制,可以实现任意圆角,其他角保持直角

代码语言:javascript
复制
@Override
protected void onDraw(Canvas canvas) {
    checkRadius();
    mRoundPath.reset();
    //绘制左上圆角
    if (mLeftTopRadius != 0) {
        mRoundPath.moveTo(0, mLeftTopRadius);
        mRoundPath.quadTo(0, 0, mLeftTopRadius, 0);
    } else {
        //如果未设置圆角,不作处理,下同
        mRoundPath.moveTo(0, 0);
    }
    //绘制右上圆角
    if (mRightTopRadius != 0) {
        mRoundPath.lineTo(width - mRightTopRadius, 0);
        mRoundPath.quadTo(width, 0, width, mRightTopRadius);
    } else {
        mRoundPath.lineTo(width, 0);
    }
    //绘制右下圆角
    if (mRightBottomRadius != 0) {
        mRoundPath.lineTo(width, height - mRightBottomRadius);
        mRoundPath.quadTo(width, height, width - mRightBottomRadius, height);
    } else {
        mRoundPath.lineTo(width, height);
    }
    //绘制左下圆角
    if (mLeftBottomRadius != 0) {
        mRoundPath.lineTo(mLeftBottomRadius, height);
        mRoundPath.quadTo(0, height, 0, height - mLeftBottomRadius);
    } else {
        mRoundPath.lineTo(0, height);
    }
    mRoundPath.close();
    canvas.clipPath(mRoundPath);
    super.onDraw(canvas);
}

实现效果

API返回原图:

实际效果:

布局文件:

代码语言:javascript
复制
    <com.android.xxx.ui.widget.RoundImageView
        android:id="@+id/banner_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:radius="6dp"
        android:scaleType="fitXY" />

由于时间紧急,还有很多考虑不周的地方,请各位大牛指正,反正...

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android扫地僧 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档