圆角控件之ImageFilterView详解

简介

  我们以往显示圆角图形,一般使用三方库或者自己写一个控件来实现。在constraintlayout 2.0出来后,提供了一个圆角控件ImageFilterView,使用更加方便,并且自带滤镜功能。可以显示本地图片(R.drawable引用)和网络图片(比如Glide加载)。

属性

  • 圆角大小\color{blue}{app:round}:取值0-size/2之间,超过就没什么意义了,默认0,就是方形;对于正方形来说,取值size/2就是圆形, 圆角是针对View的, 将View绘制成圆角.
  • 圆角比例\color{blue}{app:roundPercent}:取值0-1之间,超过1就没什么意义了,默认0就是方形,1是圆形图片。和app:round意思一样,只不过一个是具体的大小,一个是百分比。
  • 缩放\color{blue}{app:imageZoom}:放大或缩小图片大小,比如:2表示图片放大到原来的2倍,0.5表示图片缩小到原来的一半。View的大小不变,只是显示的图片缩放了。
  • 旋转\color{blue}{app:imageRotate}:旋转图片的角度,比如90,表示图片旋转90度。View的角度和大小是不变的。
  • 交叉图\color{red}{app:altSrc}:需要跟app:crossfade共同使用,app:crossfade取值0-1,默认0为交叉图完全透明,不展示。取值1交叉图完全展示,覆盖到src上。
  • 饱和度\color{red}{app:saturation}:float型,默认1,取值0为灰阶样式,大于1的数值都是超饱和状态,色彩非常艳丽,有点辣眼睛。
  • 亮度\color{red}{app:brightness}:float型,默认1,值越大亮度越高。
  • 色温\color{red}{app:warmth}:float型,默认值1,小于1是冷色调,大于1是暖色调。
  • 对比度\color{red}{app:contrast}:float型,默认1,取值0相当于图片变全黑,大于1都是高对比度状态。
  • \color{red}{app:overlay},官方释义:定义alt图像是在原始图像的顶部淡入,还是与其交叉淡入。默认值为true。对于半透明对象设置为false。

使用

1. 滤镜
  一般应用都是显示图片的原始效果图,鲜少使用滤镜功能,除非是相册等专门处理图片的应用。所以,这里不会专门介绍,只略提一笔。

//继承ImageView,本质上就是一个ImageView
public class ImageFilterView extends androidx.appcompat.widget.AppCompatImageView {
    //这个类就是用来处理滤镜功能的
    static class ImageMatrix {
        float[] m = new float[4 * 5];
        //颜色矩阵,用来实现各种滤镜效果
        ColorMatrix mColorMatrix = new ColorMatrix();
        ColorMatrix mTmpColorMatrix = new ColorMatrix();
        float mBrightness = 1;
        float mSaturation = 1;
        float mContrast = 1;
        float mWarmth = 1;

        void updateMatrix(ImageView view) {
            mColorMatrix.reset();
            boolean filter = false;
            //设置饱和度效果
            if (mSaturation != 1.0f) {
                saturation(mSaturation);
                mColorMatrix.set(m);
                filter = true;
            }
            //设置对比度效果
            if (mContrast != 1.0f) {
                mTmpColorMatrix.setScale(mContrast, mContrast, mContrast, 1);
                mColorMatrix.postConcat(mTmpColorMatrix);
                filter = true;
            }
            //设置色温效果
            if (mWarmth != 1.0f) {
                warmth(mWarmth);
                mTmpColorMatrix.set(m);
                mColorMatrix.postConcat(mTmpColorMatrix);
                filter = true;
            }
            //设置亮度效果
            if (mBrightness != 1.0f) {
                brightness(mBrightness);
                mTmpColorMatrix.set(m);
                mColorMatrix.postConcat(mTmpColorMatrix);
                filter = true;
            }
            //其实就是通过setColorFilter实现颜色的滤镜效果
            if (filter) {
                view.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));
            } else {
                view.clearColorFilter();
            }
        }
    }

2. 缩放
  以下将图片的大小放大到原来的1.5倍。

    <androidx.constraintlayout.utils.widget.ImageFilterView
        android:id="@+id/filter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="30dp"
        android:src="@drawable/setting_item_privacy_manager"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toEndOf="@id/origin"
        app:layout_constraintTop_toTopOf="parent"
        app:imageZoom="1.5"/>
image.png

效果图:屏幕背景绿色, View背景橙色. 左边是原图,右边是放大1.5倍图。可以看到,View大小是不变的,还是原图大小。

3. 旋转
  以下将图片的角度顺时针旋转60度。

    <androidx.constraintlayout.utils.widget.ImageFilterView
        android:id="@+id/filter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="30dp"
        android:src="@drawable/setting_item_privacy_manager"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toEndOf="@id/origin"
        app:layout_constraintTop_toTopOf="parent"
        app:imageRotate="60"/>
imageRotate.jpg

效果图:屏幕背景绿色, View背景橙色. 左边是原图,右边是旋转60度的效果。可以看到,图片旋转后,超出View外是不会显示的。

3. 圆角
   将圆角设置为size/2, 圆角比例设置为1. 如下88px是view的size/2.

    <androidx.constraintlayout.utils.widget.ImageFilterView
        android:id="@+id/filter"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="30dp"
        android:background="@android:color/holo_orange_dark"
        android:src="@drawable/setting_item_privacy_manager"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@id/filter2"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toEndOf="@id/origin"
        app:layout_constraintTop_toTopOf="parent"
        app:round="88px" />

    <androidx.constraintlayout.utils.widget.ImageFilterView
        android:id="@+id/filter2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="30dp"
        android:background="@android:color/holo_orange_dark"
        android:src="@drawable/setting_item_privacy_manager"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_chainStyle="packed"
        app:layout_constraintStart_toEndOf="@id/filter"
        app:layout_constraintTop_toTopOf="parent"
        app:roundPercent="1" />
round.jpg

效果图:屏幕背景绿色, View背景橙色. 左边是原图,中间是设置圆角size/2的效果, 右边是设置圆角比例为1的效果。可以看到,设置圆角或圆角比例后, View的视图显示区域变化了.

那么问题来了, 圆角的大小怎么体现, 从View来看的话, 是哪里的值呢?


image.png

如图, 红色部分即圆角大小.

原理

1. 缩放和旋转
   其实就是通过Matrix, 设置Translate和Rotate值实现的.

    private void setMatrix() {
         ......
        float panX = (Float.isNaN(mPanX)) ? 0 : mPanX;
        float panY = (Float.isNaN(mPanY)) ? 0 : mPanY;
        float zoom = (Float.isNaN(mZoom)) ? 1 : mZoom;
        float rota = (Float.isNaN(mRotate)) ? 0 : mRotate;
        Matrix imageMatrix = new Matrix();
        imageMatrix.reset();
        float iw = getDrawable().getIntrinsicWidth();
        float ih = getDrawable().getIntrinsicHeight();
        float sw = getWidth();
        float sh = getHeight();
        float scale = zoom * ((iw * sh < ih * sw) ? sw / iw : sh / ih);
        imageMatrix.postScale(scale, scale);
        float tx = 0.5f * (panX * (sw - scale * iw) + sw - (scale * iw));
        float ty = 0.5f * (panY * (sh - scale * ih) + sh - (scale * ih));
        imageMatrix.postTranslate(tx, ty);
        imageMatrix.postRotate(rota, sw / 2, sh / 2);
        setImageMatrix(imageMatrix);
        setScaleType(ScaleType.MATRIX);
    }

2. 圆角
   通过Path和Outline实现, 圆角比例类似.

    public void setRound(float round) {
            ......
            if (mPath == null) {
                mPath = new Path();
            }
            if (mRect == null) {
                mRect = new RectF();
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                if (mViewOutlineProvider == null) {
                    mViewOutlineProvider = new ViewOutlineProvider() {
                        @Override
                        public void getOutline(View view, Outline outline) {
                            int w = getWidth();
                            int h = getHeight();
                            outline.setRoundRect(0, 0, w, h, mRound);
                        }
                    };
                    setOutlineProvider(mViewOutlineProvider);
                }
                setClipToOutline(true);
            }
            int w = getWidth();
            int h = getHeight();
            mRect.set(0, 0, w, h);
            mPath.reset();
            mPath.addRoundRect(mRect, mRound, mRound, Path.Direction.CW);
   
        if (change) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                invalidateOutline();
            }
        }

    }

总结

  ImageFilterView提供了非常方便的显示圆角的方式, 另外, ImageFilterButton也具有同样的功能, 实现也几乎一样, 唯一不同的是, ImageFilterView继承ImageView, 而ImageFilterButton继承ImageButton. 还有一个控件MotionButton, 继承Button, 也可以实现圆角功能, 但是没有滤镜功能.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,914评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,935评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,531评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,309评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,381评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,730评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,882评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,643评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,095评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,448评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,566评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,253评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,829评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,715评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,945评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,248评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,440评论 2 348

推荐阅读更多精彩内容