侧边栏高斯模糊(二)
- 闲言碎语
很长一段时间没来写博客了,绝大部分是自己的惰性导致,当然工作压力大也没什么想写的也是一个因素.现在这个点,睡不着,又感觉有说不完的话,于是乎,拿出电脑要把灵感全倒出来...
还是接着前面说过的侧边栏高斯模糊的效果,今天说的效果要好很多,这里说的侧边栏高斯模糊具体指的是侧拉出多少宽度,就看到下面有多少区域模糊.今天录个清晰点的屏,上需求效果图:
- 分析思路
先引入一个"图层"的概念,约定视图按照显示层级划分,由里向外依次为 主背景图层、模糊图层、侧边栏图层;
后面的主背景会改变(先忽略播放条的滚动),每次展开抽屉时看到的模糊图层视图基本不一样,这就要求能动态获取主背景的快照 -->view有个getDrawingCache()的方法可以获取屏幕快照
模糊效果 -- > 给特哈博上找星多的,看下已知缺陷和Issue维护情况,一般就是它了
是拉出多少,显示多少宽度的模糊,实时获取屏幕快照? too naive!这本来就是个费时耗gpu的操作,别想太多了.
那怎么办?想到一个以假乱真的办法,打肿脸充下胖子~
先把模糊图片生成了,再一点一点滑出来. -->关联cover_left.setVisibily()设置控件的可见性(可见 不可见) setLayoutParams()设置控件的布局参数(宽 高)
处理时机,什么时候给主背景来一套截屏裁剪模糊的操作?主背景改变的时候?侧边栏开始展开的时候?如何判断主背景渲染完成的时机?侧边栏点击展开可以掌握,那侧拉操作呢?
- 实现步骤
- 提前获取屏幕快照 -->裁剪与侧边栏同宽高的区域 --> 生成模糊图片 -->设置模糊图层不可见
- 设置DrawerLayout(也可自己实现侧边栏)的抽屉监听,滑动多少,就把模糊图层区域的宽度设置为多少(当然了,只要开始滑动了就设置其可见)
- 关键操作一:cover_leftblur.setScaleType(ImageView.ScaleType.MATRIX);matrix表示原图从ImageView的左上角开始绘制,如果原图大于ImageView,那么多余的部分则剪裁掉,如果原图小于ImageView,那么对原图不做任何处理
关键操作二: //把大象装进冰箱分三步走
ViewGroup.LayoutParams layoutParams = cover_leftblur.getLayoutParams(); // 1.把冰箱门打开
layoutParams.width = (int) (slip_per * CommonUtils.dip2px(getContext(), 531)); // 2.把大象装进去
cover_leftblur.setLayoutParams(layoutParams); //3.把冰箱门关上
- 代码
Talk is cheap, show me the code.
mainActivity.getMain_content().setDrawingCacheEnabled(true);//打开view cache的开关 mainActivity.getMain_content().buildDrawingCache(); //生成cache Bitmap drawingCache = mainActivity.getMain_content().getDrawingCache(); //获取生成cache的bitmap
//根据这个bitmap生成一张与侧边栏同宽高的bitmap 参数2,3,4,5 分别为在屏幕上显示的左上右下 Bitmap cropBitmap = Bitmap.createBitmap(drawingCache, 0, 0, CommonUtils.dip2px(getContext(), 531), mainActivity.getMain_content().getHeight(), null, false); mainActivity.getCover_leftblur().setImageBitmap(BlurKit.getInstance().blur(cropBitmap, 8));//BlurKit是个开源的模糊框架,据此生成模糊图片,并设置到模糊视图上 mainActivity.getCover_leftblur().setVisibility(View.INVISIBLE); //先藏起来,开始滑动再露出来 mainActivity.getCover_leftblur().destroyDrawingCache(); //销毁cache mainActivity.getMain_content().setDrawingCacheEnabled(false); //关闭view cache的开关 drawerLayout.openDrawer(Gravity.LEFT, true); //展开抽屉
cover_leftblur = (ImageView) findViewById(R.id.cover_leftblur); cover_leftblur.setScaleType(ImageView.ScaleType.MATRIX);//很关键
//得到开关对象 mToggle = new ActionBarDrawerToggle(this, mDrawlayout, R.string.open, R.string.close) { /** * 当抽屉滑动状态改变的时候被调用 * 状态值是STATE_IDLE(闲置--0), STATE_DRAGGING(拖拽的--1), STATE_SETTLING(固定--2)中之一。 * 抽屉打开的时候,点击抽屉,drawer的状态就会变成STATE_DRAGGING,然后变成STATE_IDLE */ @Override public void onDrawerStateChanged(int arg0) { Log.i("drawer的状态", "" + arg0); } /** * 当抽屉被滑动的时候调用此方法 * arg1 表示 滑动的幅度(0-1) */ @Override public void onDrawerSlide(View arg0, float arg1) { cover_leftblur.setVisibility(View.VISIBLE); ViewGroup.LayoutParams layoutParams = cover_leftblur.getLayoutParams(); layoutParams.width = (int) (arg1 * CommonUtils.dip2px(getContext(), 531)); cover_leftblur.setLayoutParams(layoutParams); } /** * 当一个抽屉被完全打开的时候被调用 */ @Override public void onDrawerOpened(View arg0) { Log.i("drawer", "抽屉被完全打开了!"); } /** * 当一个抽屉完全关闭的时候调用此方法 */ @Override public void onDrawerClosed(View arg0) { Log.i("drawer", "抽屉被完全关闭了!"); cover_leftblur.setVisibility(View.INVISIBLE); } };
到这里就实现了这个特殊的模糊效果,展示一下实现的效果图:
但是有个问题还未解决,处理时机是什么?
我觉得如果侧边栏要求不能侧滑出来的话,无疑放在张开侧边栏按钮的点击回调了最恰当,原因有二:其一,主背景更新频繁,暂停播放的时候会有播放控制按钮出现,这些也应该出现在模糊区域,点击就处理,处理的结果是最新的,无论播放状态是什么,不会出现模糊区域显示不同步的情况(进度条除外);其二,主背景渲染完成的监听无疑是不行的,界面更新太频繁了,底部的进度条一直在跑,监听的回调一直在触发,cpu占用率过高是个很大的问题;现在要求,侧边栏能侧滑出来,于是考虑侧边栏有没有开始展开的监听,如果每次开始展开,我就处理一次,无疑是处理次数是最少的(主背景更改了,用户也可能不展开侧边栏).
这个处理时机的问题后续还要解决,先写到这,如果有什么好的建议,欢迎留言交流一下.