一年快要过去了, 你的目标实现了么? 不管实现没有, 先来个小目标吧.比如造一个轮子?
说点题外话. Coding
中我们总是经历着这么几个过程.
学会使用
: 不管是API
也好, 开源库也好. 总是在最开始的学会去用.了解实现原理
: 可能会因为一些不兼容, 代码的异常状态的处理不够完美等需要查看实现并修改, 或者因为你有一个好奇心向窥探一下内部实现.. 这时我们开始试着去阅读, 试着去理解.是否可以自己写一个更好的?
: 这个时候你可能已经熟悉了一个模块需要如何去写. 如何构造出一个别人没有或者扩展了新功能的一个小Demo
.终极目标
: 写出了一个功能效果更酷的Demo
, 但是这个是否是就变成了一个好的开源库? 不, 一个好的开源库不仅功能强大实用, 并且还有一个不容忽视的特点可扩展
. 这一个比较好的状态. 可以扩展, 通过对出现的问题不断完善, 慢慢的就会演变成一个强大的库
是否造轮子, 每个人看法都不一样, 我认为造轮子最起码的好处如下两点, 当然你得有时间!!!
对知识的全面理解的最好实践, 会一个知识点, 需要写一个小
Demo
巩固, 小Demo
放在项目中又会面临实际场景中可能没有想到的问题. 所以尽量不要只做到浅尝辄止
.如果不错, 或许可以帮助别人, 能帮到别人, 这应该会让自己在学习知识巩固知识的喜悦上更开心.
当然, 对于苦逼的码农来说, 不停的bug
不停的需求. 这是比较蛋疼的. 没有时间可能会阻碍到自己去学习进步.
看过第一弹的应该看到我列出了三个阶段. 结果差不多是自己挖个坑自己跳了进去. 还好大体目标还是完成了.
好了, 开始说SImageView
控件把.
控件介绍
这是一个简单到sImageView.setImageUrls("http://img3.cache.netease.com/ent/2009/4/17/20090417104402666a4.jpg");
设置一个网址即可显示图片的控件
相对ImageView
功能的扩展的控件, 但是没有继承ImageView
直接继承的View
. 比如QQ群组头像
,微信群组头像
, 设置描边
, 设置圆角矩形头像
,圆形头像
等. 几个参数搞定. 对于多个图片的排列
和图片的具体显示
进行了接口分离. 可以自定义实现任何排列效果和显示效果.
网络图片的下载会原图缓存磁盘, 并根据控件的大小加载到到内存并使用显示.
效果展示
图片可能比较大, 如果不出现, 刷新页面试试或者多等一会.
可以实现的样式
使用说明
引用方式
rootDir/app/build.gradle文件
dependencies {// ...compile 'com.szysky.customize:simageview:2.2';} |
xml声明方式
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><!--简单的配置, 默认为圆形图像,无描边--><com.szysky.customize.siv.SImageViewxmlns:app="http://schemas.android.com/apk/res-auto"app:img="@mipmap/icon_test"android:layout_width="200dp"android:layout_height="200dp" /><!--稍微完全的配置范例, 下面会有属性的详细说明--><com.szysky.customize.siv.SImageViewandroid:id="@+id/siv_main"android:layout_width="match_parent"android:layout_height="200dp"android:background="@color/colorAccent"app:displayType="rect"app:border_color="@color/colorPrimary"app:border_width="1dp"app:img="@mipmap/ic_1"app:scaleType="fix_XY"/></LinearLayout> |
属性说明
displayType
设置控件中的图片要以什么类型显示. 可选值如下:circle
: 圆形图片. (控件的默认值)rect
: 矩形图片.round_rect
: 圆角矩形图片.oval
: 椭圆形图片five_pointed_star
: 五角星形图片
border_color"
图片描边颜色. 只有当border_width>0
的时候才有效. 默认是黑色.border_width
图片描边的宽度. 默认值为0
, 不显示描边.img
前景图片, 以上所有的效果, 都是对前景图片进行操作处理.scaleType
类似于ImageView
的图片缩放选择. 只有当displayType="rect"
是矩形, 并且border_width=0dp
条件下才有效果. 其余场景无意义.可选值如下:center_inside
: 保持图片的完整性缩放, 可能会留白, 图片比例不变center_crop
: 保持控件全部被图片填充. 图片部分可能丢失, 图片比例不变.fix_XY
: 保持图片的完整性并且控件被全部填充. 图片不会丢失, 不会留白. 图片比例会改变.
代码设置形式
最简单暴力的设置方式
// 查找控件SImageView sImageView = (SImageView) findViewById(R.id.siv_main);// 直接设置一个图片URL即可, 根据控件大小进行内存缓存, 保存原图到本地磁盘缓存sImageView.setImageUrls("http://img3.cache.netease.com/ent/2009/4/17/20090417104402666a4.jpg"); |
关于群组头像
// 如果你想实现QQ群组效果, 很简单因为默认是圆形类型显示, 不需要多余设置// 直接传入多个URL, 最多支持5张. 例如sImageView.setImageUrls("http://img3.cache.netease.com/ent/2009/4/17/20090417104402666a4.jpg","http://img3.cache.netease.com/ent/2009/4/17/20090417104402666a4.jpg","http://img3.cache.netease.com/ent/2009/4/17/20090417104402666a4.jpg","http://img3.cache.netease.com/ent/2009/4/17/20090417104402666a4.jpg","http://img3.cache.netease.com/ent/2009/4/17/20090417104402666a4.jpg");// 需要微信的样式, 那么改一下 布局管理器 , 并设置显示图片类型为 矩形,// 最多支持9张. 如下sImageView.setDisplayShape(SImageView.TYPE_RECT).setLayoutManager(new WeChatLayoutManager(getApplicationContext())).setImageUrls("http://img3.cache.netease.com/ent/2009/4/17/20090417104402666a4.jpg","http://img3.cache.netease.com/ent/2009/4/17/20090417104402666a4.jpg","http://img3.cache.netease.com/ent/2009/4/17/20090417104402666a4.jpg","http://img3.cache.netease.com/ent/2009/4/17/20090417104402666a4.jpg","http://img3.cache.netease.com/ent/2009/4/17/20090417104402666a4.jpg"); |
以下是常用方法
SImageView sImageView = (SImageView) itemView.findViewById(R.id.siv);// 设置描边颜色sImageView.setBorderColor(Color.GREEN);// 设置描边宽度 单位dp值sImageView.setBorderWidth(1);// 设置图片显示类型 // 可设置类型: SImageView.TYPE_CIRCLE(默认), SImageView.TYPE_OVAL, // SImageView.TYPE_RECT, SImageView.TYPE_ROUND_RECT, // SImageView.TYPE_FIVE_POINTED_STARsImageView.setDisplayShape(SImageView.TYPE_ROUND_RECT);// 设置图片的缩放类型, 只有显示类型为矩形, 并且描边宽度为0. 才有效果. 区别在xml中有说明// 可选类型3种:SCALE_TYPE_CENTER_INSIDE(默认), // SCALE_TYPE_FIX_XY ,SCALE_TYPE_CENTER_CROPmSImageView.setScaleType(SImageView.SCALE_TYPE_CENTER_INSIDE);// 设置微信群组样式显示. (可以自定义measure测量排列规则)替换measure测量策略如下:// 默认为qq群组的测量策略. 只要设置图片时传入多张图片的集合即可.sImageView.setLayoutManager(new WeChatLayoutManager(context));// 不仅可以设置url数组, 还支持其他设置图片方式sImageView.setImages(List<Bitmap>); // 接收一个bitmap集合, 实现qq群组或者微信群组效果sImageView.setIdRes(id); // 接收图片资源idsImageView.setDrawable(Drawable); // 接收一个Drawable对象sImageView.setBitmap(Bitmap); // 接收一个图片的bitmap |
加载中的图片和加载失败的图片设置
对于网络下载图片. 会有下载失败和下载中的图片显示. 默认图片加载类ImageLoad
类中会内置两张系统两个对应图片来显示. ImageLoad
类中的加载中
和加载失败
图片会作用于全局的SImageView
控件. 也可以给SImageView
控件实例设置. 如果控件设置了. 那么优先级会比全局ImageLoad
中的图片使用优先. 如果控件没有, 那么就使用全局.
代码设置如下:
// 设置当前控件的场景图片, 优先级高于全局sImageView.setErrPicResID(R.mipmap.ic_launcher) // 设置加载错误图片.setLoadingResID(R.mipmap.icon_test) // 设置加载中图片.setImageUrls("http://xxx.jpg"); // 图片故意填写一个错误// 对于图片网址满足条件 判断正则为: "https?://.*?.(jpg|png|bmp|jpeg|gif)"//如果不满足, 那么会认为是一个错误地址, 可动态配置, 后面说明// 设置全局控件场景图片, (有默认图片可以不设置)ImageLoader.getInstance(getApplicationContext()).setLoadErrResId(R.mipmap.icon_test); ImageLoader.getInstance(getApplicationContext()).setLoadingResId(R.mipmap.ic_launcher); |
控件其他的方法
方法名称 | 参数说明 | 方法作用 |
---|---|---|
setCloseNormalOnePicLoad() | 布尔值 | 设置true 可以强制关闭一张图片时候的默认单张图片处理规则, 而由测量接口 ,绘制显示接口 处理. |
setOvalRatio() | float类型, 椭圆的宽高比值(必须大于0) | 在单张图片 并且椭圆类型 显示时, 设置椭圆的显示的宽高比例 |
setRectRoundRadius() | float类型, 设置范围0~2,默认1 | 在单张图片 并且圆角矩形类型 显示时, 设置圆角的弧度大小 |
setDrawStrategy() | 可参考下面的扩展实现, 用来设置自定义图片实现策略 | |
setLayoutManager() | 可参考下面的扩展实现, 用来设置自定义或替换 图片的排列分布规则 |
对应的getter()
方法省略.
设置图片网址匹配
上面提到过默认过滤图片链接的正则判断为"https?://.*?.(jpg|png|bmp|jpeg|gif)"
如果需要实现其他的地址规则. 可重定义过滤策略
ImageLoader.getInstance(getApplicationContext()).setPicUrlRegex("RegexStr");// 如果设置自定义正则之后需要恢复, 那么直接设置空串即可ImageLoader.getInstance(getApplicationContext()).setPicUrlRegex(""); |
输出log开关
默认类库log
是不输出的, 如果需要打开如下:
LogUtil.GlobalLogPrint = true; // 输出类库相关log信息 |
扩展实现
控件实现了
measure测量布局
和draw具体绘图实现
的功能分离. 你可以任意实现排列规则, 和具体的绘图显示的规则.
自定义measure测量布局
布局测量接口ILayoutManager
. 相当于RecyclerView
设置布局管理器. 或者View#onMeasure()
的作用.
目前内置了2种布局来实现多张图片的排列.
QQLayoutManager
: 控件默认排列规则, 效果类似于qq群组头像
,最大支持5
张图片WeChatLayoutManager
: 效果类似于微信群组头像
, 最大支持9
张图片
通过setLayoutManager(ILayoutManager)
来进行测量规则的具体实现类.
默认情况下, 如果控件只设置了一张图片是不会走测量的流程
. 如果需要一张图片时也需要不规则的排布. 那么通过SImageView#setCloseNormalOnePicLoad(true)
. 强制关闭.
自定义实现: 实现ILayoutManager
接口并在calculate()
实现具体的排列效果. 并返回一个子图片的位置信息集合. 接口如下. 可参考已经实现的两个类.
public interface ILayoutManager {/*** 布局measure排列计算方法, 具体规则由子类实现** @param viewWidth 控件的宽* @param viewHeight 控件的高* @param viewNum 控件图片的数量* @return 返回一个信息集合, 提供 {@link com.szysky.customize.siv.effect.IDrawingStrategy#algorithm(Canvas, int, int, Bitmap, SImageView.ConfigInfo)}使用*/ArrayList<LayoutInfoGroup> calculate(int viewWidth, int viewHeight, int viewNum);/*** 封装控件内部单个元素显示的布局信息*/class LayoutInfoGroup implements Cloneable{/*** 组合头像时, 每个单独元素可分配的最大宽高*/public int innerWidth;public int innerHeight;/*** 每个单独元素,左上点和右下点. 可规划区域*/public Point leftTopPoint = new Point();public Point rightBottomPoint = new Point();@Overrideprotected Object clone() throws CloneNotSupportedException {LayoutInfoGroup clone = (LayoutInfoGroup) super.clone();clone.leftTopPoint.set(leftTopPoint.x, leftTopPoint.y);clone.rightBottomPoint.set(rightBottomPoint.x, rightBottomPoint.y);return clone;}}} |
自定义的图片显示
控件内置了两种图片显示. 例如: 椭圆, 圆角矩形, 描边, 五角星等. 相当于View#onDraw()
和Adapter#getView()
作用. 具体显示分离.
绘制显示接口IDrawingStrategy
内置实现:
NormalOnePicStrategy
: 当控件设置单张图片时, 默认都是正中间(矩形除外, 保留了ImageView
三种常用的缩放). 所以无需进行测量步骤. 直接通过配置的形状属性等进行相对应的配置实现效果.ConcreteDrawingStrategy
: 当控件图片为多张的时被触发. 接收ILayoutManager#calculate()
测量布局返回的子图片的信息集合, 进行具体的绘制工作. 可通过SImageView#setCloseNormalOnePicLoad(true)
强制关闭控件单张图片执行NormalOnePicStrategy
的逻辑. 全权由测量布局
,绘制显示
两个逻辑实现所有图片数量的处理.
通过setDrawStrategy(IDrawingStrategy)
来进行图片绘制显示的具体策略类.
自定义绘制策略类. 实现IDrawingStrategy
接口并实现对应方法, 方法里面有图片对应的画布,和需要显示的宽高信息等. 接口如下:
public interface IDrawingStrategy {/*** 根据提供的画布, 和可绘制的位置实现具体效果** @param canvas {@link SImageView#onDraw(Canvas)} 中的画布* @param childTotal 图片的总个数* @param curChild 当前图片是第几张图片* @param opeBitmap 需要操作的图片* @param info 每个内部元素应该摆放的位置信息类*/void algorithm(Canvas canvas, int childTotal, int curChild, Bitmap opeBitmap, SImageView.ConfigInfo info);} |
缓存策略自定义
这部分写的自己不是很满意, 写着写着就有点耦合了, 最后精力不够… 好吧这是借口. 反正也能将就自定义, 用默认的就行…. [捂脸]
建议
尽量使控件作为头像控件显示, 如果大小低于
100dp
, 内部稍微做了一些特别处理. 性能可以好一些.由于内置样式较多, 导致了
cpu密集处理
. 和一些对象的开销. 如果项目性能要求较高那么可通过自定义绘图策略注入控件来优化. 这样项目中常用的效果就可以得到性能提升.类库需要
写外部存储的权限
, 对于新版本的动态权限
, 一定要先进行权限判断, 再对ImageLoad
进行初始化(控件的网络图片设置). 否则可能导致, 磁盘缓存无效只有内存缓存.