Android:手把手教你自定义头像View,可根据名字自动生成背景色+文字的显示效果,含动画效果。

您所在的位置:网站首页 头像网名背景图 Android:手把手教你自定义头像View,可根据名字自动生成背景色+文字的显示效果,含动画效果。

Android:手把手教你自定义头像View,可根据名字自动生成背景色+文字的显示效果,含动画效果。

2024-07-15 10:43:33| 来源: 网络整理| 查看: 265

首先看需要做成的效果,如下所示😻:

请添加图片描述 请添加图片描述

如上所示:我们需要做到如下效果,在图片加载出来之前或者加载失败需要展示一个自定义的背景色+文字的样式,加载完成之后显示对应的图片,且包含动画效果。 由于:需要项目本身包含有:com.github.open-android:RoundedImageView:v1.0.0 库。 所以直接基于RoundedImageView来自定义HeadImageView,这样就不需要处理圆形了,只需要关注背景色、文字、动画以及可配置性就可以了。 现在就可以简单的,我们可以暂时写这么多:

class HeadImageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) : RoundedImageView(context, attrs, defStyle) { } 分析:应该怎么做🧐? 定义可配置的属性:我们生成的背景图由背景色和文字组成,另外是否显示自定义绘制的样式需要有一个开关,所以我们这里就定义:文字颜色、文字大小、背景色以及是否显示自定义的样式。绘制背景色绘制文字增加动画效果考虑全局可配置性扫尾处理(recyclerView图片错乱、glide加载动画去除等)

下面,就按照以上6步,简简单单的撸起来吧 😁

开动(为了看起来舒服一点,这里就不放注释了。最后会提供git源码demo)😉 1.定义可配置的属性👀

新建attrs.xml资源文件,增加对应的属性

在HeadImageView中,对属性进行解析,核心代码如下

@ColorInt var fontColor: Int = -1 set(value) { field = value invalidate() } @Px var fontSize: Float = 0.0F set(value) { field = value invalidate() } var isDefaultPic: Boolean = false set(value) { field = value invalidate() } @ColorInt private var backgroundFontColorInt: Int = -1 init { val attributeSet = context.obtainStyledAttributes(attrs, R.styleable.HeadImageView) fontColor = attributeSet.getColor( R.styleable.HeadImageView_font_color, ContextCompat.getColor(context, R.color.white) ) fontSize = attributeSet.getDimension(R.styleable.HeadImageView_font_size, 16F.spToPx) backgroundFontColorInt = attributeSet.getColor( R.styleable.HeadImageView_background_font_color, Color.parseColor("#2E6BE5") ) isDefaultPic = attributeSet.getBoolean(R.styleable.HeadImageView_is_default_pic, false) attributeSet.recycle() } 其他的初始化操作可自行查看源码噢,比如paint、动画控制相关等,这里就不一一列举了🐷。 2.绘制背景色👀

我们需要考虑圆形以及正方形不同的绘制 需要判断当前是否是圆形,根据RoundedImageView已经提供的方法isOval()来进行判断

override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) drawDefaultPic(canvas!!) } private fun drawDefaultPic(canvas: Canvas) { mPaint.alpha = 255 mPaint.color = backgroundFontColorInt if (isOval) { canvas.drawCircle(width / 2F, height / 2F, width / 2F, mPaint) } else { canvas.drawRect(0F, 0F, width.toFloat(), height.toFloat(), mPaint) } } 3.绘制文字👀

这里我需要关注的是,怎么样让文字居中显示绘制 先看一张图🤩。 在这里插入图片描述 上面的参数很多是吧,是不是感觉有点懵😫,不慌,只需要关注这几个参数: base:字符基线,这个就比如我们规定,在一个100*100的空间里,在点(50,50)的位置绘制abcdefg,那么abcdef这几个字母全在横坐标50的上方,而g则上半部分在上方,下半部分在下方。因为绘制坐标是基于base来的。 descent:字符最低点到base的推荐距离 ascent:字符最高点到base的推荐距离 bottom:字符最低点到base的最大距离

以上便是需要知道的参数,所以怎么保证,字体绘制在最中间呢? 水平居中 定义一个矩形,设置文字对其方式设置为居中对齐,然后设置x轴坐标为矩形的中心,那水平方向就居中了。 垂直居中 垂直的需要计算出从base到垂直水平点的偏移量,那么偏移量怎么计算呢? descent - ascent 就是字体整体推荐的高度,除以2就是推荐高度的一半。考虑还有字体最低点,可以减去bottom 核心实现代码如下😋

private fun onDrawFont(canvas: Canvas) { val rect = Rect(0, 0, width, height) mPaint.color = fontColor mPaint.textSize = fontSize mPaint.textAlign = Paint.Align.CENTER val baseLine = mPaint.fontMetrics.let { val distance = (it.descent - it.ascent) / 2 - it.bottom rect.centerY() + distance } canvas.drawText(displayText, rect.centerX().toFloat(), baseLine, mPaint) } 其实这个时候基本绘制就完成了,加上控制开关的话,就可以使用了😄。 if (isDefaultPic) { drawDefaultPic(canvas) } em,这样的话基本就可以使用了,如果不需要动画等其他的效果,上面的代码粘贴粘贴基本就可以了。 4.增加动画效果👀 这怎么做到呢?

思考一下,到可以使用属性动画+离屏绘制+图层混合完成。 属性动画:可以简单的理解为,动画间隔时间内不断的更改对应属性的值,重绘。实现动画效果。 离屏绘制:简单理解使用全新的canvas,绘制完成后,盖在了原来的canvas上面。 图层混合:将所绘制的像素与canvas中对应位置的像素按照一定规则进行混合,形成新的像素值,最终更新canvas中最终显示的像素值。

这几个概念呢?如果有不清楚的,可以简单Google了解一下哈,这里不做引申了,直接看怎么使用吧😋。

大致的实现原理呢😃? 首先背景色加文字是通过canvas直接绘制出来,而图层混合有一个模式可以让绘制的交际变透明。就需要在绘制一块,而这一块和上面的绘制变成交际,就会透明了,实际的图像就会显示。自定义一个属性通过属性动画不断变化,然后达到控制第二块绘制区域的效果,实现透明区域的缩放。以上操作均在离屏绘制中完成。考虑到圆形和正方形两种情况,所以针对这两种情况分别做了不同的绘制。

下面看看绘制的代码核心实现吧😍。

//离屏绘制 val saveLayerId = canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), mPaint) //过渡渐变 //dst mPaint.color = backgroundFontColorInt if (isOval) { canvas.drawCircle(width / 2F, height / 2F, width / 2F, mPaint) } else { canvas.drawRect(0F, 0F, width.toFloat(), height.toFloat(), mPaint) } //绘制字体 onDrawFont(canvas) //设置图层混合模式 DST_OUT 绘制交集部分变透明 mPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OUT) //src mPaint.color = ContextCompat.getColor(AppUtil.application, R.color.white) if (isOval) { canvas.drawCircle(width / 2F, height / 2F, (width / 2) * (1 - animationControl), mPaint) } else { canvas.drawRect( (width / 2F) * animationControl, (height / 2F) * animationControl, (width / 2F) + ((1 - animationControl) * (width / 2F)), (height / 2F) + ((1 - animationControl) * (height / 2F)), mPaint ) } //图层混合取消 mPaint.xfermode = null canvas.restoreToCount(saveLayerId)

下面在看看动画的代码核心实现吧😍。因为把动画放到了,view里面所以使用软引用持有(内存不够,大不了就被回收了嘛,非必须)。

/** * 配合属性动画处理image默认的转场动画 值在 0-1 之间 */ var animationControl: Float = 1F set(value) { field = if (animationControl 1) 1F else value invalidate() } private var objectAnimatorSoft: SoftReference? = null if (objectAnimatorSoft == null) { objectAnimatorSoft = SoftReference(ObjectAnimator.ofFloat(this, "animationControl", 1F, 0F)) } objectAnimatorSoft?.get()?.let { it.duration = animatorTime it.addListener(object : Animator.AnimatorListener { override fun onAnimationStart(animation: Animator?) { } override fun onAnimationEnd(animation: Animator?) { this@HeadImageView?.isDefaultPic = false } override fun onAnimationCancel(animation: Animator?) { this@HeadImageView?.isDefaultPic = false } override fun onAnimationRepeat(animation: Animator?) { } }) }

提供一个方法等图片加载出来后,取消默认图的显示。isDefaultPic 的set里面调用了 invalidate() 所以会直接重绘了。

fun cancelDefaultPic() { if (isDefaultPic) { //如果绘制的是drawable则没有动画效果,直接设置isDefaultPic重新绘制即可 if (mBackgroundDrawable != null) { isDefaultPic = false return } if (enableAnim) { //启用动画 objectAnimatorSoft?.get()?.start() } else { //不启用动画 isDefaultPic = false } } } 好像就,差不多了。。。 下面进行其他的扫尾工作 5.考虑全局可配置性👀 上面的颜色以及对应的显示字体都是根据content自动生成的,所以需要一个默认的算法,同时也允许提供定制算法。 上面的默认图是背景色+文字。也有可能是图片呀 , 怎么破😃 可自定设置动画时常、判断启用动画 综上:我们需要额外提供一个object类,用来注册这些东西。太细节就不说了,简单展示一下默认提供的计算字体和背景色的方法以及默认图是图片的问题。 背景色计算😃 private fun defaultObtainImageFontBackgroundColorInt(name: String): Int { val bgColors = intArrayOf( Color.parseColor("#FA7976"), Color.parseColor("#B7A0F1"), Color.parseColor("#6890F3"), Color.parseColor("#57BAB3"), Color.parseColor("#61C7F1"), Color.parseColor("#FAA77D") ) return name.toByteArray().fold(0) { acc: Int, byte: Byte -> acc + byte }.let { bgColors[it.absoluteValue % bgColors.size] } } 取内容的后两个字显示😃 private fun defaultObtainImageFontText(name: String): String { val length = name.length return if (length > 2) { name.substring(length - 2, length) } else { name } } 至于显示默认背景为图片就更简单了,直接在object中防止一个Drawable,绘制的实现先从object中取,取不到的则继续正常的背景绘制,不然就直接绘制drawable就好了🙂。 //如果drawable不为null直接绘制drawable否则绘制默认背景图 if (mBackgroundDrawable != null) { mBackgroundDrawable?.draw(canvas) return } drawDefaultPic(canvas) 6.扫尾处理(recyclerView图片错乱、glide加载动画去除等)👀 写工具方法加载图片了,这里使用glide进行网络图片的加载。 设置tag防止图片错乱 使用自定义ViewTarget,手动设置图片,去除动画 /** * 加载图片 */ fun HeadImageView.loadImage(url: String, name: String) { val text = HeadImageViewHelp.obtainImageFontText(name) val colorInt = HeadImageViewHelp.obtainImageFontBackgroundColorInt(name) val tag = getTag(R.id.imageload_tag) if (tag == null || !TextUtils.equals(tag.toString(), url)) { //显示默认图片 setBackgroundFontColorAndText(colorInt, text) } Glide.with(this).load(url).into(ImageViewTarget(this)) } /** * 自定义ImageViewTarget设置图片 */ class ImageViewTarget(ivPic: HeadImageView) : CustomViewTarget(ivPic) { override fun onLoadFailed(errorDrawable: Drawable?) { } override fun onResourceReady(resource: Drawable, transition: Transition?) { this.view.setImageDrawable(resource) //默认取消文字显示 this.view.cancelDefaultPic() } override fun onResourceCleared(placeholder: Drawable?) { } } 至此:基本就完成了,使用的话只需要调用loadImage方法就可以了。

源码地址:觉得不错给个star吧!🧐 https://github.com/zhangnangua/grocery-store/tree/master/HeadImageViewDemo



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭