转-Android模糊图像教程

 

Android模糊图像教程(2)

前面我们介绍了使用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
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意味着可以非常容易在任意布局中添加。

最后,模糊图片实现如下:

part1

在下一篇文章中,我会更深入地回答为什么要避免在onDraw中执行模糊操作,并且还会介绍有用的性能测量工具。

这篇文章的代码在这里