年终福利 - 一款实用的图片控件 SImageView(第一弹)

3,739 阅读8分钟

一年快要过去了, 你的目标实现了么? 不管实现没有, 先来个小目标吧.比如造一个轮子?

说点题外话. Coding中我们总是经历着这么几个过程.

  • 学会使用: 不管是API也好, 开源库也好. 总是在最开始的学会去用.
  • 了解实现原理: 可能会因为一些不兼容, 代码的异常状态的处理不够完美等需要查看实现并修改, 或者因为你有一个好奇心向窥探一下内部实现.. 这时我们开始试着去阅读, 试着去理解.
  • 是否可以自己写一个更好的?: 这个时候你可能已经熟悉了一个模块需要如何去写. 如何构造出一个别人没有或者扩展了新功能的一个小Demo.
  • 终极目标: 写出了一个功能效果更酷的Demo, 但是这个是否是就变成了一个好的开源库? 不, 一个好的开源库不仅功能强大实用, 并且还有一个不容忽视的特点可扩展. 这一个比较好的状态. 可以扩展, 通过对出现的问题不断完善, 慢慢的就会演变成一个强大的库

是否造轮子, 每个人看法都不一样, 我认为造轮子最起码的好处如下两点, 当然你得有时间!!!

  • 对知识的全面理解的最好实践, 会一个知识点, 需要写一个小Demo巩固, 小Demo放在项目中又会面临实际场景中可能没有想到的问题. 所以尽量不要只做到浅尝辄止.
  • 如果不错, 或许可以帮助别人, 能帮到别人, 这应该会让自己在学习知识巩固知识的喜悦上更开心.

当然, 对于苦逼的码农来说, 不停的bug不停的需求. 这是比较蛋疼的. 没有时间可能会阻碍到自己去学习进步.

控件说明

这是一个相对ImageView功能的扩展的控件, 但是没有继承ImageView直接继承的View. 比如QQ群组头像,微信群组头像, 设置描边, 设置圆角矩形头像,圆形头像等. 直接设置即可. 对于多个图片的排列图片的具体显示进行了接口分离. 可以自定义实现任何排列效果和显示效果.

目前只完成了第一阶段.

  • 第一阶段(已完成): 完成开发常用的头像处理效果功能集合. 并对onMeasure``padding等进行处理, 实现一个可以工作的类.
  • 第二阶段(下个礼拜吧): 支持图片网络地址设置, 相当于内置了图片缓存策略. 这样可以省去一些图片加载库的依赖如果项目中对图片的依赖比较严重, 对性能要求高的. 可能还得使用一些高性能的库如Fresco. 但大公司可能会有时间去自己实现的.
  • 第三阶段(没想好..): 做一些性能优化吧

废话太多了…. 抱歉, 看效果吧

效果展示

图片可能比较大, 如果不出现, 刷新页面试试或者多等一会.

项目地址 看着还可以随手撒个星星, 鼓励鼓励新人的我吧.

使用说明

xml声明方式


    
    
    
       

属性说明

  • 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) 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_STAR
 sImageView.setDisplayShape(SImageView.TYPE_ROUND_RECT);
 
 // 设置图片的缩放类型, 只有显示类型为矩形, 并且描边宽度为0. 才有效果. 区别在xml中有说明
 // 可选类型3种:SCALE_TYPE_CENTER_INSIDE(默认), 
 //           SCALE_TYPE_FIX_XY ,SCALE_TYPE_CENTER_CROP
 mSImageView.setScaleType(SImageView.SCALE_TYPE_CENTER_INSIDE);
 
 // 设置微信群组样式显示.  (可以自定义measure测量排列规则)替换measure测量策略如下:
 // 默认为qq群组的测量策略. 只要设置图片时传入多张图片的集合即可.
sImageView.setLayoutManager(new WeChatLayoutManager(context));
// 设置图片
sImageView.setImages(List); // 接收一个图片集合, 实现qq群组或者微信群组效果
sImageView.setIdRes(id);            // 接收图片资源id
sImageView.setDrawable(Drawable);   // 接收一个Drawable对象
sImageView.setBitmap(Bitmap);       // 接收一个图片的bitmap

还有一些对外的方法:

方法名称 参数说明 方法作用
setCloseNormalOnePicLoad() 布尔值 设置true可以强制关闭一张图片时候的默认单张图片处理规则, 而由测量接口,绘制显示接口处理.
setOvalRatio() float类型, 椭圆的宽高比值(必须大于0) 单张图片并且椭圆类型显示时, 设置椭圆的显示的宽高比例
setRectRoundRadius() float类型, 设置范围0~2,默认1 单张图片并且圆角矩形类型显示时, 设置圆角的弧度大小
setDrawStrategy() 可参考下面的扩展实现, 用来设置自定义图片实现策略
setLayoutManager() 可参考下面的扩展实现, 用来设置自定义或替换 图片的排列分布规则

对应的getter()方法省略.

扩展实现

控件实现了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 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();
        @Override
        protected 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);
}

项目地址

未完待续… 后期实现支持图片链接的设置并添加内置图片缓存