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

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

前言

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

最终效果

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

image.gif

自定义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);
    }
}

image.gif

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

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

image.gif

首先我们在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)

image.gif

运行打印日志如下:

image.gif

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

实现圆形头像的思想

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

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

image.gif

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

编码实现

    • 获取原有头像的bitmap

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

    drawable = this.getDrawable();

    image.gif

     再将图片资源转化为bitmap

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

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

    image.gif

     如果不为空,我们创建一个与图片资源大小相等的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);

    image.gif

      • 绘制圆形bitmap

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

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

      image.gif

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

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

      image.gif

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

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

      image.gif

        设置paint和canvas属性

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

      image.gif

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

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

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

      image.gif

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

      * 获取圆形裁剪的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;
      }

      image.gif

        • 设置最终的bitmap

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

        setImageBitmap(getCircleBitmap(bitmap));

        image.gif

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

          9.15 22:17 更新

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

        目录
        相关文章
        |
        13天前
        |
        Android开发 开发者
        安卓开发中的自定义视图:从入门到精通
        【9月更文挑战第19天】在安卓开发的广阔天地中,自定义视图是一块充满魔力的土地。它不仅仅是代码的堆砌,更是艺术与科技的完美结合。通过掌握自定义视图,开发者能够打破常规,创造出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战应用,一步步展示如何用代码绘出心中的蓝图。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇通往创意和效率的大门。让我们一起探索自定义视图的秘密,将你的应用打造成一件艺术品吧!
        38 10
        |
        18天前
        |
        XML 编解码 Android开发
        安卓开发中的自定义视图控件
        【9月更文挑战第14天】在安卓开发中,自定义视图控件是一种高级技巧,它可以让开发者根据项目需求创建出独特的用户界面元素。本文将通过一个简单示例,引导你了解如何在安卓项目中实现自定义视图控件,包括创建自定义控件类、处理绘制逻辑以及响应用户交互。无论你是初学者还是有经验的开发者,这篇文章都会为你提供有价值的见解和技巧。
        30 3
        |
        19天前
        |
        前端开发 Android开发 开发者
        安卓应用开发中的自定义视图基础
        【9月更文挑战第13天】在安卓开发的广阔天地中,自定义视图是一块神奇的画布,它允许开发者将想象力转化为用户界面的创新元素。本文将带你一探究竟,了解如何从零开始构建自定义视图,包括绘图基础、触摸事件处理,以及性能优化的实用技巧。无论你是想提升应用的视觉吸引力,还是追求更流畅的交互体验,这里都有你需要的金钥匙。
        |
        22天前
        |
        缓存 搜索推荐 Android开发
        安卓应用开发中的自定义View组件实践
        【9月更文挑战第10天】在安卓开发领域,自定义View是提升用户体验和实现界面个性化的重要手段。本文将通过一个实际案例,展示如何在安卓项目中创建和使用自定义View组件,包括设计思路、实现步骤以及可能遇到的问题和解决方案。文章不仅提供了代码示例,还深入探讨了自定义View的性能优化技巧,旨在帮助开发者更好地掌握这一技能。
        |
        24天前
        |
        Android开发
        Android中SurfaceView的双缓冲机制和普通View叠加问题解决办法
        本文介绍了 Android 平台上的 SurfaceView,这是一种高效的图形渲染控件,尤其适用于视频播放、游戏和图形动画等场景。文章详细解释了其双缓冲机制,该机制通过前后缓冲区交换来减少图像闪烁,提升视觉体验。然而,SurfaceView 与普通 View 叠加时可能存在 Z-Order 不一致、同步问题及混合渲染难题。文中提供了使用 TextureView、调整 Z-Order 和创建自定义组合控件等多种解决方案。
        56 9
        |
        2月前
        |
        XML 前端开发 Android开发
        Android面试高频知识点(3) 详解Android View的绘制流程
        View的绘制和事件处理是两个重要的主题,上一篇《图解 Android事件分发机制》已经把事件的分发机制讲得比较详细了,这一篇是针对View的绘制,View的绘制如果你有所了解,基本分为measure、layout、draw 过程,其中比较难理解就是measure过程,所以本篇文章大幅笔地分析measure过程,相对讲得比较详细,文章也比较长,如果你对View的绘制还不是很懂,对measure过程掌握得不是很深刻,那么耐心点,看完这篇文章,相信你会有所收获的。
        78 2
        |
        3月前
        |
        消息中间件 前端开发 Android开发
        Android面试题自定义View之Window、ViewRootImpl和View的三大流程
        Android开发中,View的三大核心流程包括measure(测量)、layout(布局)和draw(绘制)。MeasureSpec类在测量过程中起到关键作用,它结合尺寸大小和模式(EXACTLY、AT_MOST、UNSPECIFIED)来指定View应如何测量。onMeasure方法用于自定义View的测量,布局阶段,ViewGroup调用onLayout确定子元素位置,而draw阶段按照特定顺序绘制背景、内容、子元素和装饰。整个流程始于ViewRootImpl的performTraversals,该方法触发测量、布局和绘制。
        47 0
        下一篇
        无影云桌面