Android毛玻璃效果侧滑菜单

相信大家都知道毛玻璃效果是怎样的,也可以说是高斯模糊效果。效果图如下:

 \
这是一个透明,且会对背景进行高斯模糊的效果,看起来就像是毛玻璃一样,其实不光是侧滑菜单,只要是view,理论上都可以实现这样的效果,接下来我们就来实现这个效果。

第一步:框架搭建

我使用的android studio,所以要创建这样一个带侧滑菜单的项目非常简单,在新建项目的步骤中,执行到这一步,选择Navigation Drawer Activity就可以了: 

这个NavigationDrawerFragment就是我们的侧滑菜单实现的类,可以不去管它。 然后需要获取到这个Fragment中的View,获取View的观察者并且注册PreDraw监听事件:

mNavigationDrawerFragment.getView().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
             
        });

 

这个OnPreDrawListener需要实现OnPreDraw方法,就在这个方法中,对当前的View,进行截图,并且高斯模糊。 首先找到根控件,并创建一个Bitmap对象来保存截图。

1 private FrameLayout view;
2 private Bitmap bitmap;

 

 

对view初始化后,开始对view进行截图,对view截图的方式如下:

view.buildDrawingCache();
bitmap = view.getDrawingCache();

 

 

这样,bitmap中就保存了当前的view的截图,接下来我们要对图片进行高斯模糊,关于高斯模糊的算法,网上有很多,我就随便贴一个(复制来的):

  1 public static Bitmap doBlur(Bitmap sentBitmap, int radius, boolean canReuseInBitmap) {
  2  
  3     // Stack Blur v1.0 from
  4     // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
  5     //
  6     // Java Author: Mario Klingemann <mario at="" quasimondo.com="">
  7     // http://incubator.quasimondo.com
  8     // created Feburary 29, 2004
  9     // Android port : Yahel Bouaziz <yahel at="" kayenko.com="">
 10     // http://www.kayenko.com
 11     // ported april 5th, 2012
 12  
 13     // This is a compromise between Gaussian Blur and Box blur
 14     // It creates much better looking blurs than Box Blur, but is
 15     // 7x faster than my Gaussian Blur implementation.
 16     //
 17     // I called it Stack Blur because this describes best how this
 18     // filter works internally: it creates a kind of moving stack
 19     // of colors whilst scanning through the image. Thereby it
 20     // just has to add one new block of color to the right side
 21     // of the stack and remove the leftmost color. The remaining
 22     // colors on the topmost layer of the stack are either added on
 23     // or reduced by one, depending on if they are on the right or
 24     // on the left side of the stack.
 25     //
 26     // If you are using this algorithm in your code please add
 27     // the following line:
 28     //
 29     // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
 30  
 31     Bitmap bitmap;
 32     if (canReuseInBitmap) {
 33         bitmap = sentBitmap;
 34     } else {
 35         bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
 36     }
 37  
 38     if (radius < 1) {
 39         return (null);
 40     }
 41  
 42     int w = bitmap.getWidth();
 43     int h = bitmap.getHeight();
 44  
 45     int[] pix = new int[w * h];
 46     bitmap.getPixels(pix, 0, w, 0, 0, w, h);
 47  
 48     int wm = w - 1;
 49     int hm = h - 1;
 50     int wh = w * h;
 51     int div = radius + radius + 1;
 52  
 53     int r[] = new int[wh];
 54     int g[] = new int[wh];
 55     int b[] = new int[wh];
 56     int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
 57     int vmin[] = new int[Math.max(w, h)];
 58  
 59     int divsum = (div + 1) >> 1;
 60     divsum *= divsum;
 61     int dv[] = new int[256 * divsum];
 62     for (i = 0; i < 256 * divsum; i++) {
 63         dv[i] = (i / divsum);
 64     }
 65  
 66     yw = yi = 0;
 67  
 68     int[][] stack = new int[div][3];
 69     int stackpointer;
 70     int stackstart;
 71     int[] sir;
 72     int rbs;
 73     int r1 = radius + 1;
 74     int routsum, goutsum, boutsum;
 75     int rinsum, ginsum, binsum;
 76  
 77     for (y = 0; y < h; y++) {
 78         rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
 79         for (i = -radius; i <= radius; i++) {
 80             p = pix[yi + Math.min(wm, Math.max(i, 0))];
 81             sir = stack[i + radius];
 82             sir[0] = (p & 0xff0000) >> 16;
 83             sir[1] = (p & 0x00ff00) >> 8;
 84             sir[2] = (p & 0x0000ff);
 85             rbs = r1 - Math.abs(i);
 86             rsum += sir[0] * rbs;
 87             gsum += sir[1] * rbs;
 88             bsum += sir[2] * rbs;
 89             if (i > 0) {
 90                 rinsum += sir[0];
 91                 ginsum += sir[1];
 92                 binsum += sir[2];
 93             } else {
 94                 routsum += sir[0];
 95                 goutsum += sir[1];
 96                 boutsum += sir[2];
 97             }
 98         }
 99         stackpointer = radius;
100  
101         for (x = 0; x < w; x++) {
102  
103             r[yi] = dv[rsum];
104             g[yi] = dv[gsum];
105             b[yi] = dv[bsum];
106  
107             rsum -= routsum;
108             gsum -= goutsum;
109             bsum -= boutsum;
110  
111             stackstart = stackpointer - radius + div;
112             sir = stack[stackstart % div];
113  
114             routsum -= sir[0];
115             goutsum -= sir[1];
116             boutsum -= sir[2];
117  
118             if (y == 0) {
119                 vmin[x] = Math.min(x + radius + 1, wm);
120             }
121             p = pix[yw + vmin[x]];
122  
123             sir[0] = (p & 0xff0000) >> 16;
124             sir[1] = (p & 0x00ff00) >> 8;
125             sir[2] = (p & 0x0000ff);
126  
127             rinsum += sir[0];
128             ginsum += sir[1];
129             binsum += sir[2];
130  
131             rsum += rinsum;
132             gsum += ginsum;
133             bsum += binsum;
134  
135             stackpointer = (stackpointer + 1) % div;
136             sir = stack[(stackpointer) % div];
137  
138             routsum += sir[0];
139             goutsum += sir[1];
140             boutsum += sir[2];
141  
142             rinsum -= sir[0];
143             ginsum -= sir[1];
144             binsum -= sir[2];
145  
146             yi++;
147         }
148         yw += w;
149     }
150     for (x = 0; x < w; x++) {
151         rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
152         yp = -radius * w;
153         for (i = -radius; i <= radius; i++) {
154             yi = Math.max(0, yp) + x;
155  
156             sir = stack[i + radius];
157  
158             sir[0] = r[yi];
159             sir[1] = g[yi];
160             sir[2] = b[yi];
161  
162             rbs = r1 - Math.abs(i);
163  
164             rsum += r[yi] * rbs;
165             gsum += g[yi] * rbs;
166             bsum += b[yi] * rbs;
167  
168             if (i > 0) {
169                 rinsum += sir[0];
170                 ginsum += sir[1];
171                 binsum += sir[2];
172             } else {
173                 routsum += sir[0];
174                 goutsum += sir[1];
175                 boutsum += sir[2];
176             }
177  
178             if (i < hm) {
179                 yp += w;
180             }
181         }
182         yi = x;
183         stackpointer = radius;
184         for (y = 0; y < h; y++) {
185             // Preserve alpha channel: ( 0xff000000 & pix[yi] )
186             pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
187  
188             rsum -= routsum;
189             gsum -= goutsum;
190             bsum -= boutsum;
191  
192             stackstart = stackpointer - radius + div;
193             sir = stack[stackstart % div];
194  
195             routsum -= sir[0];
196             goutsum -= sir[1];
197             boutsum -= sir[2];
198  
199             if (x == 0) {
200                 vmin[y] = Math.min(y + r1, hm) * w;
201             }
202             p = x + vmin[y];
203  
204             sir[0] = r[p];
205             sir[1] = g[p];
206             sir[2] = b[p];
207  
208             rinsum += sir[0];
209             ginsum += sir[1];
210             binsum += sir[2];
211  
212             rsum += rinsum;
213             gsum += ginsum;
214             bsum += binsum;
215  
216             stackpointer = (stackpointer + 1) % div;
217             sir = stack[stackpointer];
218  
219             routsum += sir[0];
220             goutsum += sir[1];
221             boutsum += sir[2];
222  
223             rinsum -= sir[0];
224             ginsum -= sir[1];
225             binsum -= sir[2];
226  
227             yi += w;
228         }
229     }
230  
231     bitmap.setPixels(pix, 0, w, 0, 0, w, h);
232  
233     return (bitmap);
234 }

 

 

第一个参数为需要高斯模糊的图片,第二个参数可以理解为模糊效果大小,第三个参数表示位图是否可以重复使用。 好了,有了处理好的图片,就可以设置到Drawer的背景里了。 。。。 真的吗? 仔细一想,当然不对,现在截的图是整个view的图,而我们的Drawer是慢慢侧滑出来的,随时都可能停止,所以,使用完整的图肯定是不行的。 那么就需要计算出截取view的图片的位置,和设置到Drawer的位置,具体可以参考下图: \
从图上可以看的出来,其中需要的参数都是有联系的,也就是说,只要能得到其中的一个参数,其他的就能同时拿到了。 Drawer的宽度可以定死,例子中定的是240,现在就只需要获取到侧滑到哪一个位置就能够算出所有的参数。 在View中,有这样一个方法,叫做getLocationInWindow,可以获取到当前View在整个Window中的位置,可以想象的到,获取到的肯定是一个负数,通过Drawer的宽度和这个负数,可以很简单的算出来显示的宽度。(View中除了getLocationInWindow,还有很多其他的方法,获取相对于各种视图的位置,大家可以研究一下)。
首先获取到位置:

 

int[] location = new int[2];
mNavigationDrawerFragment.getView().getLocationInWindow(location);
blur(bitmap, listView, location[0]);//只传x坐标

 

blur函数就是我们处理位置的细节了,传的listView是Fragment里的唯一一个view:

 

private void blur(Bitmap bkg, View view,int width) {
    float scaleFactor = 4;//缩放图片,缩放之后模糊效果更好
    float radius = 2;
 
    Bitmap overlay = Bitmap.createBitmap((int) (view.getMeasuredWidth()/scaleFactor),
            (int) (view.getMeasuredHeight()/scaleFactor), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(overlay);
    canvas.translate(-view.getLeft()/scaleFactor, -view.getTop()/scaleFactor);
    canvas.scale(1 / scaleFactor, 1 / scaleFactor);
    Paint paint = new Paint();
    paint.setFlags(Paint.FILTER_BITMAP_FLAG);
    float visibleWidth = slideMenuWidth +width;//可见宽
    int visibleHeight = view.getHeight();//可见高
    //从view的截图中截取的区域,+10和下面-10的原因是,高斯模糊的边有时会有黑影,所以增大模糊区域
    Rect src = new Rect(0,0, (int)(visibleWidth)+10, visibleHeight);        
    RectF dest = new RectF(-width - 10, 0, slideMenuWidth, visibleHeight);//设置Drawer背景的区域                
    canvas.drawBitmap(bkg, src, dest, paint);        
    overlay = ImageUtils.doBlur(overlay, (int)radius, true);//进行高斯模糊操作        
    if (Build.VERSION.SDK_INT < 16) {//16level以前使用这个方法,在16中被废弃                     
        view.setBackgroundDrawable(new BitmapDrawable(getResources(), overlay));        
    } else {            
        view.setBackground(new BitmapDrawable(getResources(), overlay));        
    }    
}

 

整个的事件监听代码如下:

 

mNavigationDrawerFragment.getView().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                if (bitmap == null) {
                    view.buildDrawingCache();
                    bitmap = view.getDrawingCache();
                }
                int[] location = new int[2];
                mNavigationDrawerFragment.getView().getLocationInWindow(location);
                blur(bitmap, listView, location[0]);//只传x坐标
                return true;
            }
 });

 

 

到这里,侧滑菜单的毛玻璃效果就已经完成了。

posted @ 2016-04-20 13:50  HolySee  阅读(553)  评论(0编辑  收藏  举报