简介

  • 在WanAndroid项目中涉及到用户头像的生成,需求有以下几点:

    • 仿照探照灯效果
    • 头像含有用户姓名

复习View绘制流程

Android窗口机制

  • PhoneWindow:继承自Window类,负责管理界面显示以及事件响应,每个Activity 界面都包含一个PhoneWindow对象,它是Activity和整个View系统交互的接口。

  • DecorView:是PhoneWindow中的起始节点View,继承自View类,作为整个视图容器来使用的,主要负责设置窗口属性。

  • ViewRoot:在系统启动一个Activty组件的同时将其创建,类似于MVC模型中的Controller,负责管理、布局和渲染窗口UI等事务。

View绘制准备

  • 系统启动一个Activity的同时创建一个ViewRoot实例。Activity在attach阶段生成一个PhoneWindow对象,它包含一个DecorView对象。在Activity执行onCreate中的setContentView之后,将设置进来的view加载进入ContentViews区域。之后触发ViewRoot中的scheduleTraversals异步函数,从而进入ViewRoot的performTraversals函数,View的绘制从这里开始。
View绘制流程

  • View 的绘制流程是从ViewRoot 的 performTraversals 方法开始,它以DecorView为父容器开始自上而下的进行View绘制工作,它经过 measure 、 layout 和 draw 三个过程才能最终将一个 View 绘制出来,其中 measure 用来测量 View 的宽和高,layout 用来确定 View 在父容器中的放置位置,而 draw 则负责将 View 绘制在屏幕上
  • 注意
    • Measure 过程决定了 View 的宽/高, Measure 完成以后可以通过 getMeasuredWidthgetMeasuredHeight 方法来获取到 View 测量后的宽/高,在几乎所有的情况下它都等同于 View 最终的宽/高,但是特殊情况除外。
    • Layout 过程 决定了 View 的四个顶点的坐标和实际的 View 的宽/高,完成以后可以通过getTop、getBottom、getLeft、getRight 来拿到 View 的四个顶点的位置,并可以通过getWidth 和 getHeight 方法拿到 View 最终的宽/高。
    • Draw 过程则决定了 View 的显示,只有 draw 方法完成以后 View 的内容才能呈现在屏幕上。

探照灯效果

  • 我采用的是BlurMaskFilter发光效果结合ShadowLayer阴影效果实现

  • 具体两种效果可以参考

    • BlurMaskFilter发光效果
    • ShadowLayer阴影效果
  • 效果图如下

具体实现

发光效果
  • 绘制发光效果难度不大直接上代码
// 绘制发光效果
int color = getColor(R.color.always_white_text);
mPaintBackground.setColor(color);
mPaintBackground.setStyle(Paint.Style.FILL);
mPaintBackground.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.SOLID));
canvas.drawCircle(getWidth() / 2, getWidth() / 2, (getWidth() - 20) / 2, mPaintBackground);

绘制头像文本

难点:垂直居中
  • 首先文字的绘制 参考【绘图入门】

  • 在Android中绘制文字采用基线的方式,同时系统还会利用四条辅助线来辅助绘制文字,具体释义如下 :

  • 注意:

    • 蓝线:基线
    • 红线:ascent线
    • 绿线:descent线
    • 黄线:top线
    • 黑线:bottom线
ascent:系统推荐的,在绘制单个字符的时,字符应当的最高高度所在线
descent:系统推荐的,在绘制单个字符时,字符应当的最低高度所在线
top:可绘制的最高高度所在线
bottom:可绘制的最低高度所在线我们就尽量将文字绘制在系统推荐的最高和最低的限度之内,这样在不同的屏幕的手机上,文字都可以完整的展现出来// 通过如下的方式计算出显示文字的区域
ascent线y坐标 = baseline线y坐标 + fontMetric.ascent
descent线 y坐标 = baseline线的y坐标 + fontMetric.descent
top线y坐标 = baseline线y坐标 + fontMetric.top
bottom线的y坐标 = baseline线y坐标 + fontMetric.bottom
  • 明白了如何绘制就来绘制垂直居中的文字吧
  • 公式:
 int baseLine = getHeight() / 2 - fontMetricsInt.descent  + (fontMetricsInt.bottom - fontMetricsInt.top) / 2;
对公式的理解
  • getHeight() / 2 - fontMetricsInt.descent

    • 将文本的bottom线抬高至控件的1/2处,效果图及数据如下
getHeight() / 2: 270
baseLine:       252
ascent:        185.20312
descent:      269.57812
top:        175.95703
bottom:     271.51172getHeight () /2 约等于desect 也约等于bottom, 即控件向上抬至控件1/2处
  • getHeight() / 2 - fontMetricsInt.descent + (fontMetricsInt.bottom - fontMetricsInt.top) / 2

    • 即将文字绘制在控件中央,文本的辅助线(top+bottom)/2就是文本的中位线(我是这样理解的)恰好在控件中位线处,即垂直居中。效果图及数据如下
getHeight() / 2:  270
baseLine:          300
ascent:           233.20312
descent:           317.57812
top:              223.95703
bottom:          319.51172getHeight() / 2 - fontMetricsInt.descent  +
(fontMetricsInt.bottom - fontMetricsInt.top) / 2;270 约等于现在的(top+bottom)/2, 即将文字绘制在控件中央
完整代码
@SuppressLint("AppCompatCustomView")
public class CustomUserAvatar extends ImageView {private Paint mPaintText;private Paint mPaintBackground;private Rect mRect;private String mUserName;public CustomUserAvatar(Context context) {super(context);init();}public CustomUserAvatar(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}public CustomUserAvatar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {setLayerType(LAYER_TYPE_SOFTWARE, null);mPaintText = new Paint(Paint.ANTI_ALIAS_FLAG);mPaintBackground = new Paint(Paint.ANTI_ALIAS_FLAG);mPaintBound = new Paint(Paint.ANTI_ALIAS_FLAG);mRect = new Rect();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, widthMeasureSpec);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// 绘制发光效果int color = getColor(R.color.always_white_text);mPaintBackground.setColor(color);mPaintBackground.setStyle(Paint.Style.FILL);mPaintBackground.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.SOLID));canvas.drawCircle(getWidth() / 2, getWidth() / 2, (getWidth() - 20) / 2, mPaintBackground);// 设置文本大小mPaintText.setTextSize(getWidth() / 3);// 设置文本颜色跟随应用主题颜色mPaintText.setColor(Constant.getColor(getContext()));// 设置画笔粗细mPaintText.setStrokeWidth(5);// 设置阴影半径mPaintText.setShadowLayer(5, 5, 5, getColor(R.color.black));// 绘制文字的最小矩形mPaintText.getTextBounds(mUserName, 0, 1, mRect);Paint.FontMetricsInt fontMetricsInt = mPaintText.getFontMetricsInt();// baseLine上面是负值,下面是正值// 所以getHeight()/2-fontMetricsInt.descent 将文本的bottom线抬高至控件的1/2处// + (fontMetricsInt.bottom - fontMetricsInt.top) / 2:(fontMetricsInt.bottom - fontMetricsInt.top) 文本的辅助线(top+bottom)/2就是文本的中位线(我是这样理解的)恰好在控件中位线处int baseLine = getHeight() / 2 - fontMetricsInt.descent + (fontMetricsInt.bottom - fontMetricsInt.top) / 2;// 水平居中mPaintText.setTextAlign(Paint.Align.CENTER);canvas.drawText(mUserName, getWidth() / 2, baseLine, mPaintText);}/*** 判断一个字符是否是中文*/public boolean isChineseChar(char c) {// 根据字节码判断return c >= 0x4E00 && c <= 0x9FA5;}/*** 判断一个字符串是否含有中文** @param str* @return*/public boolean isChineseString(String str) {if (str == null) {return false;}for (char c : str.toCharArray()) {if (isChineseChar(c)) {return true;}}return false;}/*** 设置显示的名字** @param userName*/public void setUserName(String userName) {// 中文名字取后两个if (isChineseString(userName)) {if (userName.length() > 2) {mUserName = userName.substring(userName.length() - 2, userName.length());} else {mUserName = userName;}} else {// 非中文名字取第一个if (userName.length() > 1) {mUserName = userName.substring(0, 1);mUserName = mUserName.toUpperCase();} else {mUserName = userName;mUserName = mUserName.toUpperCase();}}invalidate();}
}

添加使用依赖

allprojects {repositories {...maven { url 'https://jitpack.io' }}
}

引入项目

dependencies {implementation 'com.github.wangjianxiandev:CircularAvatar:v1.0.0'
}

使用方法

<com.wjx.android.lib_circularavatar.CustomUserAvatarandroid:id="@+id/avatar_one"android:layout_width="110dp"android:layout_height="110dp"<!--设置头像背景色-->app:background_color="@color/background_color"<!--设置填充样式 0-不填充 1-填充-->app:background_style="0"<!--设置字体颜色-->app:text_color="@color/black"<!--设置中文字符显示数目-->app:chinese_name="1"<!--设置英文字符显示数目-->app:english_name="2"<!--设置是否具有发光效果-->app:show_blur_Mask="false" />

展示

Android自定义姓名头像相关推荐

  1. android 自定义圆形头像,android自定义圆形头像

    这几天看了项目框架里面的圆形头像,发现其实这个东西并不是很难的东西,学会了原理,无论圆形头像,五角星头像都可以实现. 目前我上传的Demo里用了两种实现方式,那么我们分别来讲讲这两种实现方式: Bit ...

  2. android人脸识显示头像自定义,Android 仿QQ头像自定义截取功能

    看了Android版QQ的自定义头像功能,决定自己实现,随便熟悉下android绘制和图片处理这一块的知识. 先看看效果: 思路分析: 这个效果可以用两个View来完成,上层View是一个遮盖物,绘制 ...

  3. Android自定义拍照上传界面,Android自定义dialog——设置头像(拍照,相册)

    Android自定义dialog--设置头像(拍照,相册) 需求场景:个人信息设置,点击头像,在界面上弹出一个弹框,用户选择"拍照"/"从图库选择",选择照片后 ...

  4. android自定义view获取控件,android 自定义控件View在Activity中使用findByViewId得到结果为null...

    转载:http://blog.csdn.net/xiabing082/article/details/48781489 1.  大家常常自定义view,,然后在xml 中添加该view 组件..如果在 ...

  5. Android 自定义表格显示数据

    Android 自定义TextView控件,用来组成表格方便数据的展示. 首先看一下效果 样式不是很好看,需要用的可以自己优化一下. 实现方式很简单. 1.自定义控件 MyTableTextView ...

  6. android自定义带进度条的圆形图片

    前言:在项目听新闻的改版中需要实现环绕圆形新闻图片的进度条功能,作为技术预备工作我就去看了一些网上的相关的原理,做了一个自定义带进度条的圆形图片的demo,并将这个实现写成文章发布出来,谁需要了可以进 ...

  7. Android技术分享| 【Android 自定义View】多人视频通话控件

    [Android 自定义View]多人视频通话控件 *以上图片截自微信等待中界面 等待中界面 上图是微信多人视频通话时未接通的界面状态,可见每个人的 View 中大致需包含了以下元素. 头像 昵称 L ...

  8. android 自定义特效,Android自定义View之高仿QQ健康

    我们都知道自定义View一般有三种直接继承View.继承原有的控件对控件的进行修改.重新拼装组合,最后一种主要针对于ViewGroup.具体的怎么做不是本文的所涉及的内容(本文是基于第一种方式实现的) ...

  9. Android自定义ViewGroup实现朋友圈九宫格控件

    在我们的实际应用中,经常需要用到自定义控件,比如自定义圆形头像,自定义计步器等等,这篇文章主要给大家介绍了关于Android自定义ViewGroup实现朋友圈九宫格控件的相关资料,需要的朋友可以参考下 ...

最新文章

  1. innodb force recovery
  2. 高精度运算(C++实现)
  3. 网络宣传推广教大家网站的过期页面更合理的处理方法
  4. POJ 3320 Jessica's Reading Problem (尺取)
  5. Linux的关机与重启命令
  6. 禁忌搜索算法求解带时间窗的车辆路径问题原理讲解
  7. secureCRT修改鼠标颜色
  8. Firefox 66 将阻止自动播放音频和视频
  9. 参数化的RBAC模型
  10. elementUI 分页组件的使用 - 踩坑篇
  11. 开会坐在后面,意思是不感兴趣
  12. Excel常用10个函数
  13. QGis二次开发:预览几何图形,QgsRubberBand的应用
  14. 坚果pro2s android 8,坚果Pro2s和iPhone8手机对比实用评测
  15. AES解密报错,Input length must be multiple of 16 when decrypting with padded cipher
  16. Ubuntu 18.04 锁屏 快捷键 无效
  17. 二级计算机的office用到哪些函数,计算机二级MS office excel中所用函数整理
  18. LWIP应用开发|心跳机制
  19. 关于计算机与教育的英语作文,信息技术对教育的影响英文作文
  20. windows启动引导管理

热门文章

  1. Java高级容器——集合
  2. 快手内容运营技巧,快手上热门秘籍分享;国仁楠哥
  3. kubernetes-二进制安装,亲测无坑,在参考安装过程中如果有什么问题欢迎交流,超级详细的文档
  4. C#导出数据到excel表格
  5. 孤岛惊魂3-孤岛惊魂3-武器射速修改-一枪100发
  6. NXUOJ(ds二次考试编程复现) A2:干饭1
  7. button与验证控件的矛盾
  8. What is Deinterlacing--小引
  9. 哞哞快的 C# 高斯模糊实现
  10. [IMX6DL][Android4.4] 超声波模块KS103 Linux驱动源代码