Android自定义View之绘制圆形头像

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/huangliniqng/article/details/100813623

前言

做APP应用开发的时候,用户头像肯定是必不可少的,但是90%以上的需求头像都是圆形的。那么,如何通过自定义View的方式实现圆形头像呢,那么,本片博文会告诉你不仅仅是实现过程。一定会有意想不到的收获哦!

最终效果

国际惯例,我们先来看最终实现的效果图

自定义RoundImageView继承自ImageView

public class RoundImageView extends ImageView {
    
    public RoundImageView(Context context) {
        super(context);
    }

    public RoundImageView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

不知你是否注意过每当我们继承自View的时候,系统都会提示我们覆盖重写4个构造方法,这里我们只覆盖了三个,然后就开始在每个构造方法中进行初始化,那么,是不是每次都会调用所有的构造方法呢,如果不是,这三个构造方法又会什么时候调用呢?下面我们来通过例子来验证。

使用自定义View无非就两种情况下,第一种就是直接在xml布局中使用,另一种就是在Activity中new出来,下面我们分别使用上述两种方式,为了便于观察我们在三个构造方法中分别加入一行打印。

首先我们在xml直接使用,运行打印如下:

com.example.roundimageview D/RoundImageView: RoundImageView: 两个参数的构造方法

然后我们在Activity中,new一个RoundImageView

roundImageView = RoundImageView(this@MainActivity)
roundImageView = RoundImageView(this@MainActivity, null)
roundImageView = RoundImageView(this@MainActivity, null,0)

运行打印日志如下:

结论:自定义View当在xml中使用,使用的是第二个构造方法,当在Activity中使用时,实例化时传入几个参数调用的就是含有几个参数的构造方法。

实现圆形头像的思想

我始终认为自定义View的难度只在于它的实现思想,通常我们遇到问题的时候,并不是Google不到,而是压根就不知道这个问题该去如何Google,如果知道了问题所产生的原因,其实问题已经迎刃而解了,最怕的是不知道问题为什么会产生。

实现圆形头像的思想一个简单的图就可以表示了。

矩形区域是完整的图片,圆形区域就是我们最终显示的头像区域,那么就很简单了,圆形区域与矩形区域相交,取并集区域?在矩形中画一个与矩形长或宽相切的圆,而圆的直径是长或宽较短的一边。

编码实现

  • 获取原有头像的bitmap

首先我们需要获取设置头像的bitmap,我们可以直接通过API来获取设置的图片资源,

drawable = this.getDrawable();

再将图片资源转化为bitmap

首先我们判断drawable是否为空,如果为空说明用户没有设置,抛出资源未找到的异常。

if (drawable == null) {
    throw new Resources.NotFoundException("Image resource not set");
}

如果不为空,我们创建一个与图片资源大小相等的bitmap,并将bitmap绘制出来,代码如下所示:

bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),
        Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
        drawable.getIntrinsicHeight());
drawable.draw(canvas);
  • 绘制圆形bitmap

通过上面的代码,我们得到了原有的bitmap图像,紧接着我们需要绘制圆形的bitmap,与上面类似,首先创建一个和bitmap大小一致的位图

circleBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);

我们画一个与bitmap等大的矩形

Paint paint = new Paint();
Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
canvas.drawRect(rect,paint);
RectF rectF = new RectF(rect);

将较短的一边设置圆的半径

float roundRa = 0.0f;
if (bitmap.getWidth() > bitmap.getHeight()) {
    roundRa = bitmap.getHeight() / 2.0f;
} else {
    roundRa = bitmap.getWidth() / 2.0f;
}

设置paint和canvas属性

paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(Color.WHITE);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

canvas.drawARGB将绘制裁剪设为透明,paint.setXfermode中的PorterDuffXfermode类很强大,后面我们会单独一篇文章讲解。

最终我们重新将bitmap绘制出来即可

canvas.drawBitmap(bitmap, rect, rect, paint);

绘制部分完整代码如下所示:

 * 获取圆形裁剪的bitmap
 *
 * @param bitmap 原bitmap
 */
private Bitmap getCircleBitmap(Bitmap bitmap) {
    circleBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(circleBitmap);
    Paint paint = new Paint();
    Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
    RectF rectF = new RectF(rect);

    float roundRa = 0.0f;
    if (bitmap.getWidth() > bitmap.getHeight()) {
        roundRa = bitmap.getHeight() / 2.0f;
    } else {
        roundRa = bitmap.getWidth() / 2.0f;
    }

    paint.setAntiAlias(true);
    canvas.drawARGB(0, 0, 0, 0);
    paint.setColor(Color.GRAY);
    canvas.drawRoundRect(rectF, roundRa, roundRa, paint);
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
    canvas.drawBitmap(bitmap, rect, rect, paint);
    return circleBitmap;

}
  • 设置最终的bitmap

得到bitma后我们直接重新设置即可显示

 setImageBitmap(getCircleBitmap(bitmap));

本实例较为简单,就不贴所有代码了,如有需要留言邮箱即可,如有纰漏之处,欢迎指正!晚安!

9.15 22:17 更新

代码已上传github:https://github.com/huanglinqing123/RoundImageView

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏web秀

微信小程序popup组件你会了吗?

上述就是一个微信小程序pop组件实现的全部代码,看起来是不是很简单了,赶紧自己动手试试吧,没有你想象的那么难!!!

34410
来自专栏日常学python

爬虫开发中逆向思维是什么?怎么修炼?

这半年间我和一些有多年开发经验的大牛都有过交流,发现他们中大多数都表示对于简单的加密还可以试着逆向,但是稍微复杂一点的就不行了。其实也不难发现,...

9510
来自专栏web秀

SVG 线条动画基础入门知识

1、CSS3 动画 2、javascript 动画(canvas) 3、html 动画(SVG)

12830
来自专栏吉林乌拉

注解简单详解

在前几篇中,我们基本都是通过xml的方式来为对象织入增强的,但在实际的开发中,我们基本不会采用这种方式,因为spring为我们提供了更方便的注解的方式来实现和x...

7920
来自专栏算法channel

推荐一个超详细的Hadoop安装教程,已有32万次点击量

安装Hadoop软件系列,首选Linux系统,比如Ubuntu。如果你的系统是Windows,建议再安装一个Ubuntu,组成双系统,如此可以发挥各自系统的特...

20910
来自专栏余林丰

Spring中老生常谈的FactoryBean

FactoryBean和BeanFactory由于在命名上极其相似,一直以来困扰了不少的开发者。

10740
来自专栏用户6117737的专栏

微信小程序的技术架构特点

腾讯为微信小程序的工程项目开发了完整的编辑开发、运行调试、打包发布环境,形成了一个完整的IDE。

26940
来自专栏AndroidStudio初识

错误操作怎么办?用他让你不再害怕!—Dialog最详解

Hi,好久不见,甚是想念各位花粉,为了感谢花粉们长久以来的支持,本篇文章继续分享Android中非常实用的干货— Dialog(对话框)!那么什么叫 Dialo...

6420
来自专栏python爱好部落

python处理testlink

在软件活动中,我们需要对测试用例进行管理,如果只用excel,不用管理工具系统的管理,那么将出现以下一些问题: 案例文件分散,测试进度不透明; ...

20020
来自专栏日拱一兵

Mybatis拦截器执行过程解析

上一篇文章 Mybatis拦截器之数据加密解密 介绍了 Mybatis 拦截器的简单使用,这篇文章将透彻的分析 Mybatis 是怎样发现拦截器以及调用拦截器 ...

14630

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励