探一探,非常实用的GIF图圆角控件(3行代码)
前言
现代人的崩溃是一种默不吭声的崩溃,看起来很正常,会说笑,会打闹,会社交,表面平静,实际上心里的糟心事已经积累到一定程度了。不会摔门砸东西,不会流眼泪或歇斯心底,但可能某一秒突然就积累到极致,也不说话,也不真的崩溃,也不太想活着。也不敢去死。
这里引用了「张帅B」的一段话,说的很贴切,我们这一代年轻人到底怎么了?
正文
先来看看效果图:
初步分析
众所周知,Android 中主流的图片加载框架有 Picasso,Glide,Fresco。Picasso 加载 gif 图没有动画效果,Glide 与 Fresco 支持 gif 动效图。Fresco 自带的控件 SimpleDraweeView 支持圆角属性,Glide 需要手动给 ImageView 设置 shape,那么 Glide,Fresco 是否支持 gif 图圆角?
先来看一个例子,Glide 加载 gif 圆角,先看看 xml 布局:
<ImageViewandroid:id="@+id/iv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="64dp"android:background="@drawable/corners_bg"android:src="@mipmap/gif_01"/>
圆角文件 corners_bg :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"><corners android:radius="8dp"></corners>
</shape>
Glide 加载动图:
Glide.with(this).load(R.mipmap.gif_01).asGif().override(720, 512).into(mImageView);
效果图:
发现 gif 图并没有显示圆角,经过测试 Fresco 的圆角属性,在 gif 图上也会失效。那么我们怎么才能让 gif 图显示圆角呢?
大家都知道 gif 是由多张静态图组合而成,如果处理单张图片效率将会极其低下,既然不能对源图片进行处理,那么就只能在显示控件上想办法了。
经过初步分析有以下三种可行方案:
- 裁剪图片控件
- 在 gif 图片控件上覆盖一层圆角图片(4角圆角中间透明)
- Path 的填充样式 FillType
方案一裁剪控件,改变图片显示区域,但裁剪的效率并不高,顾排除方案一;方案二,ui 切圆角图片,如果在换肤的情况下,ui 需要切多套圆角图片,可扩展性太差,同时覆盖的圆角图片加载会消耗性能,排除方案二;那就只有第三方案了,在性能与可扩展性方面优于前两种方案。
Path填充样式FillType
/*** Set the path's fill type. This defines how "inside" is computed.** @param ft The new fill type for this path*/public void setFillType(FillType ft) {// 调用 c 层的 jni 方法}
设置路径的填充样式,定义了 “内部” 是如何计算的。 参数 ft
是个枚举值,有 4 种类型:
/*** Enum for the ways a path may be filled.*/public enum FillType {// these must match the values in SkPath.h/*** Specifies that "inside" is computed by a non-zero sum of signed* edge crossings.*/WINDING (0),/*** Specifies that "inside" is computed by an odd number of edge* crossings.*/EVEN_ODD (1),/*** Same as {@link #WINDING}, but draws outside of the path, rather than inside.*/INVERSE_WINDING (2),/*** Same as {@link #EVEN_ODD}, but draws outside of the path, rather than inside.*/INVERSE_EVEN_ODD(3);}
WINDING (0)
默认值是 WINDING (0) ,我们一起来探究下这几个值产生的效果,先来看看下面这段代码:
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.save();mPath.addCircle(600, 400, 200, Path.Direction.CW);mPath.addCircle(900, 400, 200, Path.Direction.CW);canvas.drawPath(mPath, mPaint);canvas.restore();}
绘制两相交圆,并两圆的方向都是顺时针绘制。效果图是这样的:
上图是两圆都是顺时针的方向,改变一圆的绘制方向为逆时针:
mPath.addCircle(600, 400, 200, Path.Direction.CW);mPath.addCircle(900, 400, 200, Path.Direction.CCW);
效果图如下:
WINDING 的原理:
其实WINDING表示非零环绕原则,从任意一点发射一条线,默认值是 0,遇到顺时针交点则 +1,遇到逆时针交点则 -1,最终如果不等于 0,则认为这个点是图形内部的点,则需要绘制颜色;反之,如果这个值是 0,则认为这个点不在图形内部,则不需要绘制颜色。
正好解释上图相交的部分没有绘制颜色,相交的部分首先是绘制顺时针方向的圆 +1 ,然后绘制逆时针方向的圆 -1 ,正好等于 0,不需要绘制颜色。
EVEN_ODD (1)
这个值和 WINDING 不同,WINDING 要求每个图形都是有方向的。EVEN_ODD 并不要求圆的方向,看看下面一段代码:
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.save();mPath.setFillType(Path.FillType.EVEN_ODD);// 改成 Path.Direction.CCW 的效果一样mPath.addCircle(600, 400, 200, Path.Direction.CW);mPath.addCircle(900, 400, 200, Path.Direction.CW);canvas.drawPath(mPath, mPaint);canvas.restore();}
效果图如下:
EVEN_ODD 原理:
英文单词中 EVEN 是偶数,ODD 是奇数的意思。这个原则也被称为奇偶原则。从任意一点射出一条线,与图形的交线是奇数,则认为这个点在图形内部,需要绘制颜色;反之如果是偶数,则认为这个点在图形外部,不需要绘制颜色。
INVERSE_WINDING (2)
inverse 表示反转的意思,相同的一段代码,绘制出来的结果是相反的。如下代码:
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.save();mPath.setFillType(Path.FillType.INVERSE_WINDING);mPath.addCircle(600, 400, 200, Path.Direction.CW);mPath.addCircle(900, 400, 200, Path.Direction.CW);canvas.drawPath(mPath, mPaint);canvas.restore();}
圆的区域都不需要绘制颜色,非圆区域绘制颜色。效果图如下:
设置一圆的绘制方向为逆时针:
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.save();mPath.setFillType(Path.FillType.INVERSE_WINDING);mPath.addCircle(600, 400, 200, Path.Direction.CW);mPath.addCircle(900, 400, 200, Path.Direction.CCW);canvas.drawPath(mPath, mPaint);canvas.restore();}
两圆相交的部分需要绘制颜色,非圆区域绘制颜色。效果图一览:
INVERSE_EVEN_ODD(3)
INVERSE_EVEN_ODD 模式与 INVERSE_WINDING 模式的不同的绘制方向效果一样,代码如下:
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.save();mPath.setFillType(Path.FillType.INVERSE_EVEN_ODD);mPath.addCircle(600, 400, 200, Path.Direction.CW);mPath.addCircle(900, 400, 200, Path.Direction.CCW);canvas.drawPath(mPath, mPaint);canvas.restore();}
效果图:
相信各位小伙伴们看到这里,心里很清楚了 gif 圆角控件的填充样式为以下两种情况:
- INVERSE_WINDING 样式,逆时针绘制圆角矩形
- INVERSE_EVEN_ODD 样式
编写代码
通过上文原理的介绍,gif 图圆角控件的代码就非常简单。
起名字
接地气的名字,能够让人眼前一亮,就叫 CornersGifView吧。
CornersGifView圆角控件
核心代码如下(3 行代码):
mPath.reset();// add round rectmPath.setFillType(Path.FillType.INVERSE_EVEN_ODD);mPath.addRoundRect(new RectF(0, 0, w, h), mCorners, Path.Direction.CCW);
相关参数:
- w 表示控件的宽度
- h 表示控件的高度
- mCorners 圆角数组(大小为 8,一组圆角包含长宽两个参数,4 组圆角顾 8 个参数)
- 绘制方向 Path.Direction.CCW 与 Path.Direction.CW 效果一致
大家可能已经注意到了,非圆角矩形区域的颜色,为 mPaint 画笔的颜色,那么就需要保证画笔的颜色与父控件的背景颜色一致,如果父控件颜色为透明,那么就需要取父控件的父控件颜色,以此类推,递归获取父控件的颜色,请参考以下两个方法:
/*** @param vp parent view* @return paint color*/private int getPaintColor(ViewParent vp) {if (null == vp) {return Color.TRANSPARENT;}if (vp instanceof View) {View parentView = (View) vp;int color = getViewBackgroundColor(parentView);if (Color.TRANSPARENT != color) {return color;} else {getPaintColor(parentView.getParent());}}return Color.TRANSPARENT;}
通过反射获取 View 的背景颜色值:
/*** @param view* @return*/private int getViewBackgroundColor(View view) {Drawable drawable = view.getBackground();if (null != drawable) {Class<Drawable> drawableClass = (Class<Drawable>) drawable.getClass();if (null == drawableClass) {return Color.TRANSPARENT;}try {Field field = drawableClass.getDeclaredField("mColorState");field.setAccessible(true);Object colorState = field.get(drawable);Class colorStateClass = colorState.getClass();Field colorStateField = colorStateClass.getDeclaredField("mUseColor");colorStateField.setAccessible(true);int viewColor = (int) colorStateField.get(colorState);if (Color.TRANSPARENT != viewColor) {return viewColor;}} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}return Color.TRANSPARENT;}
不仅支持 gif 图圆角,还支持静态图圆角,最终效果图如下:
引起的思考,FillType 能够支持圆角矩形,那么是不是还可以支持圆形,椭圆,任意的 Path 呢?答案是肯定的,那么又改怎么去实现?
这里以扩展圆形为例:
新建 Path 先逆时针绘制控件大小的矩形,再绘制圆形(以控件中心为圆点),接着设置 FillType 为 WINDING ,最后添加路径到原始路径上 addPath ,代码就像这样:
private void addCirclePath() {int w = getWidth();int h = getHeight();Path addPath = new Path();addPath.addRect(new RectF(0, 0, w, h), Path.Direction.CCW);addPath.addCircle(w / 2, h / 2, Math.min(w, h) / 2, Path.Direction.CW);setPath(addPath);}
private void setPath(Path path) {mPath.reset();mPath.setFillType(Path.FillType.WINDING);mPath.addPath(path);invalidate();}
效果图如下:
结束语
「张帅B」的那段话,概述的非常经典。都说 80 后的在忙着挣钱;00 后在忙着谈恋爱;只有我们 90 的在忙着赚钱又谈恋爱,最后钱没挣到,恋爱也没谈到。
最后再啰嗦一点,生活都不容易,一直坚持做一件事情更不容易,还希望各位朋友,能够多多支持,小编新开的公众号「控件人生」,有你们的相伴,才有写下去的动力。
扫一扫 关注我的公众号 与小伙伴们一同成长~
源码地址:
https://github.com/HpWens/MeiWidgetView
探一探,非常实用的GIF图圆角控件(3行代码)相关推荐
- 分享一个WM上绘制饼图、柱形图、折线图的控件类
最近工作中需要在Windows Mobile上实现统计图表绘制,如饼图.柱形图.折线图等,虽然要求的形式比较简单,但是不允许用第三方或开源的控件类库,只好自己动手写了一个简单的.初步实现了饼图.柱形图 ...
- 云炬Android开发笔记 15评价晒单功能实现(自定义评分控件和仿微信自动多图选择控件)
阅读目录 1. 晒单评价 1.1 点击页面跳转的实现 1.2 自定义评价订单的布局实现 1.3 星星布局的实现 2. 仿微信自动多图及删除控件 2.1 属性值及控件的定义 2.2 图片初始化方法onM ...
- Matlab App Designer自学笔记(九):容器及图窗控件
一.容器 容器,顾名思义,就是装东西的.Matlab里的容器有面板和选项卡组两个(2018a版本). 1.1 卡组 选项卡组就是分栏使用,下图这个就是: 例如在画布里设置这么两个卡组(可以点击+增加) ...
- MFC/WTL 设置背景图和控件透明的方法
1.MFC 对话框设置背景图 方法一: 1>资源中导入一张bitmap图,如命名为 IDB_BITMAP1 2>对话框中添加一个 Picture Control,属性设置 Type 改为B ...
- 可以直接拖拽编辑的雷达图、蛛网图网页控件
这是我 2019 年开发的一个网页小控件,可以通过鼠标点选拖拽多边形的顶点对蛛网图或者雷达图进行编辑操作,并可以将编辑结果实时发回给服务端,直观方便. 开源地址 https://github.com/ ...
- Android自定义控件之轮播图控件
背景 最近要做一个轮播图的效果,网上看了几篇文章,基本上都能找到实现,效果还挺不错,但是在写的时候感觉每次都要单独去重新在Activity里写一堆代码.于是自己封装了一下.这里只是做了下封装成一个控件 ...
- Android 实用开源控件
图片放大缩小: PinchImageView 体验最好的图片手势控件,不同分辨率无缝切换,可与ViewPager结合使用. GestureViews 带有手势控制的ImageView和FrameLay ...
- 过年回家,走之前留一个用GDI+实现的略缩图控件
这是一个加载文件夹图片略缩图的控件,支持多种图片格式~~用法也比较简单 (1).源代码 //头文件ListImageCtrl.h #pragma once #include <vector> ...
- 实用的签到、日程表日历控件(可扩展)
当初写这个控件主要是为了每日签到.当然你也可以改成事件(日程表)的日历控件,所有代码和样式都会分享给你. javascript代码极其简单,详细请下载demo文件(最下方有下载链接). 其实网上能搜索 ...
最新文章
- 页面A使用window.open打开页面B,然后取得B的返回值
- 'mysql' 不是内部或外部命令,也不是可运行的程序或批处理文件
- 消息队列--RabbitMQ简单使用
- 对11位手机号进行3-4-4格式化
- java编程顺序,Java种的完整构造执行顺序(转)
- 负责指挥与控制整台电子计算机,2011秋季计算机应用基础期末考试卷(修改)
- C++socket编程(七):7.4 正则表达式分析用户请求
- requestIdleCallback函数
- 【java笔记】线程(2):多线程的原理
- 微服务浅述---架构演进
- CAD关于线型操作添加线型(com接口c#语言)
- 中国一共有多少个神仙?
- 自学Java软件编程需要哪些基础?
- 【JAVASE】IO流基础
- 工业机器人技术全解析,值得收藏!
- 限幅二极管基础知识详解
- 计算机开机显示器不亮,电脑开机显示器不亮该如何解决
- 免费的编程中文书籍索引
- Fastbot_Android稳定性测试
- webcam实现拍照