Android毛玻璃效果侧滑菜单
2016-04-20 13:50
495 查看
相信大家都知道毛玻璃效果是怎样的,也可以说是高斯模糊效果。效果图如下:
这是一个透明,且会对背景进行高斯模糊的效果,看起来就像是毛玻璃一样,其实不光是侧滑菜单,只要是view,理论上都可以实现这样的效果,接下来我们就来实现这个效果。
这个NavigationDrawerFragment就是我们的侧滑菜单实现的类,可以不去管它。 然后需要获取到这个Fragment中的View,获取View的观察者并且注册PreDraw监听事件:
这个OnPreDrawListener需要实现OnPreDraw方法,就在这个方法中,对当前的View,进行截图,并且高斯模糊。 首先找到根控件,并创建一个Bitmap对象来保存截图。
对view初始化后,开始对view进行截图,对view截图的方式如下:
这样,bitmap中就保存了当前的view的截图,接下来我们要对图片进行高斯模糊,关于高斯模糊的算法,网上有很多,我就随便贴一个(复制来的):
第一个参数为需要高斯模糊的图片,第二个参数可以理解为模糊效果大小,第三个参数表示位图是否可以重复使用。 好了,有了处理好的图片,就可以设置到Drawer的背景里了。 。。。 真的吗? 仔细一想,当然不对,现在截的图是整个view的图,而我们的Drawer是慢慢侧滑出来的,随时都可能停止,所以,使用完整的图肯定是不行的。 那么就需要计算出截取view的图片的位置,和设置到Drawer的位置,具体可以参考下图:
从图上可以看的出来,其中需要的参数都是有联系的,也就是说,只要能得到其中的一个参数,其他的就能同时拿到了。 Drawer的宽度可以定死,例子中定的是240,现在就只需要获取到侧滑到哪一个位置就能够算出所有的参数。 在View中,有这样一个方法,叫做getLocationInWindow,可以获取到当前View在整个Window中的位置,可以想象的到,获取到的肯定是一个负数,通过Drawer的宽度和这个负数,可以很简单的算出来显示的宽度。(View中除了getLocationInWindow,还有很多其他的方法,获取相对于各种视图的位置,大家可以研究一下)。
首先获取到位置:
blur函数就是我们处理位置的细节了,传的listView是Fragment里的唯一一个view:
整个的事件监听代码如下:
到这里,侧滑菜单的毛玻璃效果就已经完成了。
这是一个透明,且会对背景进行高斯模糊的效果,看起来就像是毛玻璃一样,其实不光是侧滑菜单,只要是view,理论上都可以实现这样的效果,接下来我们就来实现这个效果。
第一步:框架搭建
我使用的android studio,所以要创建这样一个带侧滑菜单的项目非常简单,在新建项目的步骤中,执行到这一步,选择Navigation Drawer Activity就可以了:这个NavigationDrawerFragment就是我们的侧滑菜单实现的类,可以不去管它。 然后需要获取到这个Fragment中的View,获取View的观察者并且注册PreDraw监听事件:
这个OnPreDrawListener需要实现OnPreDraw方法,就在这个方法中,对当前的View,进行截图,并且高斯模糊。 首先找到根控件,并创建一个Bitmap对象来保存截图。
private FrameLayout view; 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;
}
});
到这里,侧滑菜单的毛玻璃效果就已经完成了。
相关文章推荐
- Android知识梳理之事件传递机制
- Android Studio之版本管理工具Git (图文教程)
- 基于UT4418编写自己的第一个Android程序 (五)
- Android SDK中tools详解
- Android 实现简易下载管理器 (暂停、断点续传、多线程下载)
- android设备信息
- Android edittext 禁止复制粘贴问题解决方案
- 在用Android studio进行单元测试时,getContext为null
- Android Resource介绍和使用
- 86、Android 通知栏Notification的整合 全面学习
- Android 使用ListView显示信息列表
- 你应该知道的那些Android小经验
- 85、android handler的警告Handler Class Should be Static or Leaks Occur
- android studio
- android studio
- android应用发布流程
- android studio 2.0 GPU Debugger使用说明
- Android中ViewPager的使用(二):实现图片轮播效果
- Android 使用VDPlayerSDK组件实现播放视频(二)
- Android 加密 MD5和Base64简单介绍