Android毛玻璃效果侧滑菜单

来源:互联网 发布:java getservername 编辑:程序博客网 时间:2024/04/27 08:01

实现目标

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

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

第一步:框架搭建

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

android studio会自动创建带有这种侧滑覆盖当前Activity菜单的项目,如果是Eclipse,就得自己去实现了,因为重点不在侧滑菜单上,就不说侧滑菜单的实现了,可以去下面的源码地址看看源码,就能够实现了。

第二步:实现原理

实现之前,得先确认,要通过什么样的方式来实现这个效果。
因为Drawer侧滑菜单背后的View实时都在变动,所以只能将View的图实时的截取下来,进行高斯模糊,再作为Drawer的背景。
这个操作只能在Drawer进行绘制之前完成,因为如果在绘制的时候再进行截取和处理,不仅会卡顿,而且截取的图像不会是现在所需要的。
好在Android中有这样一个事件,叫PreDraw,故名思议,就是绘制之前调用的事件,Android的View在绘制的时候,会首先绘制下一层,那么,拿到View的层级绘制的观察者,当绘制之前,给需要截图的View进行截图然后高斯模糊,设置为Drawer的背景,这样就实现了我们需要的功能。
也许我没有表达清楚,不过没关系,看下面的代码解析,就能够明白了。

第三步:实现代码

首先,需要拿到这个侧滑菜单的控件。android studio生成的侧滑菜单是用Fragment实现的,所以使用如下的代码就能够获取到它:
[java] view plaincopy
  1. mNavigationDrawerFragment = (NavigationDrawerFragment)  
  2.                 getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);  
这个NavigationDrawerFragment就是我们的侧滑菜单实现的类,可以不去管它。
然后需要获取到这个Fragment中的View,获取View的观察者并且注册PreDraw监听事件:
[java] view plaincopy
  1. mNavigationDrawerFragment.getView().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {  
  2.               
  3.         });  
这个OnPreDrawListener需要实现OnPreDraw方法,就在这个方法中,对当前的View,进行截图,并且高斯模糊。
首先找到根控件,并创建一个Bitmap对象来保存截图。
[java] view plaincopy
  1. private FrameLayout view;  
  2. private Bitmap bitmap;  
对view初始化后,开始对view进行截图,对view截图的方式如下:
[java] view plaincopy
  1. view.buildDrawingCache();  
  2. bitmap = view.getDrawingCache();  
这样,bitmap中就保存了当前的view的截图,接下来我们要对图片进行高斯模糊,关于高斯模糊的算法,网上有很多,我就随便贴一个(复制来的):
[java] view plaincopy
  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, 00, 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, 00, w, h);  
  232.   
  233.     return (bitmap);  
  234. }  
第一个参数为需要高斯模糊的图片,第二个参数可以理解为模糊效果大小,第三个参数表示位图是否可以重复使用。
好了,有了处理好的图片,就可以设置到Drawer的背景里了。
。。。
真的吗?
仔细一想,当然不对,现在截的图是整个view的图,而我们的Drawer是慢慢侧滑出来的,随时都可能停止,所以,使用完整的图肯定是不行的。
那么就需要计算出截取view的图片的位置,和设置到Drawer的位置,具体可以参考下图:

从图上可以看的出来,其中需要的参数都是有联系的,也就是说,只要能得到其中的一个参数,其他的就能同时拿到了。
Drawer的宽度可以定死,例子中定的是240,现在就只需要获取到侧滑到哪一个位置就能够算出所有的参数。
在View中,有这样一个方法,叫做getLocationInWindow,可以获取到当前View在整个Window中的位置,可以想象的到,获取到的肯定是一个负数,通过Drawer的宽度和这个负数,可以很简单的算出来显示的宽度。(View中除了getLocationInWindow,还有很多其他的方法,获取相对于各种视图的位置,大家可以研究一下)。
首先获取到位置:
[java] view plaincopy
  1. int[] location = new int[2];  
  2. mNavigationDrawerFragment.getView().getLocationInWindow(location);  
  3. blur(bitmap, listView, location[0]);//只传x坐标  
blur函数就是我们处理位置的细节了,传的listView是Fragment里的唯一一个view:
[java] view plaincopy
  1. private void blur(Bitmap bkg, View view,int width) {  
  2.     float scaleFactor = 4;//缩放图片,缩放之后模糊效果更好  
  3.     float radius = 2;  
  4.   
  5.     Bitmap overlay = Bitmap.createBitmap((int) (view.getMeasuredWidth()/scaleFactor),  
  6.             (int) (view.getMeasuredHeight()/scaleFactor), Bitmap.Config.ARGB_8888);  
  7.     Canvas canvas = new Canvas(overlay);  
  8.     canvas.translate(-view.getLeft()/scaleFactor, -view.getTop()/scaleFactor);  
  9.     canvas.scale(1 / scaleFactor, 1 / scaleFactor);  
  10.     Paint paint = new Paint();  
  11.     paint.setFlags(Paint.FILTER_BITMAP_FLAG);  
  12.     float visibleWidth = slideMenuWidth +width;//可见宽  
  13.     int visibleHeight = view.getHeight();//可见高  
  14.     //从view的截图中截取的区域,+10和下面-10的原因是,高斯模糊的边有时会有黑影,所以增大模糊区域  
  15. t src = new Rect(0,0, (int)(visibleWidth)+10, visibleHeight);          
  16.     RectF dest = new RectF(-width - 100, slideMenuWidth, visibleHeight);//设置Drawer背景的区域                  
  17.     canvas.drawBitmap(bkg, src, dest, paint);          
  18.     overlay = ImageUtils.doBlur(overlay, (int)radius, true);//进行高斯模糊操作          
  19.     if (Build.VERSION.SDK_INT < 16) {//16level以前使用这个方法,在16中被废弃                       
  20.         view.setBackgroundDrawable(new BitmapDrawable(getResources(), overlay));          
  21.     } else {              
  22.         view.setBackground(new BitmapDrawable(getResources(), overlay));          
  23.     }      
  24. }  
整个的事件监听代码如下:
[java] view plaincopy
  1. mNavigationDrawerFragment.getView().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {  
  2.             @Override  
  3.             public boolean onPreDraw() {  
  4.                 if (bitmap == null) {  
  5.                     view.buildDrawingCache();  
  6.                     bitmap = view.getDrawingCache();  
  7.                 }  
  8.                 int[] location = new int[2];  
  9.                 mNavigationDrawerFragment.getView().getLocationInWindow(location);  
  10.                 blur(bitmap, listView, location[0]);//只传x坐标  
  11.                 return true;  
  12.             }  
  13.         });  
到这里,侧滑菜单的毛玻璃效果就已经完成了。

结语

如果能够理解我上面使用的方法,我相信大家一定能够举一反三,实现所有view的毛玻璃效果,这个例子的代码,我会放到github上,这里就是链接地址:https://github.com/xjyaikj/GlassSlideMenuSample,如果代码有什么不对,或者值得优化的地方,非常希望您能跟我交流,我的联系方式在左边个人资料的下面。
0 0
原创粉丝点击