ImageView / Layouts

Blurring Images – Part 2

Previously we looked at a method which uses RenderScript to blur the section of an image which lies within the bounds of another view. However we didn’t get as far as actually calling that method and see the blurring in action. The reason for this is that we need to think quite carefully about performance, and on this article we shall look in to this further.

The most obvious place to call this method would be in the onDraw method of the parent layout. The more experienced developers reading this are probably already shaking their heads at this suggestion because we should keep our onDraw implementations as lean and efficient as possible, and the code from the previous article contains object creation, bitmap operations, and context switching to RenderScript. Doing this in onDraw will kill our frame rate. But let’s not just take me word for it, we can measure it and, later in the series, we shall do precisely that.

If our layout is fairly static (i.e we’re not going to perform any animations on our layout) then the position and bounds of the blurred area will not change between layout passes, it would make sense to perform the blur operation only when the layout changes, but we must do it after the positions and sizes of all of the Views on the layout have been measured and calculated following the layout change. There’s a really useful trick that we can use here. Firstly we register an OnGlobalLayoutListener – its onGlobalLayout() method will get called each time the layout changes. When we are notified of a layout change we register an OnPreDrawListener whose onPreDraw() method will be called before each invocation of onDraw. The first thing that we do in the onPreDraw() method is to deregister itself so that it only gets called once following a layout change and not each time onDraw is invoked. Next we perform our blur. The return value from this method is important because it allows us to abort the onDraw operation and repeat the layout pass. This can be useful if we have modified the layout in the listener callback but, in this case, we haven’t so we return true to continue with the draw.

So our Activity looks like this:

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) {
	.
	.
	.
	}
}

The advantage that this approach has ahead of overriding onDraw in the parent layout is that we can attach an OnPreDrawListener to any layout in our view hierarchy, so the parent layout needs to be a custom class which subclasses on of the standard layout classes so that we can override onDraw. Using a predrawlistener means that we can simply attach it to any layout out view.

Finally we have a working blur:

part1

In the next article we’ll take a look at precisely why we should try to avoid doing the blur operation in onDraw, and some useful tools for measuring performance.

The source code for this article is available here.

© 2014, Mark Allison. All rights reserved.

Copyright © 2014 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.