探一探,很是实用的GIF图圆角控件(3行代码)

前言

现代人的崩溃是一种默不吭声的崩溃,看起来很正常,会说笑,会打闹,会社交,表面平静,实际上内心的糟心事已经积累到必定程度了。不会摔门砸东西,不会流眼泪或歇斯心底,但可能某一秒忽然就积累到极致,也不说话,也不真的崩溃,也不太想活着。也不敢去死。java

这里引用了「张帅B」的一段话,说的很贴切,咱们这一代年轻人到底怎么了?android

正文

先来看看效果图: git

在这里插入图片描述

初步分析

众所周知,Android 中主流的图片加载框架有 Picasso,Glide,Fresco。Picasso 加载 gif 图没有动画效果,Glide 与 Fresco 支持 gif 动效图。Fresco 自带的控件 SimpleDraweeView 支持圆角属性,Glide 须要手动给 ImageView 设置 shape,那么 Glide,Fresco 是否支持 gif 图圆角?github

先来看一个例子,Glide 加载 gif 圆角,先看看 xml 布局:canvas

<ImageView
        android: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);
复制代码

效果图: ide

在这里插入图片描述
发现 gif 图并无显示圆角,通过测试 Fresco 的圆角属性,在 gif 图上也会失效。那么咱们怎么才能让 gif 图显示圆角呢?

你们都知道 gif 是由多张静态图组合而成,若是处理单张图片效率将会极其低下,既然不能对源图片进行处理,那么就只能在显示控件上想办法了。布局

通过初步分析有如下三种可行方案:性能

  1. 裁剪图片控件
  2. 在 gif 图片控件上覆盖一层圆角图片(4角圆角中间透明)
  3. 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) ,咱们一块儿来探究下这几个值产生的效果,先来看看下面这段代码:

@Override
    protected 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 并不要求圆的方向,看看下面一段代码:

@Override
    protected 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 表示反转的意思,相同的一段代码,绘制出来的结果是相反的。以下代码:

@Override
    protected 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();
    }
复制代码

圆的区域都不须要绘制颜色,非圆区域绘制颜色。效果图以下:

在这里插入图片描述
设置一圆的绘制方向为逆时针:

@Override
    protected 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 模式的不一样的绘制方向效果同样,代码以下:

@Override
    protected 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 rect
        mPath.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 的在忙着赚钱又谈恋爱,最后钱没挣到,恋爱也没谈到。

最后再啰嗦一点,生活都不容易,一直坚持作一件事情更不容易,还但愿各位朋友,可以多多支持,小编新开的公众号「控件人生」,有大家的相伴,才有写下去的动力。小编还会不按期发放现金红包,发放现金红包,发放现金红包。

源码地址:

github.com/HpWens/MeiW…

qrcode_for_gh_232b5a56667d_258.jpg

扫一扫 关注个人公众号
与小伙伴们一同成长~
相关文章
相关标签/搜索