zoukankan      html  css  js  c++  java
  • 阴影效果 ShadowLayout 布局实现(让控件实现立体效果)

    效果

    这里写图片描述 
    第二张和第三张图是加入了阴影效果的,是不是觉得立体感很强,感觉图片是浮在屏幕上。这个效果也可以用Google 提供扩展包下的CardView控件来实现,而这篇文章是带大家自己来实现这样一个效果。

    原理

    我们仔细观察上图,可以发现,有带阴影效果的图和没带阴影效果的图,其实就一个地方不同,就是在图片的底下绘制了阴影效果,而图片的大小都没变。所以我们要做的就是给子 View 绘制阴影。那么阴影部分怎么绘制呢?这里是整个效果实现的一个难点;阴影部分其实就是一张 bitmap 图片,而接下来的工作就是如何生成一张这样效果的 bitmap 图,还有就是 bitmap 图片绘制位置的确定。

    实现

    1、阴影效果的 bitmap 图片的生成 
    最简单的办法叫美工做一张,我们把他转化成.9图片,这是一种方法;还有一种方法就是用代码生成这样一张 bitmap,要实现这样的效果,我们需要用到 Paint 画笔中的一个属性

    public MaskFilter setMaskFilter(MaskFilter maskfilter) {
    //...
    }

    这个 MaskFilter有一个子类BlurMaskFilter就能实现这样的效果,一般把它叫为毛玻璃效果。这个类的实现需要传两个参数

    public BlurMaskFilter(float radius, Blur style) {
            //...
        }

    radius:渐变效果的距离。

    style:模式,这里有四中模式

        public enum Blur {
            /**
             * Blur inside and outside the original border.
             */
            NORMAL(0),
    
            /**
             * Draw solid inside the border, blur outside.
             */
            SOLID(1),
    
            /**
             * Draw nothing inside the border, blur outside.
             */
            OUTER(2),
    
            /**
             * Blur inside the border, draw nothing outside.
             */
            INNER(3);
    
            Blur(int value) {
                native_int = value;
            }
            final int native_int;
        }

    这几种模式到底是怎么的呢?来看看下面那张图 
    这里写图片描述
    以上图形是通过

        public void drawRect(RectF rect, Paint paint) {
            ;
        }

    看到这张图加上上面几种模式的注解,应该很清楚了。 
    这里有一个注意点是:我们绘制矩形的时候,如果没有设置这种模糊效果,这绘制的图形的大小就是矩形的大小,如果绘制了模糊效果,则图形的大小需要加上实例化BlurMaskFilter时候的radius,就是渐变的距离。 
    创建 bitmap 的代码如下:

            //设置画笔的 style
            mPaint.setStyle(Paint.Style.FILL);
            //设置画笔的模糊效果
            mPaint.setMaskFilter(new BlurMaskFilter(BLUR_WIDTH, BlurMaskFilter.Blur.NORMAL));
            //设置画笔的颜色
            mPaint.setColor(Color.BLACK);
            //创建 bitmap 图片
            mShadowBitmap = Bitmap.createBitmap(mOriginRect.width(), mOriginRect.height(), Bitmap.Config.ARGB_8888);
            //绑定到画布上
            Canvas canvas = new Canvas(mShadowBitmap);
            //让画布平移,这里为什么要平移,看了前面图片就知道
            canvas.translate(BLUR_WIDTH,BLUR_WIDTH);
            //绘制阴影效果
            canvas.drawRoundRect(mDesRecF, mRadius, mRadius, mPaint);

    2、bitmap 图片绘制位置的确定 
    bitmap 的绘制,是放在

    protected void dispatchDraw(Canvas canvas) {
    //...
    }

    这里有各地方需要注意,需要先绘制 bitmap,在调用

    super.dispatchDraw(canvas);

    为什么?很好理解了,因为super.dispatchDraw(canvas);是分发绘制机制,Layout 的所有子类的绘制都需要通过它来分发,如果先绘制子类,那么 bitmap 阴影部分就会显示在子类的上面,会把子类覆盖。 
    代码如下:

        @Override
        protected void dispatchDraw(Canvas canvas) {
    
            int N = getChildCount() ;
            for (int i = 0; i < N ; i ++){
                View view = getChildAt(i) ;
                if (view.getVisibility() == GONE ||view.getVisibility() == INVISIBLE||
                        view.getAlpha() == 0){
                    continue;
                }
                int left = view.getLeft() ;
                int top = view.getTop() ;
                /*保存画布的位置*/
                canvas.save() ;
                /*平移画布*/
                canvas.translate(left + (1-mDepth)*80,top + (1-mDepth)*80);
                /*设置绘制阴影画笔的透明度*/
                mAlphaPaint.setAlpha((int) (125 + 100 * (mDepth)));
                /*获取阴影的绘制宽度*/
                mDesRecF.right = view.getWidth() ;
                /*获取阴影的绘制高度*/
                mDesRecF.bottom = view.getHeight() ;
                /*绘制阴影*/
                canvas.drawBitmap(mShadowBitmap, mOriginRect, mDesRecF, mAlphaPaint);
                /*还原画笔*/
                canvas.restore();
            }
            super.dispatchDraw(canvas);
    
        }

    到这里整个效果的 Layout 布局就写完了,代码非常简洁,总共100行代码不到,索性就全部贴出来吧

    /**
     * Created by moon.zhong on 2015/3/25.
     */
    public class ShadowLayout extends RelativeLayout {
    
        private float mDepth = 0.5f;
        private Bitmap mShadowBitmap;
        private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        private final int BLUR_WIDTH = 5 ;
        private final Rect mOriginRect = new Rect(0,0,150+ 2*BLUR_WIDTH,150+2*BLUR_WIDTH) ;
        private RectF mDesRecF = new RectF(0,0,150,150) ;
        private int mRadius = 6 ;
        private Paint mAlphaPaint ;
        public ShadowLayout(Context context) {
            super(context);
            initView(context);
        }
    
        public ShadowLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
            initView(context);
        }
    
        public ShadowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            initView(context);
        }
    
        private void initView(Context context) {
            setWillNotDraw(false);
            //设置画笔的 style
            mPaint.setStyle(Paint.Style.FILL);
            //设置画笔的模糊效果
            mPaint.setMaskFilter(new BlurMaskFilter(BLUR_WIDTH, BlurMaskFilter.Blur.NORMAL));
            //设置画笔的颜色
            mPaint.setColor(Color.BLACK);
            //创建 bitmap 图片
            mShadowBitmap = Bitmap.createBitmap(mOriginRect.width(), mOriginRect.height(), Bitmap.Config.ARGB_8888);
            //绑定到画布上
            Canvas canvas = new Canvas(mShadowBitmap);
            //让画布平移,这里为什么要平移,看了前面图片就知道
            canvas.translate(BLUR_WIDTH,BLUR_WIDTH);
            //绘制阴影效果
            canvas.drawRoundRect(mDesRecF, mRadius, mRadius, mPaint);
            mAlphaPaint = new Paint(Paint.ANTI_ALIAS_FLAG) ;
    
        }
    
        public void setDepth(float depth){
            mDepth = depth ;
    
            invalidate();
        }
    
        @Override
        protected void dispatchDraw(Canvas canvas) {
    
            int N = getChildCount() ;
            for (int i = 0; i < N ; i ++){
                View view = getChildAt(i) ;
                if (view.getVisibility() == GONE ||view.getVisibility() == INVISIBLE||
                        view.getAlpha() == 0){
                    continue;
                }
                int left = view.getLeft() ;
                int top = view.getTop() ;
                /*保存画布的位置*/
                canvas.save() ;
                /*平移画布*/
                canvas.translate(left + (1-mDepth)*80,top + (1-mDepth)*80);
                /*设置绘制阴影画笔的透明度*/
                mAlphaPaint.setAlpha((int) (125 + 100 * (mDepth)));
                /*获取阴影的绘制宽度*/
                mDesRecF.right = view.getWidth() ;
                /*获取阴影的绘制高度*/
                mDesRecF.bottom = view.getHeight() ;
                /*绘制阴影*/
                canvas.drawBitmap(mShadowBitmap, mOriginRect, mDesRecF, mAlphaPaint);
                /*还原画笔*/
                canvas.restore();
            }
            super.dispatchDraw(canvas);
    
        }
    
    }

    再来看一下动态的效果图 
    这里写图片描述

    总结

    整体实现不是特别难,最主要的是 Paint类中 的

     mPaint.setMaskFilter(new BlurMaskFilter(BLUR_WIDTH, BlurMaskFilter.Blur.NORMAL));

    在创建 bitmap 的时候,bitmap 的大小一定要加上2倍的BLUR_WIDTH也就是

    private final Rect mOriginRect = new Rect(0,0,150+ 2*BLUR_WIDTH,150+2*BLUR_WIDTH) ;

    至于为什么前面已经讲了。

    http://blog.csdn.net/jxxfzgy/article/details/44644799

  • 相关阅读:
    Taro api封装
    taro中如何定义全局变量
    移动端悬浮框可移动,可回弹,Vue and React
    VS2015 安装mvc4安装包以及vs2010 sp1后导致Razor语法失效代码不高亮(能正常运行)/视图页面无法智能提示(.cshtml)解决办法
    C#6.0新特性的尝试
    Visual Studio Code 添加设置代码段(snippet)
    AngularJs的$http发送POST请求,php无法接收Post的数据解决方案
    [Asp.net mvc] 在Asp.net mvc 中使用MiniProfiler
    [Asp.net mvc]实体更新异常:存储区更新、插入或删除语句影响到了意外的行数(0)。实体在加载后可能被修改或删除。
    win8.1右键新建菜单添加新建php文件
  • 原文地址:https://www.cnblogs.com/shanzei/p/4654225.html
Copyright © 2011-2022 走看看