这才是真正的万能圆角ImageView

2024-02-29 08:08

本文主要是介绍这才是真正的万能圆角ImageView,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文已授权我的公众号:我就是马云飞 独家发布

不知道有没有人记得我去年写过一个圆角的imageview。不知道的可以先去看看:万能圆角imagview,本文是基于上一篇的内容进行添加以及修改的。不然直接看这篇可能会有点懵。

前言

我为什么要二次封装?最近公司有个需求是这样的。
这里写图片描述

同事说,不知道怎么搞,于是乎,我把之前写的imageview给过去了。他来了句,你这圆角和fitxy同时设置会有问题啊,我反手就是一个大嘴巴子。我的代码怎么会有问题。于是,拿来一瞧,的确有点问题。So,我决定对这个imageview进行二次的封装。(当然了,这个问题的最后处理是后台直接给一个圆角的imageview)。

如何实现

细想一下,上文我们是怎么做的,我们是把绘制的区域,从(0,0)移动到我们想要的地方,说个粗暴点的话,我们强制的把这个imagview的scaletype的属性设置了centercrop。那么这次我们就要将它的scaletype设置成可调的属性。

实现逻辑

我前面也说过了,上次我们是根据imageview的源码来修改他的编辑区域的,这次,我们照常打开源码,找到园中对scaletype的处理逻辑,代码如下:

private void configureBounds() {if (mDrawable == null || !mHaveFrame) {return;}final int dwidth = mDrawableWidth;final int dheight = mDrawableHeight;final int vwidth = getWidth() - mPaddingLeft - mPaddingRight;final int vheight = getHeight() - mPaddingTop - mPaddingBottom;final boolean fits = (dwidth < 0 || vwidth == dwidth)&& (dheight < 0 || vheight == dheight);if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {/* If the drawable has no intrinsic size, or we're told toscaletofit, then we just fill our entire view.*/mDrawable.setBounds(0, 0, vwidth, vheight);mDrawMatrix = null;} else {// We need to do the scaling ourself, so have the drawable// use its native size.mDrawable.setBounds(0, 0, dwidth, dheight);if (ScaleType.MATRIX == mScaleType) {// Use the specified matrix as-is.if (mMatrix.isIdentity()) {mDrawMatrix = null;} else {mDrawMatrix = mMatrix;}} else if (fits) {// The bitmap fits exactly, no transform needed.mDrawMatrix = null;} else if (ScaleType.CENTER == mScaleType) {// Center bitmap in view, no scaling.mDrawMatrix = mMatrix;mDrawMatrix.setTranslate(Math.round((vwidth - dwidth) * 0.5f),Math.round((vheight - dheight) * 0.5f));} else if (ScaleType.CENTER_CROP == mScaleType) {mDrawMatrix = mMatrix;float scale;float dx = 0, dy = 0;if (dwidth * vheight > vwidth * dheight) {scale = (float) vheight / (float) dheight;dx = (vwidth - dwidth * scale) * 0.5f;} else {scale = (float) vwidth / (float) dwidth;dy = (vheight - dheight * scale) * 0.5f;}mDrawMatrix.setScale(scale, scale);mDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));} else if (ScaleType.CENTER_INSIDE == mScaleType) {mDrawMatrix = mMatrix;float scale;float dx;float dy;if (dwidth <= vwidth && dheight <= vheight) {scale = 1.0f;} else {scale = Math.min((float) vwidth / (float) dwidth,(float) vheight / (float) dheight);}dx = Math.round((vwidth - dwidth * scale) * 0.5f);dy = Math.round((vheight - dheight * scale) * 0.5f);mDrawMatrix.setScale(scale, scale);mDrawMatrix.postTranslate(dx, dy);} else {// Generate the required transform.mTempSrc.set(0, 0, dwidth, dheight);mTempDst.set(0, 0, vwidth, vheight);mDrawMatrix = mMatrix;mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));}}}

我们找到上次对其实位置修改的地方。跟着源码一样改成一样。改完之后代码如下:

if (drawablewidth <= 0 || drawableheight <= 0) {drawable.setBounds(0, 0, viewwidth, viewheight);matrix = null;} else {drawable.setBounds(0, 0, drawablewidth, drawableheight);if (ScaleType.MATRIX == getScaleType()) {if (matrix.isIdentity()) {matrix = null;}} else if (fits) {matrix = null;} else if (ScaleType.CENTER == getScaleType()) {matrix.setTranslate(Math.round((viewwidth - drawablewidth) * 0.5f),Math.round((viewheight - drawableheight) * 0.5f));} else if (ScaleType.CENTER_CROP == getScaleType()) {if (drawablewidth * viewheight > viewwidth * drawableheight) {dx = (viewwidth - drawablewidth * scale) * 0.5f;} else {dy = (viewheight - drawableheight * scale) * 0.5f;}matrix.setScale(scale, scale);matrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));} else if (ScaleType.CENTER_INSIDE == getScaleType()) {if (drawablewidth <= viewwidth && drawableheight <= viewheight) {scale = 1.0f;} else {scale = Math.min((float) viewwidth / (float) drawablewidth,(float) viewheight / (float) drawableheight);}dx = Math.round((viewwidth - drawablewidth * scale) * 0.5f);dy = Math.round((viewheight - drawableheight * scale) * 0.5f);matrix.setScale(scale, scale);matrix.postTranslate(dx, dy);} else {if (drawablewidth * viewheight > viewwidth * drawableheight) {dx = (viewwidth - drawablewidth * scale) * 0.5f;} else {dy = (viewheight - drawableheight * scale) * 0.5f;}matrix.setScale(scale, scale);matrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));}}if (ScaleType.FIT_XY == getScaleType() && matrix != null) {scale1 = viewwidth * 1.0f / drawablewidth;scale2 = viewheight * 1.0f / drawableheight;matrix.setScale(scale1, scale2);}bitmapShader.setLocalMatrix(matrix);paint.setShader(bitmapShader);

我们可以发现其实和源码对比下来,改动还是有的。为什么呢?我们仔细看下这段代码:

     if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {/* If the drawable has no intrinsic size, or we're told toscaletofit, then we just fill our entire view.*/mDrawable.setBounds(0, 0, vwidth, vheight);mDrawMatrix = null;}

源码里面只对drawable进行了处理。但我们可以发现后面的判断每次都会调用matrix.setScale这个方法。但在当scaletype为fitxy时,没有进行处理。我们也知道,我们看的是源码,他肯定偷偷的在某个地方进行了处理。那么我们要处理怎么办呢?仔细看代码:

        if (ScaleType.FIT_XY == getScaleType() && matrix != null) {scale1 = viewwidth * 1.0f / drawablewidth;scale2 = viewheight * 1.0f / drawableheight;matrix.setScale(scale1, scale2);}

fitxy我们都知道是充满布局,然后在细看这块代码,你是不是懂了呢?布局的宽高除以图片的宽高。然后设置它的比例。

加上边框

为了更好的封装,我选择加上边框和边框颜色的自定义属性。那么接下来就是直接上代码了。
我们需要再定义一个画笔:

        boder_paint.setAntiAlias(true);boder_paint.setStyle(Paint.Style.STROKE);boder_paint.setColor(border_color);boder_paint.setStrokeWidth(border_width);

接下来我们就是直接画上去了。当然了,我们这边默认是不设置,也就是borderwidth为0,所以我们要加一个判断:

  if (type == TYPE_ROUND) {canvas.drawRoundRect(rectF, borderRadius, borderRadius,paint);if (border_width > 0) {canvas.drawRoundRect(rectF, borderRadius, borderRadius, boder_paint);}} else if (type == TYPE_CIRCLE) {canvas.drawCircle(radius, radius, radius, paint);if (border_width > 0) {canvas.drawCircle(radius, radius, radius, boder_paint);}} else {getDrawable().draw(canvas);}

这里写图片描述
我们一编译,一运行,效果炸了。你问我为什么?我们先来看个效果在说把。
这里写图片描述
我们发现我们修改的fitxy属性已经生效了。但是,为什么加了边框是这样呢?
仔细想想。我们画圆角和圆的时候是不是忘记去掉了边框的宽度呢?那么我们既然找到了原因就可以找到解决方法了。那我们就直接去掉边框的高度,注意!!圆角和圆的都要处理。

   canvas.drawCircle(radius, radius, radius, paint);if (border_width > 0) {canvas.drawCircle(radius, radius, radius - border_width / 2, boder_paint);}
protected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);if (type == TYPE_ROUND) {rectF = new RectF(border_width/2,border_width/2, getWidth()- border_width / 2, getHeight()- border_width / 2);}}

我们再来看下效果:
这里写图片描述
搞定。完美~

使用

在gradle加上如下代码:

compile 'com.angel:SWImageView:1.0.0' 

关于自定义属性:

<declare-styleable name="SWImageView"><attr name="borderRadius" format="dimension"/><attr name="type"><enum name="normal" value="-1"/><enum name="circle" value="0"/><enum name="round" value="1"/></attr><attr name="borderWidth" format="dimension" /><attr name="borderColor" format="integer" /></declare-styleable>

我觉得命名很清晰明了。我就不介绍了。
项目我已上传到github:https://github.com/sw950729/SWImageView 喜欢的朋友随手点了star。谢谢。

这篇关于这才是真正的万能圆角ImageView的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/758093

相关文章

Imageview在百度地图中实现点击事件

1.首先第一步,需要声明的全局有关类的引用 private BMapManager mBMapMan; private MapView mMapView; private MapController mMapController; private RadioGroup radiogroup; private RadioButton normalview; private RadioBu

Python 中考虑 concurrent.futures 实现真正的并行计算

Python 中考虑 concurrent.futures 实现真正的并行计算 思考,如何将代码所要执行的计算任务划分成多个独立的部分并在各自的核心上面平行地运行。 Python 的全局解释器锁(global interpreter lock,GIL)导致没办法用线程来实现真正的并行​,所以先把这种方案排除掉。另一种常见的方案,是把那些对性能要求比较高的(performance-critica

不同饭局,如何说开场白才能打开氛围?教你一个万能公式

在人情社会中,饭局不仅是吃饱饭的场合,更是人际交往、情感交流的重要平台。无论是家庭聚会、商务宴请、朋友相聚还是同事联谊,一个恰当的开场白都能迅速打破沉默,营造温馨和谐的氛围。 针对现实生活中最常见的四种饭局,酱酒亮哥教你一个万能开场白公式,这个公式分为四步,当然,不是一步不落的照搬,需要灵活应用,挑其中的两步、三步就行了,只要打开氛围,我们的目的也就达到了。接下来我们一起学习一下,希望你在不同的

正面超越Spark | 几大特性垫定Flink1.12流计算领域真正大规模生产可用(下)

点击上方蓝色字体,选择“设为星标” 回复”资源“获取更多资源 我们书接上文,我们在之前的文章《正面超越Spark | 几大特性垫定Flink1.12流计算领域真正大规模生产可用(上)》详细描述了Flink的生产级别Flink on K8s高可用方案和DataStream API 对批执行模式的支持。 接下来是另外的几个特性增强。 第三个,Flink对SQL操作的全面支持 再很早之前,我在浏览社

经济不景气?相反,这才是普通人赚钱的绝佳机会!

“日子越来越难了!”身边类似的抱怨越来越多。 想想也是,这两年市场低迷、房地产暴雷、各行业内卷.....即便兜里有钱的,也面临资产缩水的风险。 但好在从去年开始,国内外AI企业黑马连出,AI文本、图片、视频生产模型直接颠覆了传统生产方式,创造了新的财富增长点。 趋势变了,从公司到普通个体的赚钱方式也该变了。 前不久,英伟达公布了自己2023的财报数据,毛利率达到了恐怖的72.7%,直接超过

Android中圆角Button实现

在android开发中,Button是使用很频繁的一种控件,而android提供的原生Button是很规矩的矩形外观,有时候缺乏美感,而相反,圆角按钮则可以提升美感。那么,我们如何设计实现出圆形按钮呢?     话不多说,请看实现! 在drawable目录下新建名称如“shape.xml”的文件 <pre class="html" name="code"><pr

互赖-真正成熟的关系

们从出生、长大到年老,无不生活在社会中、人际关系中。小的时候,爸爸妈妈无微不至地照顾着我们。那时,因为我们还小,不能自立,所以只能依赖大人;随着年龄渐大,大人们要我们学会自己面对生活和工作,于是我们学着去独立,并努力摆脱大人们的照顾。要做给他们看,也是要证明自己可以独立完成一切。于是,我们也为自己的独立而自豪。   真正走进工作岗位后,我们慢慢发现,有些独立的豪情不免会带来离群的尴尬,独立的

自动驾驶真正踏出迈向“用户”的第一步:IROS24新SOTA提出个性化的实例迁移模仿学习

导读: 本文针对自动驾驶规划任务,提出了一种基于实例的迁移模仿学习方法,通过预先训练的微调框架从专家域迁移专业知识,以解决用户域数据稀缺问题。实验结果显示,该方法能有效捕捉用户驾驶风格并实现具有竞争力的规划性能,但仍需开发合适的用户风格测量方法。©️【深蓝AI】编译 1. 摘要 个性化运动规划在自动驾驶领域中具有重要意义,可以满足个人用户的独特需求。然而,以往的工作在同时解决两个关键问题

递归算法专题——真正理解递归和正确使用递归力扣实战应用

目录 1、使用递归 1.1 如何理解递归 1.2 如何写好一个递归算法 2、 算法应用【leetcode】 2.1 题一:汉诺塔问题【面试题】  2.1.1 算法原理  2.1.2 算法代码 2.2 题二:合并两个有序链表 2.2.1 算法原理 2.2.2 算法代码  2.3 题三:反转链表 2.3.1 算法原理 2.3.2 算法代码  2.4 题四:两两交换链表中的节

Android中通过反射的方式判断U盘是否真正挂载

由于StorageManager.java类中的getVolumeList()和getVolumeState(String mountPoint)方法是hide(隐藏)的,所以需要通过反射的方式获取对应的存储信息。源码./frameworks/base/core/java/android/os/storage/StorageManager.java类中的getVolumeList()