带你一步步实现带有多彩阴影的ImageView

身为android开发者,ImageView那一定是玩的滚瓜烂熟,现在如今Material Design设计也越来越流行,给ImageView实现阴影也不是什么难事,用CardView包裹一下,就能实现了,但是阴影都是一种颜色,实在太单调了╮(╯▽╰)╭,我就在想ImageView阴影能不能设置成图片自身的颜色呢?,答案当然是可以的,基于这个思路就有了PaletteImageView这个控件,PaletteImageView不仅仅可以将阴影设置成图片自身颜色,还可以解析图片的颜色,提供可以跟图片匹配的UI颜色方案,是不是很酷,接下来让我们一步步来实现它。
先看一下效果图:
                  这里写图片描述

要实现的功能:
  • 默认情况下为图片添加相应颜色的阴影
  • 可以自定义阴影的颜色
  • 可以为图片添加圆角
  • 可以设置阴影半径大小,提供可以调控的用户体验
  • 可以自定义设置阴影分别在x和y方向上的偏移量,更高的可控性
  • 根据图片自身的颜色提供与图片匹配的颜色方案(智能推荐颜色功能)

难点就是怎么才能获取到图片本身的颜色呢?,这就要用到Palette这个类了,Palette就是调色板的意思,不了解的可以参考这篇文章Palette使用详解
我们先来实现给图片添加相应颜色的阴影,下面只贴出关键代码,要阅读完整代码请移步github https://github.com/DingMouRen/PaletteImageView,主要思路是解析获取到的bitmap的颜色,当解析出bitmap颜色的时候,使用handler来通知控件来绘制带有颜色的阴影

在onSizeChanged(…)中能够获取到控件宽高的时候,对图片先进行压缩处理 ,为了防止内存溢出情况的发生,然后就是获取图片颜色得操作
 @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        zipBitmap(mImgId, mBitmap, mOnMeasureHeightMode);//先压缩图片,再去解析图片bitmap中的颜色
        mRectFShadow = new RectF(mPadding, mPadding, getWidth() - mPadding, getHeight() - mPadding);//阴影的载体
        ...
    }
压缩图片
  private void zipBitmap(int imgId, Bitmap bitmap, int heightNode) {
        WeakReference<Matrix> weakMatrix = new WeakReference<Matrix>(new Matrix());
        if (weakMatrix.get() == null) return;
        Matrix matrix = weakMatrix.get();
        int reqWidth = getWidth() - mPadding - mPadding;
        int reqHeight = getHeight() - mPadding - mPadding;
        if (reqHeight <= 0 || reqWidth <= 0) return;
        int rawWidth = 0;
        int rawHeight = 0;
        if (imgId != 0 && bitmap == null) {
            WeakReference<BitmapFactory.Options> weakOptions = new WeakReference<BitmapFactory.Options>(new BitmapFactory.Options());
            if (weakOptions.get() == null) return;
            BitmapFactory.Options options = weakOptions.get();
            BitmapFactory.decodeResource(getResources(), imgId, options);
            options.inJustDecodeBounds = true;
            rawWidth = options.outWidth;
            rawHeight = options.outHeight;
            options.inSampleSize = calculateInSampleSize(rawWidth, rawHeight, getWidth() - mPadding * 2, getHeight() - mPadding * 2);
            options.inJustDecodeBounds = false;
            bitmap = BitmapFactory.decodeResource(getResources(), mImgId, options);
        } else if (imgId == 0 && bitmap != null) {
            rawWidth = bitmap.getWidth();
            rawHeight = bitmap.getHeight();
            float scale = rawHeight * 1.0f / rawWidth;
            mRealBitmap = Bitmap.createScaledBitmap(bitmap, reqWidth, (int) (reqWidth * scale), true);
            initShadow(mRealBitmap);
            return;
        }
        if (heightNode == 0) {
            float scale = rawHeight * 1.0f / rawWidth;
            mRealBitmap = Bitmap.createScaledBitmap(bitmap, reqWidth, (int) (reqWidth * scale), true);
        } else {
            int dx = 0;
            int dy = 0;
            int small = Math.min(rawHeight, rawWidth);
            int big = Math.max(reqWidth, reqHeight);
            float scale = big * 1.0f / small;
            matrix.setScale(scale, scale);
            if (rawHeight > rawWidth) {
                dy = (rawHeight - rawWidth) / 2;
            } else if (rawHeight < rawWidth) {
                dx = (rawWidth - rawHeight) / 2;
            }
            mRealBitmap = Bitmap.createBitmap(bitmap, dx, dy, small, small, matrix, true);
        }
        initShadow(mRealBitmap);//解析图片颜色,关键
    }
解析图片的关键代码
  private void initShadow(Bitmap bitmap) {
        if (bitmap != null) {
            mAsyncTask = Palette.from(bitmap).generate(paletteAsyncListener);//异步解析图片的颜色
        }
    }

      //异步线程中解析图片颜色的监听器
       private Palette.PaletteAsyncListener paletteAsyncListener = new Palette.PaletteAsyncListener() {
           @Override
           public void onGenerated(Palette palette) {
               if (palette != null) {
                   ...
                   mMainColor = palette.getDominantSwatch().getRgb();//获取图片的主色,也就是默认的阴影颜色
                   mHandler.sendEmptyMessage(MSG);//解析到图片的颜色,此时发送重绘的消息
                   ...
               }
           }
       };

      //处理发送的消息
      private Handler mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                //阴影半径大小以及x、y方向上阴影偏移量的限制
                if (mOffsetX < DEFAULT_OFFSET) mOffsetX = DEFAULT_OFFSET;
                if (mOffsetY < DEFAULT_OFFSET) mOffsetY = DEFAULT_OFFSET;
                if (mShadowRadius < DEFAULT_SHADOW_RADIUS) mShadowRadius = DEFAULT_SHADOW_RADIUS;
                //设置画笔阴影的颜色,也就是图片阴影的颜色了
                mPaintShadow.setShadowLayer(mShadowRadius, mOffsetX, mOffsetY, mMainColor);
                invalidate();//重新绘制控件
            }
        };
绘制阴影
 @Override
    protected void onDraw(Canvas canvas) {
        if (mRealBitmap != null) {
            canvas.drawRoundRect(mRectFShadow, mRadius, mRadius, mPaintShadow);//绘制带有颜色的阴影
            ...
            if (mMainColor != -1) mAsyncTask.cancel(true);//取消解析图片颜色的异步任务
        }
    }
经过上面就可以绘制出带有颜色的阴影,要自定义阴影颜色的话,使用这句代码
   public void setShadowColor(int color){
        this.mMainColor = color;
        mHandler.sendEmptyMessage(MSG);//修改了阴影颜色,发送重新绘制的消息
    }
接下来我们给图片设置圆角,在onSizeChange(…)获取到宽高以及bitmap的同时,我们就创建好了带有圆角的mRoundBitmap,绘制过阴影后,就绘制可以带有圆角的图片
 @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        ...
        mRoundRectF = new RectF(0, 0, getWidth() - mPadding * 2, getHeight() - mPadding * 2);
        mRoundBitmap = createRoundConerImage(mRealBitmap,mRadius);//创建带有圆角的bitmap图片

    }

//创建带有圆角的bitmap图片
 private Bitmap createRoundConerImage(Bitmap source, int radius) {
        Bitmap target = Bitmap.createBitmap(getWidth() - mPadding * 2, getHeight() - mPadding * 2, Bitmap.Config.ARGB_4444);
        Canvas canvas = new Canvas(target);
        canvas.drawRoundRect(mRoundRectF, radius, radius, mPaint);
        mPaint.setXfermode(mPorterDuffXfermode);
        canvas.drawBitmap(source, 0, 0, mPaint);
        mPaint.setXfermode(null);
        return target;
    }

 @Override
    protected void onDraw(Canvas canvas) {
        if (mRealBitmap != null) {
            ...
            canvas.drawBitmap(mRoundBitmap, mPadding, mPadding, null);//绘制带有圆角的bitmap
            if (mMainColor != -1) mAsyncTask.cancel(true);
        }
    }
修改圆角半径的效果图

                  这里写图片描述

经过以上带有多彩颜色阴影的图片已经出笼了,现在我们要可以修改阴影半径的大小,修改阴影半径大小的方式有两种,一种是在xml中定义,另一种是在代码中设置
//xml中添加属性:
app:paletteShadowRadius 表示阴影半径

//代码中设置阴影半径大小的源码
public void setPaletteShadowRadius(int radius) {
        this.mShadowRadius = radius;//修改阴影半径大小
        mHandler.sendEmptyMessage(MSG);//发送消息,阴影半径修改了,发送重绘消息
    }
修改阴影半径的效果图

                  这里写图片描述

修改阴影在x y方向的偏移量的原理跟上面一样,修改参数再发送消息重绘
 public void setPaletteShadowOffset(int offsetX, int offsetY) {
        if (offsetX >= mPadding) {
            this.mOffsetX = mPadding;
        } else {
            this.mOffsetX = offsetX;
        }
        if (offsetY > mPadding) {
            this.mOffsetX = mPadding;
        } else {
            this.mOffsetY = offsetY;
        }

        mHandler.sendEmptyMessage(MSG);//阴影在x y方向的偏移量改变了,此时发送消息进行重绘。
    }
修改阴影在x y方向的偏移量的效果图

                 这里写图片描述

现在终于说到智能配色的地方了,看过Palette使用详解这篇文章的都知道获取到颜色方案的过程了,不了解的可以先去看看,没有太神秘的东西O(∩_∩)O哈哈~,下面讲一下实现过程:

首先定义一个监听器,用来监听图片颜色的解析过程,解析完成我们返回看控件本身的引用,我们可以获取六种不同颜色主题,每种主题颜色都有与之匹配的标题颜色、正文颜色、背景色。怎么获取到这些颜色呢,实现是将每种主题的是三种子颜色放入一个数组中存储,只要拿到相应的数组就可以获取到想要的颜色了。

 public interface OnParseColorListener {
        void onComplete(PaletteImageView paletteImageView);
        void onFail();
    }
//异步线程中解析图片颜色的监听器
private Palette.PaletteAsyncListener paletteAsyncListener = new Palette.PaletteAsyncListener() {
         @Override
         public void onGenerated(Palette palette) {
             if (palette != null) {
                 mPalette = palette;
                 mMainColor = palette.getDominantSwatch().getRgb();
                 mHandler.sendEmptyMessage(MSG);
                 if (mListener != null) mListener.onComplete(mInstance);//解析颜色完成
             } else {
                 if (mListener != null) mListener.onFail();//解析颜色失败
             }
           }
    };

//获取Vibrant主题的颜色,
public int[] getVibrantColor() {
      if (mPalette == null || mPalette.getVibrantSwatch() == null) return null;
        int[] arry = new int[3];
        arry[0] = mPalette.getVibrantSwatch().getTitleTextColor();
        arry[1] = mPalette.getVibrantSwatch().getBodyTextColor();
        arry[2] = mPalette.getVibrantSwatch().getRgb();
        return arry;
    }
智能配色效果图

这里写图片描述

大功告成,可以愉快的玩耍啦。PaletteImageView的具体使用方法,请移步github

项目源码地址https://github.com/DingMouRen/PaletteImageView

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值