圆形图片的制作

简介: 近期又在啃《Android开发艺术探索》这本书,最近看到了第六章节—Android中的Drawable。我写博客的风格不喜欢一味的介绍理论知识,更喜欢从实战的角度去学习,在敲代码的过程中去补充理论知识,根据实际情况做出分析,最后实现想要的效果。

近期又在啃《Android开发艺术探索》这本书,最近看到了第六章节—Android中的Drawable。我写博客的风格不喜欢一味的介绍理论知识,更喜欢从实战的角度去学习,在敲代码的过程中去补充理论知识,根据实际情况做出分析,最后实现想要的效果。本文就从制作圆形头像的角度,来学习Android中的Drawable的那些事。

一.准备工作
Drawable有很多种,表示的是一种可以在Canvas上进行绘制的抽象的概念,它的种类有很多种,最常见的颜色和图片都可以是一个Drawable。它是所有Drawable对象的基类,每个具体的Drawable都是它的子类。比如下文提到的BitmapDrawable。

BitmapDrawable是一种最简单的Drawable,它表示的就是一张图片,在实际开发中应用也是最广泛的,我们可以直接引用原始图片即可获取。

回到需求上,我们要制作一个圆形头像,首先需要一张图片,最终以圆形的方式展现出来。

这是我在网上找的一张图片:

这里写图片描述

制作圆形图片,最理想的效果是原始图片能够是正方形的,这样图片裁剪成圆形以后才会显得对称。而提供给我们的原始图片很有可能不是正方形的,比如上图,所以我们首先要加工这张图片,使成为正方形图片。

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_third"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="org.tyk.android.artstudy.ThirdActivity">


    <ImageView
        android:id="@+id/my_circle_img"
        android:layout_width="100dp"
        android:layout_height="150dp"
        android:layout_marginTop="10dp"
        android:src="@drawable/k" />


    <ImageView
        android:id="@+id/second_img"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginTop="10dp" />

    <Button
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="缩放" />

</LinearLayout>

布局文件中,上面是我们的原始图片,下面是我们处理以后的图片,看看点击事件做了什么:

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {


                Bitmap bitmap = ((BitmapDrawable) myCircleImageView.getDrawable()).getBitmap();
                Matrix matrix = new Matrix();
                float size = Math.min(bitmap.getWidth(), bitmap.getHeight());
                //x缩放比例
                float x = size / bitmap.getWidth();
                //y缩放比例
                float y = size / bitmap.getHeight();
                matrix.setScale(x, y);
                Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
                img.setImageBitmap(newBitmap);
            }
        });

首先获取当前ImageView的Bitmap,关于ImageView转换为Bitmap,大家可以参考这篇博客:

Android 怎么把imageview 转为Bitmap

然后构造一个矩阵Matrix,计算并设置好缩放比例,然后调用

createBitmap(Bitmap source, int x, int y, int width, int height,
Matrix m, boolean filter)

方法生成新的Bitmap。看看这个方法具体干了什么:

这里写图片描述

这是官方文档解释,source代表原始图片的Bitmap;x,y代表X,Y方向上的起始位置;width,height代表X,Y方向需要处理的宽度与高度;m代表图片处理的矩阵;,filter参数为true表示进行滤波处理,有助于改善新图像质量,flase代表不做过滤处理。

看看处理后的效果:

这里写图片描述

OK,图片的前期准备工作已经做好了,现在开始制作圆形头像了。

二.XferMode方法制作圆形图片
Xfermode有三个子类 :
AvoidXfermode ,PixelXorXfermode,PorterDuffXfermode,其中前两个类在API 16被遗弃了 。PorterDuffXfermode类主要用于计算图形合成时的图像过渡模式 ,一共有16条规则。然后调用 paint.setXfermode(XferMode)方法设置图像的过渡模式,这样就可以完成一些复杂的效果。先看看有哪些模式:

这里写图片描述

每种模式代表的含义如下:
1.PorterDuff.Mode.CLEAR
所绘制不会提交到画布上。
2.PorterDuff.Mode.SRC
显示上层绘制图片
3.PorterDuff.Mode.DST
显示下层绘制图片
4.PorterDuff.Mode.SRC_OVER
正常绘制显示,上下层绘制叠盖。
5.PorterDuff.Mode.DST_OVER
上下层都显示。下层居上显示。
6.PorterDuff.Mode.SRC_IN
取两层绘制交集。显示上层。
7.PorterDuff.Mode.DST_IN
取两层绘制交集。显示下层。
8.PorterDuff.Mode.SRC_OUT
取上层绘制非交集部分。
9.PorterDuff.Mode.DST_OUT
取下层绘制非交集部分。
10.PorterDuff.Mode.SRC_ATOP
取下层非交集部分与上层交集部分
11.PorterDuff.Mode.DST_ATOP
取上层非交集部分与下层交集部分
12.PorterDuff.Mode.XOR
异或:去除两图层交集部分
13.PorterDuff.Mode.DARKEN
取两图层全部区域,交集部分颜色加深
14.PorterDuff.Mode.LIGHTEN
取两图层全部,点亮交集部分颜色
15.PorterDuff.Mode.MULTIPLY
取两图层交集部分叠加后颜色
16.PorterDuff.Mode.SCREEN
取两图层全部区域,交集部分变为透明色

大家也可以参考这篇文章:

各个击破搞明白PorterDuff.Mode

这里我们结合圆形图像的例子看看怎么使用:

                //缩放以后的bitmap
                Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
                int width = newBitmap.getWidth();
                int height = newBitmap.getHeight();
                //圆形bitmap
                Bitmap circleBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(circleBitmap);
                Paint paint = new Paint();
                paint.setAntiAlias(true);
                paint.setColor(Color.BLACK);
                canvas.drawCircle(width / 2, height / 2, width / 2, paint);
                PorterDuffXfermode porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
                paint.setXfermode(porterDuffXfermode);
                canvas.drawBitmap(newBitmap, 0, 0, paint);
                circleImg.setImageBitmap(circleBitmap);

得到缩放以后的Bitmap以后,创建一个Canvas对象,首先绘制了一个黑色实心圆,然后设置Xfermode为PorterDuff.Mode.SRC_IN,最后绘制缩放以后的Bitmap。根据这种模式的定义:取两层绘制交集并显示上层,可以得到我们的圆形头像。最后实现的效果如下:

这里写图片描述

三.BitmapShader方法制作圆形图片

BitmapShader是Shader的子类,可以通过Paint.setShader(Shader shader)进行设置,BitmapShader的构造方法如下:

mBitmapShader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP);

参数1:bitmap
参数2,参数3:TileMode;
TileMode的取值有三种:
CLAMP 拉伸 拉伸的是图片最后的那一个像素;横向的最后一个横行像素,不断的重复,纵项的那一列像素,不断的重复
REPEAT 重复 就是横向、纵向不断重复这个bitmap
MIRROR 镜像 横向不断翻转重复,纵向不断翻转重复

关于BitmapShader可参考:
自定义控件其实很简单1/3

这里我们结合圆形图像的例子看看怎么使用:

                //缩放以后的bitmap
                Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
                int width = newBitmap.getWidth();
                int height = newBitmap.getHeight();

                //圆形bitmap
                Bitmap circleBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(circleBitmap);
                Paint paint = new Paint();
                paint.setAntiAlias(true);
                paint.setColor(Color.BLACK);
                /**第一种方式**/
//                canvas.drawCircle(width / 2, height / 2, width / 2, paint);
//                PorterDuffXfermode porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
//                paint.setXfermode(porterDuffXfermode);
//                canvas.drawBitmap(newBitmap, 0, 0, paint);
                /**第二种方式**/
                BitmapShader bitmapShader = new BitmapShader(newBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
                paint.setShader(bitmapShader);
                canvas.drawCircle(width / 2, height / 2, width / 2, paint);
                circleImg.setImageBitmap(circleBitmap);

得到缩放以后的Bitmap以后,创建一个Canvas对象,初始化BitmapShader,画笔设置Shader,最后在canvas里面进行画圆就行了。效果与第一种方式实现的一样,就不重复贴图了。

四.ClipPath方法制作圆形图片

使用clipPath的方法进行切割,来实现圆角图片,具体可参考:

Path图形与逻辑运算

这里我们结合圆形图像的例子看看怎么使用:

                //缩放以后的bitmap
                Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
                int width = newBitmap.getWidth();
                int height = newBitmap.getHeight();

                //圆形bitmap
                Bitmap circleBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(circleBitmap);
                Paint paint = new Paint();
                paint.setAntiAlias(true);
                paint.setColor(Color.BLACK);
                /**第一种方式**/
//                canvas.drawCircle(width / 2, height / 2, width / 2, paint);
//                PorterDuffXfermode porterDuffXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
//                paint.setXfermode(porterDuffXfermode);
//                canvas.drawBitmap(newBitmap, 0, 0, paint);
                /**第二种方式**/
//                BitmapShader bitmapShader = new BitmapShader(newBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//                paint.setShader(bitmapShader);
//                canvas.drawCircle(width / 2, height / 2, width / 2, paint);
//                circleImg.setImageBitmap(circleBitmap);
                /**第三种方式**/
                Path path = new Path();
                //按照顺时针方向添加一个圆
                path.addCircle(width / 2, height / 2, width / 2, Path.Direction.CW);
                canvas.save();
                //设置为在圆形区域内绘制
                canvas.clipPath(path);
                canvas.drawBitmap(newBitmap, 0, 0, paint);
                canvas.restore();
                circleImg.setImageBitmap(circleBitmap);

得到缩放以后的Bitmap以后,创建一个Canvas对象,设置为在圆形区域内绘制,最后在canvas里面绘制Bitmap。效果与第一种方式实现的一样,就不重复贴图了。

当然,要实现其他形状图片,其实很简单,每种方法稍微改变一下就行,只要先绘制出不同的形状,原理还是和这个一样。

OK,源码已同步上传至github,欢迎star,fork,提issues,一起交流进步!

https://github.com/18722527635/AndroidArtStudy

目录
相关文章
|
机器学习/深度学习 计算机视觉 网络架构
【YOLO11改进 - C3k2融合】C3k2DWRSeg二次创新C3k2_DWR:扩张式残差分割网络,提高特征提取效率和多尺度信息获取能力,助力小目标检测
【YOLO11改进 - C3k2融合】C3k2DWRSeg二次创新C3k2_DWR:扩张式残差分割网络,提高特征提取效率和多尺度信息获取能力,助力小目DWRSeg是一种高效的实时语义分割网络,通过将多尺度特征提取分为区域残差化和语义残差化两步,提高了特征提取效率。它引入了Dilation-wise Residual (DWR) 和 Simple Inverted Residual (SIR) 模块,优化了不同网络阶段的感受野。在Cityscapes和CamVid数据集上的实验表明,DWRSeg在准确性和推理速度之间取得了最佳平衡,达到了72.7%的mIoU,每秒319.5帧。代码和模型已公开。
【YOLO11改进 - C3k2融合】C3k2DWRSeg二次创新C3k2_DWR:扩张式残差分割网络,提高特征提取效率和多尺度信息获取能力,助力小目标检测
|
传感器 API Android开发
Android摄像头采集选Camera1还是Camera2?
Camera1与Camera2是Android平台上的两种摄像头API。Camera1(API1)在Android 5.0后被标记为过时,新项目应优先选用Camera2(API2)。Camera2提供了更精细的控制选项,如曝光时间、ISO感光度等;支持多摄像头管理;采用异步操作提高应用响应速度;并支持RAW图像捕获及实时图像处理。此外,它还具备更好的适配性和扩展性,适用于各类应用场景,如相机应用开发、视频通话和计算机视觉等。因此,在现代Android开发中推荐使用Camera2。
795 0
|
C#
WPF 自带Datagrid编辑后无法更新数据源的问题
原文  WPF 自带Datagrid编辑后无法更新数据源的问题 解决办法: 在列的绑定属性里加上UpdateSourceTrigger,示例XAML如下                                                                 这样当即时编辑Datagrid单元格数据时会立马更新数据源的值。
1825 0
|
前端开发 Android开发
【Android 应用开发】Paint 渲染 之 BitmapShader 位图渲染 ( 渲染流程 | CLAMP 拉伸最后像素 | REPEAT 重复绘制图片 | MIRROR 绘制反向图片 )(一)
【Android 应用开发】Paint 渲染 之 BitmapShader 位图渲染 ( 渲染流程 | CLAMP 拉伸最后像素 | REPEAT 重复绘制图片 | MIRROR 绘制反向图片 )(一)
709 0
|
15天前
|
人工智能 JSON 供应链
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
LucianaiB分享零成本畅用JVS Claw教程(学生认证享7个月使用权),并开源GeoMind项目——将JVS改造为科研与产业地理情报可视化AI助手,支持飞书文档解析、地理编码与腾讯地图可视化,助力产业关系图谱构建。
23512 12
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
|
4天前
|
人工智能 BI 持续交付
Claude Code 深度适配 DeepSeek V4-Pro 实测:全场景通关与真实体验报告
在 AI 编程工具日趋主流的今天,Claude Code 凭借强大的任务执行、工具调用与工程化能力,成为开发者与自动化运维的核心效率工具。但随着原生模型账号稳定性问题频发,寻找一套兼容、稳定、能力在线的替代方案变得尤为重要。DeepSeek V4-Pro 作为新一代高性能大模型,提供了完整兼容 Claude 协议的 API 接口,只需简单配置即可无缝驱动 Claude Code,且在任务执行、工具调用、复杂流程处理上表现极为稳定。
1268 3
|
9天前
|
人工智能 缓存 Shell
Claude Code 全攻略:命令大全 + 实战工作流(完整版)
Claude Code 是一款运行在终端环境下的 AI 编码助手,能够直接在项目目录中理解代码结构、编辑文件、执行命令、执行开发计划,并支持持久化记忆、上下文压缩、后台任务、多模型切换等专业能力。对于日常开发、项目维护、快速重构、代码审查等场景,它可以大幅减少手动操作、提升编码效率。本文从常用命令、界面模式、核心指令、记忆机制、图片处理、进阶工作流等维度完整说明,帮助开发者快速上手并稳定使用。
2331 4
|
3天前
|
Shell API 开发工具
Claude Code 快速上手指南(新手友好版)
AI编程工具卷疯啦!Claude Code凭借任务驱动+终端原生的特性,成了开发者的效率搭子。本文从安装、登录、切换国产模型到常用命令,手把手带新手快速上手,全程避坑,30分钟独立用起来。
929 7