先看效果图:

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

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="CircleImageView"><attr name="src" format="reference"/></declare-styleable>
</resources>

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

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"><com.hualinfo.myviewtext.MyCircleImageViewandroid:id="@+id/head"android:layout_width="100dp"android:layout_height="100dp"app:src="@mipmap/head"android:layout_marginLeft="15dp"android:layout_marginTop="15dp"/>
</RelativeLayout>

接着,我们在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(){ //初始化画笔shaderfloat 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圆形半径

@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<sizeY?sizeX/2:sizeY/2;}else if(modeX == MeasureSpec.EXACTLY){radius=sizeX/2;}else if(modeY == MeasureSpec.EXACTLY){radius=sizeY/2;}//width和height都是AT_MOST(尽可以大)的测量模式,或者是UNSPECIAL(无上限)else{radius=headBitmap.getWidth()<headBitmap.getHeight()?headBitmap.getWidth()/2:headBitmap.getHeight()/2;}initPaintShader();//保存测量宽度和测量高度setMeasuredDimension(radius*2, radius*2);
}

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

@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);int realRadius=radius;//根据padding值,获取真实的圆的半径if(getPaddingLeft()+getPaddingRight()>=getPaddingTop()+getPaddingBottom())realRadius=(realRadius*2-getPaddingLeft()-getPaddingRight())/2;elserealRadius=(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);}@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<sizeY?sizeX/2:sizeY/2;}else if(modeX == MeasureSpec.EXACTLY){radius=sizeX/2;}else if(modeY == MeasureSpec.EXACTLY){radius=sizeY/2;}//width和height都是AT_MOST(尽可以大)的测量模式,或者是UNSPECIAL(无上限)else{radius=headBitmap.getWidth()<headBitmap.getHeight()?headBitmap.getWidth()/2:headBitmap.getHeight()/2;}initPaintShader();//保存测量宽度和测量高度setMeasuredDimension(radius*2, radius*2);}private void initPaintShader(){ //初始化画笔shaderfloat 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);}//获取自定义属性private void getCustomType(Context context,AttributeSet attrs){TypedArray array=context.obtainStyledAttributes(attrs, R.styleable.CircleImageView);headBitmap= BitmapFactory.decodeResource(getResources(),array.getResourceId(R.styleable.CircleImageView_src,0));}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);int realRadius=radius;//根据padding值,获取真实的圆的半径if(getPaddingLeft()+getPaddingRight()>=getPaddingTop()+getPaddingBottom())realRadius=(realRadius*2-getPaddingLeft()-getPaddingRight())/2;elserealRadius=(realRadius*2-getPaddingTop()-getPaddingBottom())/2;//根据padding值,设置圆心的真实坐标canvas.drawCircle(radius+getPaddingLeft()-getPaddingRight(),radius+getPaddingTop()-getPaddingBottom(),realRadius,paint);}
}

Android 自定义View实现圆形头像(适用于任意布局)相关推荐

  1. Android自定义View之圆形头像

    记录贴 现在制作圆形头像的第三方工具已经很多了,本帖只为记录自定义view学习过程. 1.主体代码部分 public class CirclePhotoView extends View {priva ...

  2. android 清空canvas部分内容_Android自定义View实现圆形头像效果

    在我们的APP中通常会遇到,展示圆形头像的需求,一般通过Glide就能实现,但是让我们做一个圆形头像,如果让我们自定义实现这种效果,该怎样做呢? 好,接下来本文通过三种方式来实现这种效果! 注意:这是 ...

  3. android view 渐变动画,Android自定义view渐变圆形动画

    本文实例为大家分享了Android自定义view渐变圆形动画的具体代码,供大家参考,具体内容如下 直接上效果图 自定义属性 attrs.xml文件 创建一个类 ProgressRing继承自 view ...

  4. Android自定义view,圆形的TextView,并通过xml设置属性,AttributeSet中取值

    Android自定义view设置xml属性 一个圆形的自定义TextView,通过xml来设置背景颜色的属性 values/attrs <declare-styleable name=" ...

  5. android 环绕布局,Android自定义View实现圆形环绕效果

    之前项目中需要实现一个四周环绕中心圆形头像的效果,感觉还是自定义比较方便,于是就自己封装了一个控件去实现.先贴张图显示最终效果. 首先自定义一个View继承自LinearLayout,通过动态添加ch ...

  6. Android自定义滑动进度条,Android自定义View实现圆形水波进度条

    每次听到某大牛谈论自定义View,顿时敬佩之心,如滔滔江水连绵不绝,心想我什么时候能有如此境界,好了,心动不如行动,于是我开始了自定义View之路,虽然过程有坎坷,但是结果我还是挺满意的.我知道大牛还 ...

  7. 自定义View,圆形头像

    1. 效果图 2. xml中 <com.etoury.etoury.ui.view.CircleImgandroid:id="@+id/user_info_head_img" ...

  8. Android自定义view之圆形进度条

    本节介绍自定义view-圆形进度条 思路: 根据前面介绍的自定义view内容可拓展得之: 1:新建类继承自View 2:添加自定义view属性 3:重写onDraw(Canvas canvas) 4: ...

  9. android 缺半圆形头像,Android 自定义Glide实现圆形头像效果(圆角,正常,黑白,圆形效果)...

    /** * Glide 图片加载工具类 */ /** * diskCacheStrategy参数补充 * * DiskCacheStrategy.NONE 表示不缓存任何内容. * * DiskCac ...

最新文章

  1. pandas 删掉空行
  2. PYTHON1.day01
  3. shareSDK 微信分享的时候只能分享jpg格式的图片大于32k时为什么分享不出去?
  4. python mainloop bind_Python3 tkinter基础 Frame bind 捕捉多键同时按
  5. CISSP的成长之路(七):复习信息安全管理(1)
  6. linux/unix编程手册-16_20
  7. ActionTileViewController.js
  8. python数据类型汇总_python基础数据类型汇总
  9. 这些全国各地的特色面,你都吃过了吗?
  10. 阿里五年晋升三次,这个程序员要聊聊他的选择
  11. 安装oracle 10g闪退,Windows 7安装Oracle 10g常见错误及解决方法
  12. 5-2计算机视觉的常见概念
  13. python不带颜色的图形_用python给黑白图像上色
  14. citp协议服务器,Picturall Octo 媒体服务器
  15. 对封装的ajax的应用-查询商铺
  16. 4.16 期货每日早盘操作建议
  17. 流媒体播放器VLC media player
  18. Java笔记(韩顺平Java基础15-20章)
  19. RocketMQ Topic是如何注册和保存的
  20. 批处理Bat建立微信多开程序

热门文章

  1. DNS服务器IP地址大全
  2. 计算机制图怎么学,新手学电脑学习画图的方法
  3. 用计算机弹奏音乐的数字乐谱,抖音计算器音乐
  4. 教你如何爬取某哪儿网酒店数据
  5. 状态同步的mmo网络游戏中的帧率
  6. 60英寸的电视长宽是多少
  7. 【Unity3D日常开发】获取天气信息以及地方位置
  8. python爬取链家新房_Python爬虫项目--爬取链家热门城市新房
  9. 软考 - 软件设计师 - 下午-案例分析 做题技巧与考点整理
  10. php 截取逗号前字符串,php如何截取逗号之前的字符