前面我们介绍了使用RenderScript使另一个视图范围内的图片部分模糊。但是实际上,我们并没有深入地调用这个方法来研究图像模糊行为。原因是我们需要在性能方面进行仔细考虑,这篇文章我们会进行更进一步地的探索。
调用这个方法最直白的方法是父布局的onDraw()
。有经验的开发者读到这里可能会开始摇头,我们应该保持onDraw
方法的实现尽可能有效。以前的文章中的代码包括创建对象、位图操作和切换到renderScript
上下文。其中,OnDraw
会降低帧速率。你可以不相信我的做法,但是可以通过测量并证明它是有效的。在后面的系列中,我们就会这样做。
如果布局是静态的(即我们的布局不包含任何动画),在布局时就不会改变待模糊的位置和范围。只有在布局改变时执行该操作才有意义,但前提是布局的所有视图大小和位置根据布局变化测量和计算过。这里有一个非常实用的技巧,可以注册一个OnGlobalLayoutListener
监听函数。当布局发生改变的时候会调用onGlobalLayout()
方法。当我们收到布局已经改变的通知时,注册的OnPreDrawListener
监听函数的onPreDraw()
方法会被调用每当执行onDraw
方法。我们要做的第一件事情就是取消注册onPreDraw()
方法,这样只有在布局改变的时候才会被调用,而不是每次onDraw
方法触发时都调用。下面可以执行模糊方法,从这个方法的返回值很重要,使用它可以让我们放弃onDraw
操作,重复之前的布局。这对在回调函数中修改布局非常有帮助,但是这里不需要这么做。所以返回true,继续绘制。
我们的Activity
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
; html-script: false ] public class MainActivity extends Activity { private ImageView mImage; private TextView mText; private OnPreDrawListener mPreDrawListener = new OnPreDrawListener() { @Override public boolean onPreDraw() { ViewTreeObserver observer = mText.getViewTreeObserver(); if(observer != null) { observer.removeOnPreDrawListener(this); } Drawable drawable = mImage.getDrawable(); if (drawable != null && drawable instanceof BitmapDrawable) { Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); if (bitmap != null) { blur(bitmap, mText, 25); } } return true; } }; private OnGlobalLayoutListener mLayoutListener = new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { ViewTreeObserver observer = mText.getViewTreeObserver(); if(observer != null) { observer.addOnPreDrawListener( mPreDrawListener); } } }; /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mImage = (ImageView) findViewById(R.id.image); mText = (TextView)findViewById(R.id.text); if (mImage != null && mText != null) { ViewTreeObserver observer = mText.getViewTreeObserver(); if (observer != null) { observer.addOnGlobalLayoutListener( mLayoutListener); } } } private void blur(Bitmap bkg, View view, float radius) { . . . } } |
在父布局覆盖onDraw
方法的优点是,可以在布局层次上任意附加OnPreDrawListener
方法。因此需要自定义一个布局,这个布局是标准布局的子类,这样就可以覆盖onDraw
方法。使用predrawlistener
意味着可以非常容易在任意布局中添加。
最后,模糊图片实现如下:
在下一篇文章中,我会更深入地回答为什么要避免在onDraw
中执行模糊操作,并且还会介绍有用的性能测量工具。
这篇文章的代码在这里。