Android 自定义View实现圆形头像(适用于任意布局)
发布日期:2021-06-29 11:46:17 浏览次数:3 分类:技术文章

本文共 4675 字,大约阅读时间需要 15 分钟。

先看效果图:

先来说下我的思路:首先我需要在自定义View中动态获取头像id,那么就需要在attrs文件中,写一个关于该View类的自定义属性。这里仿照ImageView,取名为src,类型为reference引用类型

然后在xml布局文件中,使用CircleImageView控件并加上这个“src”属性,表示我要通过src来获取图片引用

接着,我们在CircleImageView类里调用该属性来获取图片

//获取自定义属性    private void getCustomType(){        TypedArray array=context.obtainStyledAttributes(attrs, R.styleable.CircleImageView);        headBitmap= BitmapFactory.decodeResource(getResources(),array.getResourceId(R.styleable.CircleImageView_src,0));    }

R.styleable.CircleImageView_src:这是特定的书写模式,declare-styleable标签的name属性值_attr标签的name属性值

 通过以上步骤,我们就能动态获取到自己在布局页面中设置的图片。下面,我们来分析一下如何将图片变成圆形:

首先,canvas.drawCircle()方法是用来绘制一个圆的,但是传参时不能传入一张图片,也就是bitmap。了解过Shader着色器的应该知道,有一个类叫BitmapShader,它被创建的时候,需要传入一个bitmap,然后这个shader会被作为参数传给Paint画笔。这就是我们需要的,我们可以将上面动态获取到的bitmap作为参数传给Shader,Shader再传给Paint,最后在drawCircle()传入该Paint画笔,这样图片就能以圆形的样式显示出来了。具体代码如下:

public MyCircleImageView(Context context, @Nullable AttributeSet attrs) {    super(context, attrs);    getCustomType(context,attrs);    init();}private void init(){    shader=new BitmapShader(headBitmap,Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);    matrix=new Matrix();    paint=new Paint();    paint.setAntiAlias(true);}private void initPaintShader(){ //初始化画笔shader    float scaleX=1,scaleY=1;    //如果图片与圆的直径不一致,等比例缩放图片    if(headBitmap.getWidth()!=radius*2||headBitmap.getHeight()!=radius*2){        scaleX=(radius*2)/(headBitmap.getWidth()*1.0f);        scaleY=(radius*2)/(headBitmap.getHeight()*1.0f);    }    matrix.setScale(scaleX,scaleY);    shader.setLocalMatrix(matrix);    paint.setShader(shader);}

好了,通过以上方法,已经可以成功绘制出圆形头像。那么最后,我们要解决的问题是如何让自定义View可以在布局文件随意使用,我们需要适配控件中的padding属性,以及根据它的宽,高的测量模式,改变我们drawCircle时传入的半径radius参数。

1、重写onMeasure方法,动态调整radius圆形半径

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    int modeX=MeasureSpec.getMode(widthMeasureSpec);    int modeY=MeasureSpec.getMode(heightMeasureSpec);    int sizeX=MeasureSpec.getSize(widthMeasureSpec);    int sizeY=MeasureSpec.getSize(heightMeasureSpec);    //width和height都是EXACTLY(精确)的测量模式    if(modeX == MeasureSpec.EXACTLY && modeY == MeasureSpec.EXACTLY){        radius=sizeX

2、在onDraw()中适配padding属性:

@Overrideprotected void onDraw(Canvas canvas) {    super.onDraw(canvas);    int realRadius=radius;    //根据padding值,获取真实的圆的半径    if(getPaddingLeft()+getPaddingRight()>=getPaddingTop()+getPaddingBottom())        realRadius=(realRadius*2-getPaddingLeft()-getPaddingRight())/2;    else        realRadius=(realRadius*2-getPaddingTop()-getPaddingBottom())/2;    //根据padding值,设置圆心的真实坐标    canvas.drawCircle(radius+getPaddingLeft()-getPaddingRight(),radius+getPaddingTop()-getPaddingBottom(),realRadius,paint);}

通过以上两步,就能够成功适配了。我们可以在布局文件中随意调整常用属性。

最后呈上完整代码:

import android.content.Context;import android.content.res.TypedArray;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.BitmapShader;import android.graphics.Canvas;import android.graphics.Matrix;import android.graphics.Paint;import android.graphics.Shader;import android.util.AttributeSet;import android.view.View;import androidx.annotation.Nullable;public class MyCircleImageView extends View {    private Bitmap headBitmap;  //头像图片    private int radius;         //圆的半径    private Paint paint;        //自定义画笔    private Matrix matrix;    private Shader shader;    public MyCircleImageView(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        getCustomType(context,attrs);        init();    }    private void init(){        shader=new BitmapShader(headBitmap,Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);        matrix=new Matrix();        paint=new Paint();        paint.setAntiAlias(true);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        int modeX=MeasureSpec.getMode(widthMeasureSpec);        int modeY=MeasureSpec.getMode(heightMeasureSpec);        int sizeX=MeasureSpec.getSize(widthMeasureSpec);        int sizeY=MeasureSpec.getSize(heightMeasureSpec);        //width和height都是EXACTLY(精确)的测量模式        if(modeX == MeasureSpec.EXACTLY && modeY == MeasureSpec.EXACTLY){            radius=sizeX
=getPaddingTop()+getPaddingBottom()) realRadius=(realRadius*2-getPaddingLeft()-getPaddingRight())/2; else realRadius=(realRadius*2-getPaddingTop()-getPaddingBottom())/2; //根据padding值,设置圆心的真实坐标 canvas.drawCircle(radius+getPaddingLeft()-getPaddingRight(),radius+getPaddingTop()-getPaddingBottom(),realRadius,paint); }}

 

转载地址:https://blog.csdn.net/zz51233273/article/details/107556534 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Android 自定义View实现可拖动边框缩放的矩形
下一篇:Android 自定义View实现动画形式加载环形图

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2024年04月04日 08时30分31秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

知乎:硬件和软件哪个吃香? 2019-04-29
中国深圳,600架无人机的盛典! 2019-04-29
干货分享 JVM 之第 3 篇 —— Java 内存结构相关 2019-04-29
干货分享 JVM 之第 4 篇 —— 掌握 Jmeter 压力测试工具,熟悉 Jconsole.exe 工具 2019-04-29
干货分享 JVM 之第 5 篇 —— 类加载器 2019-04-29
干货分享 JVM 之第 6 篇 —— SpringBoot2.0 框架性能调优 2019-04-29
基于 Hystrix 高并发服务限流第 1 篇 —— 必须了解的相关概念 2019-04-29
基于 Hystrix 高并发服务限流第 2 篇 —— 服务隔离(线程池隔离、信号量隔离) 2019-04-29
基于 Hystrix 高并发服务限流第 3 篇 —— 服务熔断、服务降级 2019-04-29
基于 Hystrix 高并发服务限流第 4 篇 —— 基于 Feign 实现服务熔断降级处理 2019-04-29
基于 Hystrix 高并发服务限流第 5 篇 —— Hystrix 监控 2019-04-29
Eureka 如何快速的、优雅的停止某个微服务 2019-04-29
Eureka 实现安全认证 2019-04-29
基于 Hystrix 高并发服务限流第 6 篇 —— 服务限流,基于 RateLimiter 实现 2019-04-29
Nginx 反向代理、负载均衡配置、Location正则表达式 2019-04-29
SpringBoot + WebSocket 实现前后端的收发消息 2019-04-29
SpringBoot 整合 JWT 实现统一认证 2019-04-29
SpringBoot 使用 CompletableFuture 实现非阻塞异步编程 2019-04-29
即刻就业:本科毕业如何快速高薪就业? 2019-04-29
即刻就业:java的应用程序有哪些,java程序有哪些编码规范,开发java应用程序有哪些步骤 2019-04-29