<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>KevinDion.com</title>
	<atom:link href="http://kevindion.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://kevindion.com</link>
	<description>Musings and random thoughts of Kevin Dion</description>
	<lastBuildDate>Sun, 27 Feb 2011 15:38:41 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>Introducing FuelTrack for Android</title>
		<link>http://kevindion.com/2011/02/introducing-fueltrack-for-android/</link>
		<comments>http://kevindion.com/2011/02/introducing-fueltrack-for-android/#comments</comments>
		<pubDate>Sat, 26 Feb 2011 19:20:11 +0000</pubDate>
		<dc:creator>Kevin Dion</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[Works]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[fueltrack]]></category>

		<guid isPermaLink="false">http://kevindion.com/?p=588</guid>
		<description><![CDATA[I just launched my first (official) app in the Android market &#8211; FuelTrack! Basically, it&#8217;s an app that tracks your fuel purchases and uses that data to give you lots of information about your fuel consumption, spending, and efficiency! There are plenty of details and more information on the apps page, so check it out!]]></description>
				<content:encoded><![CDATA[<p><img src="http://kevindion.com/blog/wp-content/uploads/2011/02/promo.png" alt="Fuel Track image" title="promo" width="180" height="120" class="aligncenter size-full wp-image-589" /></p>
<p>I just launched my first (official) app in the Android market &#8211; FuelTrack! Basically, it&#8217;s an app that tracks your fuel purchases and uses that data to give you lots of information about your fuel consumption, spending, and efficiency! There are plenty of details and more information on the <a href="http://kevindion.com/apps/">apps page</a>, so check it out!</p>
]]></content:encoded>
			<wfw:commentRss>http://kevindion.com/2011/02/introducing-fueltrack-for-android/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Custom XML Attributes For Your Custom Android Widgets</title>
		<link>http://kevindion.com/2011/01/custom-xml-attributes-for-android-widgets/</link>
		<comments>http://kevindion.com/2011/01/custom-xml-attributes-for-android-widgets/#comments</comments>
		<pubDate>Fri, 28 Jan 2011 03:16:36 +0000</pubDate>
		<dc:creator>Kevin Dion</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[custom layout]]></category>
		<category><![CDATA[custom xml attributes]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://kevindion.com/?p=561</guid>
		<description><![CDATA[In the last part of my Custom UI Tutorial, commenter Knut asked: Is there an easy way to declare the listener in XML (similar to the android:onClick=”onMyButtonClick” in the stock controls)? This is a great question, and in answering his question, I&#8217;ll go ahead and show you how to create other custom attributes you can [...]]]></description>
				<content:encoded><![CDATA[<p>In the last part of my <a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-3">Custom UI Tutorial</a>, commenter Knut <a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-3/#comment-108">asked</a>:</p>
<blockquote><p>Is there an easy way to declare the listener in XML (similar to the android:onClick=”onMyButtonClick” in the stock controls)?</p></blockquote>
<p>This is a great question, and in answering his question, I&#8217;ll go ahead and show you how to create other custom attributes you can put in the XML for your custom widgets. The goal will be something like this in your layouts:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;com.kdion.tutorial.MyCustomWidget
	android:id=&quot;@+id/theId&quot;
	&lt;!-- ...more stuff here... --&gt;
	custom:myText=&quot;Something&quot;
	custom:fancyColors=&quot;true&quot;
	custom:onAction=&quot;myDoSomething&quot;
	/&gt;
</pre>
<p>We have a custom <code>View </code>called <code>MyCustomWidget</code>, and along with the standard <code>android:</code> attributes, we have a few of our own: <code>myText="Something"</code>, <code>fancyColors="true"</code>, and <code>onAction="doSomething"</code>. Presumably, these would map to attributes we already have for our custom widget which we could set in code, but we want a shortcut to set them from XML.<br />
<span id="more-561"></span></p>
<h2>Step 1: attrs.xml</h2>
<p>The first step to enable us to use our custom xml attributes is to define them for the android system. To do this, you need to create a new xml file under <code>res/values/</code> and call it <code>attrs.xml</code>:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;resources&gt;
	&lt;declare-styleable name=&quot;MyCustomWidget&quot;&gt;
		&lt;attr name=&quot;myText&quot; format=&quot;string&quot;/&gt;
		&lt;attr name=&quot;fancyColors&quot; format=&quot;boolean&quot;/&gt;
		&lt;attr name=&quot;onAction&quot; format=&quot;string&quot;/&gt;
	&lt;/declare-styleable&gt;
&lt;/resources&gt;
</pre>
<p>The first thing you see inside the requisite <code>resources </code>tag is a <code>declare-styleable</code> tag, with <code>name="MyCustomWidget"</code>. This lets android know what it is we want to add our attributes to (the Java class name). As far as I know you can only do this with your own widgets &#8211; I don&#8217;t think you can put, say, <code>declare-styleable name="EditText"</code> (not that I could think of why you would <em>want </em>to, but still). Next, we have out custom attributes in <code>attr </code>tags. The name= value is what we want to use in our xml, and the format= is the type of attribute. There are a number of different types, including string, integer, float, dimension, boolean, float, resource, enum, and color. There may be one or two more, but I&#8217;m having a hard time finding where they are defined.</p>
<h2>Step 2: Add logic to read in XML attributes to your custom widget</h2>
<p>Now that we have a way to pass in attributes from XML to our widget, we need to read them in. This code will go in the constructors that get passed an <code>AttributeSet</code>, since that is what contains the XML attributes. To read the values in the XML, you need to first create a <code>TypedArray </code>from the <code>AttributeSet</code>, then use that to read the values:</p>
<pre class="brush: java; title: ; notranslate">
TypedArray a = context.obtainStyledAttributes(attrs,
	R.styleable.MyCustomWidget);

final int N = a.getIndexCount();
for (int i = 0; i &lt; N; ++i)
{
	int attr = a.getIndex(i);
	switch (attr)
	{
		case R.styleable.MyCustomWidget_myText:
			String myText = a.getString(attr);
			//...do something with myText...
			break;
		case R.styleable.MyCustomWidget_fancyColors:
			boolean fancyColors = a.getBoolean(attr, false);
			//...do something with fancyColors...
			break;
		case R.styleable.MyCustomWidget_onAction:
			String onAction = a.getString(attr);
			//...we'll setup the callback in a bit...
			break;
	}
}
a.recycle();
</pre>
<p>The first call to <code>obtainStyledAttributes </code>passes in the <code>AttributeSet </code>we get in the constructor, and we also pass in <code>R.styleable.MyCustomWidget</code>, which tells the system to just return those attributes we defined in our <code>attrs.xml</code>. Next, we simply loop over each of the attributes we get, then perform a switch on the index, which is really just an int we can tie back to the name in <code>attrs.xml</code>. The convention for the attribute &#8216;name&#8217; or &#8216;index&#8217; or whatever you want to call it is just:</p>
<pre class="brush: plain; title: ; notranslate">
R.styleable.ClassName_attributeName
</pre>
<p>Thus <code>R.styleable.MyCustomWidget_myText</code>, <code>R.styleable.MyCustomWidget_fancyColors</code>, etc. An alternative way to read out the values is </p>
<pre class="brush: java; title: ; notranslate">
TypedArray a = context.obtainStyledAttributes(attrs,
	R.styleable.MyCustomWidget);

String myText = a.getString(R.styleable.MyCustomWidget_myText);
//...do something with myText...

boolean fancyColors = a.getBoolean(R.styleable.MyCustomWidget_fancyColors, false);
//...do something with fancyColors...
</pre>
<p>But I like the <code>switch</code> method because then we only try to read the attributes that were present in the XML. It may not be a big deal if you only have one or two attributes, but if you have a lot, it could save processing time to only read available attributes. Plus that is how Google does it in <code>View</code>&#8230;so until I have a firmer grasp of best practices in android than the creators, I&#8217;ll try to use their conventions.</p>
<p>Also, note this doesn&#8217;t really show you how to use an XML attribute as a callback, but we&#8217;ll see that later.</p>
<h2>Step 3: Add the attributes to the XML</h2>
<p>The final step is to add our new custom attributes to the layout XML:</p>
<pre class="brush: xml; highlight: [3]; title: ; notranslate">
&lt;LinearLayout
  xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
  xmlns:custom=&quot;http://schemas.android.com/apk/res/com.kdion.tutorial&quot;
  android:layout_width=&quot;fill_parent&quot;
  android:layout_height=&quot;fill_parent&quot;
  android:orientation=&quot;vertical&quot;
  &gt;
	&lt;com.kdion.tutorial.MyCustomWidget
		android:id=&quot;@+id/theId&quot;
		android:layout_width=&quot;wrap_content&quot;
		android:layout_height=&quot;wrap_content&quot;
		custom:myText=&quot;Something&quot;
		custom:fancyColors=&quot;true&quot;
		custom:onAction=&quot;myDoSomething&quot;
		/&gt;
&lt;!-- Other stuff --&gt;
&lt;/LinearLayout&gt;
</pre>
<p>The important line is the additional <code>xmlns </code>in the root element, right after <code>xmlns:android</code>. The url you pass as the argument is <code>"http://schemas.android.com/apk/res/your.package.namespace"</code>. Note also that you can use anything after <code>xmlns</code>:, in this example I used <code>custom</code>, but you could use app, mine, foobar, whatever (well not what<em>ever</em>, but you get the idea). Just make sure you use the same prefix when you use your custom attributes in your widget.</p>
<p>And that&#8217;s it! You now have the ability to define and use your own custom XML attributes in your layouts.</p>
<h2>Using a custom attribute for a callback</h2>
<p>So, the original question is how you can setup a custom XML attribute to use as a method callback, like <code>android:onClick="myMethod"</code> for the stock controls. As we saw above, the first step is to simply define our desired attribute to accept a string (the method name we will call), but once we&#8217;ve read it in, what do we do? At first, I had no idea &#8211; but fortunately, Android already does this, and it is open source. So, it was simply a matter of tracking down where in the source code android deals with the <code>onClick </code>setting, and we can use that as a guide for how to handle our custom callback.</p>
<p>I finally found the relevant code in <code>android.view.View</code>. It is in the section of the constructor where it is reading the passed in attributes, dealing specifically with <code>R.styleable.View_onClick</code>. As of API 9 (2.3), you can see the code <a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/java/android/view/View.java;h=fc9ad5325fa587ac8116d021c7bfef3d7f531d74;hb=HEAD#l2110">here</a>. I won&#8217;t reproduce the whole thing here, since the case logic is pretty complicated. But, I will do my best to strip down the logic to pseudocode, so I can try to explain it, and you can follow along (with the actual source is best).</p>
<pre class="brush: java; highlight: [8,14,21]; title: ; notranslate">
case R.styleable.View_onClick:
	if (context.isRestricted()) {
		throw IllegalStateException();
	}

	final String handlerName = a.getString(attr);
	if (handlerName != null) {
		setOnClickListener(new OnClickListener() {
			private Method mHandler;

			public void onClick(View v) {
				if (mHandler == null) {
					try {
						mHandler = getContext().getClass().getMethod(handlerName, View.class);
					} catch (NoSuchMethodException e) {
						throw IllegalStateException();
					}
				}

				try {
					mHandler.invoke(getContext(), View.this);
				} catch (IllegalAccessException e) {
					throw IllegalStateException();
				} catch (InvocationTargetException e) {
					throw IllegalStateException();
				}
			}
		});
	}
	break;
</pre>
<p>The first thing the code does is to check if the <code>context </code>(passed into the constructor) is restricted, and if so throws an exception. Otherwise, <code>setOnClickListener </code>is called, with an anonymous created <code>OnClickListener</code>. You would obviously replace this with your own listener set method and class. </p>
<p>The next important bit of code is when a <code>Method </code>is retrieved from the <code>Context</code>&#8216;s <code>Class </code>(usually our <code>Activity</code>), with the name we passed into the xml attribute. The <code>View.class</code> parameter passed to <code>getMethod </code>is passed because the <code>onClick </code>methos takes a <code>View </code>as an argument. So if your callback method had no arguments, you wouldn&#8217;t pass a second argument into <code>getMethod</code>.</p>
<p>Finally, the <code>Method </code>is invoked, once again passing in the <code>View </code>as an argument. The code above is not really valid, and it&#8217;s missing a lot of information in the exception methods you wouldn;t want to take out in your own code.</p>
<p>But anyways, that&#8217;s how you would use your own custom xml attribute to define a callback you can set in xml!</p>
]]></content:encoded>
			<wfw:commentRss>http://kevindion.com/2011/01/custom-xml-attributes-for-android-widgets/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Android Bitmap Blending &#8211; Color Channels</title>
		<link>http://kevindion.com/2011/01/android-bitmap-blending-color-channels/</link>
		<comments>http://kevindion.com/2011/01/android-bitmap-blending-color-channels/#comments</comments>
		<pubDate>Tue, 18 Jan 2011 02:22:48 +0000</pubDate>
		<dc:creator>Kevin Dion</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[alpha]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[Bitmap]]></category>
		<category><![CDATA[blending]]></category>
		<category><![CDATA[PorterDuff]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[Xfermode]]></category>

		<guid isPermaLink="false">http://kevindion.com/?p=540</guid>
		<description><![CDATA[I recently was doing some investigation into doing image blending in android, and came across the quite powerful built-in capabilities of Paint. I was wondering if it was possible to combine several grayscale images into a color image, using the grayscale images as the color channels. If you are familiar with Photoshop or other graphics-editing [...]]]></description>
				<content:encoded><![CDATA[<p>I recently was doing some investigation into doing image blending in android, and came across the quite powerful built-in capabilities of <code>Paint</code>. I was wondering if it was possible to combine several grayscale images into a color image, using the grayscale images as the color channels. If you are familiar with Photoshop or other graphics-editing programs, you are probably familiar with the concept of color channels, and how they are combined to create a color image. For this example, we will re-create the following image from its separated color channels:<br />
<div id="attachment_545" class="wp-caption aligncenter" style="width: 510px"><a href="http://www.flickr.com/photos/beger/2356202266/"><img src="http://kevindion.com/blog/wp-content/uploads/2011/01/original.jpg" alt="The original, full color image" title="Original Image" width="500" height="333" class="size-full wp-image-545" /></a><p class="wp-caption-text">The original image. Original by Steve Berger Photography via flikr</p></div><span id="more-540"></span></p>
<h3>Separating the image</h3>
<p>To start off with, I separated the image into its three color channel images: red, green, and blue.<br />
<div id="attachment_546" class="wp-caption aligncenter" style="width: 510px"><img src="http://kevindion.com/blog/wp-content/uploads/2011/01/red.png" alt="red channel" title="red" width="500" height="333" class="size-full wp-image-546" /><p class="wp-caption-text">Red</p></div></p>
<div id="attachment_544" class="wp-caption aligncenter" style="width: 510px"><img src="http://kevindion.com/blog/wp-content/uploads/2011/01/green.png" alt="green channel" title="green" width="500" height="333" class="size-full wp-image-544" /><p class="wp-caption-text">Green</p></div>
<div id="attachment_543" class="wp-caption aligncenter" style="width: 510px"><img src="http://kevindion.com/blog/wp-content/uploads/2011/01/blue.png" alt="blue channel" title="blue" width="500" height="333" class="size-full wp-image-543" /><p class="wp-caption-text">Blue</p></div>
<h4>Quick primer on color channels</h4>
<p>&#8220;But wait,&#8221; you ask, &#8220;why are these <em>color</em> channels in black and white?&#8221;. A good question. As you may or may not know, digital images are generally represented by and presented with three colors of light &#8211; red, green, and blue (RGB). Each pixel in the image is comprised of three values representing the <em>intensity</em> of the red, green, and blue &#8216;parts&#8217; of the pixel. The grayscale color channel images simply separate the red, green, or blue part of each pixel&#8217;s color and present them separately. The red channel image is very light (almost white) on the parrot&#8217;s body, since it is very red, but the green and blue channel images are almost black in that area. Using the right techniques, we can re-combine these images into the original, full-color version.</p>
<h3>The Code</h3>
<p>In order to &#8216;build&#8217; our full color image from the individual channels, we start with the following code</p>
<pre class="brush: java; title: ; notranslate">
private Bitmap getARGBImage()
{
	BitmapFactory.Options opt = new BitmapFactory.Options();
	opt.inPreferredConfig = Config.ARGB_8888;

	Bitmap red = BitmapFactory.decodeResource(getResources(),
			R.drawable.red, opt);
	Bitmap green = BitmapFactory.decodeResource(getResources(),
			R.drawable.green, opt);
	Bitmap blue = BitmapFactory.decodeResource(getResources(),
			R.drawable.blue, opt);

	int width = red.getWidth();
	int height = red.getHeight();

	Bitmap result = Bitmap.createBitmap(width, height, Config.ARGB_8888);
	result.eraseColor(Color.BLACK);

	// We will do more here...

	return result;
}
</pre>
<p>In this initial setup code, we create a <code>BitmapFactory.Options</code> object, which we use to read in our channel images. We tell it that we want to ise a configuration of <code>ARGB_8888</code>, and that is important. Any time you want to do image blending or color manipulation, you need to make sure you are in <code>ARGB_888</code>8 mode, <em>not</em> in <code>RGB_565</code> mode. Up until 2.3, android will usully default to <code>RGB_565 </code>mode unless you explicitly tell it to do otherwise in order to save memory. The next bit of the code just reads in our channel images and creates a new, blank (black-filled) image that we will use to build our full-color image. If you were to build and run a project where you put the returned Bitmap from this function into an <code>ImageView </code>(go ahead, I&#8217;ll wait&#8230;), you would just get a black image. Adding this next bit of code will change that (replace the comment above)</p>
<pre class="brush: java; title: ; notranslate">
Paint redP = new Paint();
redP.setShader(new BitmapShader(red, TileMode.CLAMP, TileMode.CLAMP));
redP.setColorFilter(new PorterDuffColorFilter(Color.RED, Mode.MULTIPLY));
redP.setXfermode(new PorterDuffXfermode(Mode.SCREEN));

Paint greenP = new Paint();
greenP.setShader(new BitmapShader(green, TileMode.CLAMP,TileMode.CLAMP));
greenP.setColorFilter(new PorterDuffColorFilter(Color.GREEN,Mode.MULTIPLY));
greenP.setXfermode(new PorterDuffXfermode(Mode.SCREEN));

Paint blueP = new Paint();
blueP.setShader(new BitmapShader(blue, TileMode.CLAMP, TileMode.CLAMP));
blueP.setColorFilter(new PorterDuffColorFilter(Color.BLUE,Mode.MULTIPLY));
blueP.setXfermode(new PorterDuffXfermode(Mode.SCREEN));

Canvas c = new Canvas(result);
c.drawRect(0, 0, width, height, redP);
c.drawRect(0, 0, width, height, greenP);
c.drawRect(0, 0, width, height, blueP);
</pre>
<p>It looks a lot more complicated than it actually is. I&#8217;ll walk through the red paint, since the process is almost exactly the same for each channel. The first thing we do is create a <code>Paint </code>object to hold our blending information. Next, we set the <em>Shader </em>of the <em>Paint</em> to a <code>BitmapShader</code>, passing in our red channel image. Now our Paint will draw the image. </p>
<pre class="brush: java; title: ; notranslate">
Paint redP = new Paint();
redP.setShader(new BitmapShader(red, TileMode.CLAMP, TileMode.CLAMP));
</pre>
<p>Next, we set the <code>ColorFilter </code>of the paint, and we use a <code>PorterDuffColorFilter </code>with a mode of <code>Multiply</code>. We also pass in <code>Color.Red</code> to the <code>ColorFilter</code>. What this bit of code accomplishes is to turn our black and white image into a black and <em>red </em>image. The Multiply algorithm multiplies the values of each pixel (in this case, the shader image and pure red), leaving black areas black and generally darkening other areas. Since the white areas of the channel image really mean increased levels of red in the full color image, we make that translation here.</p>
<pre class="brush: java; title: ; notranslate">
redP.setColorFilter(new PorterDuffColorFilter(Color.RED, Mode.MULTIPLY));
</pre>
<p>Finally, we set the <code>Xfermode </code>of the <code>Paint </code>to a <code>PorterDuffXfermode</code>, using the <code>Screen </code>algorithm this time. With the Screen algorithm, black areas become transparent, and light areas are generally lightened. </p>
<pre class="brush: java; title: ; notranslate">
redP.setXfermode(new PorterDuffXfermode(Mode.SCREEN));
</pre>
<p>There are lots of build-in capabilities you can get by using <code>PorterDuffColorFilter</code>s and <code>PorterDuffXfermode</code>s, but the various modes and operations they perform is a bit more math than I want to cover here. But if you find yourself needing to do basic blending or alpha manipulation, check out the documentation. </p>
<p>The last two paints for green and blue are exactly the same as the red paint, except for the color used in the <code>ColorFilter</code>. Now, if you were to run, you would get the original, full-color image as output!</p>
<h3>Adding Alpha</h3>
<p>The last trick I will show you is how to take a grayscale image and use it as an alpha (transparency) channel for your image. The image we will use is below.<br />
<div id="attachment_542" class="wp-caption aligncenter" style="width: 510px"><img src="http://kevindion.com/blog/wp-content/uploads/2011/01/alpha.png" alt="alpha channel" title="alpha" width="500" height="333" class="size-full wp-image-542" /><p class="wp-caption-text">Alpha - grayscale and fully opaque</p></div><br />
The white area will stay opaque and the black area will be transparent. </p>
<p>In order for us to use this as the alpha channel for our image, we need it to have transparency where we want it. Unfortunately, it is fully opaque right now. Fortunately, we can change that fairly quickly. Right now, we have the data we want in the alpha channel in the rgb channels. So, to move that data where we want it, all we have to do is perform a bitwise shift on the value for each pixel, moving the red value into the alpha value. We can do this because android stores ARGB values as AARRGGBB (hex), so the red channel data is offset by 8 bits from the alpha. Also, since in a (true) grayscale image, the values at any given pixel for red, green, or blue will be the same, we can ignore the green and blue channels. Also, in a grayscale image, white will have a value of 255, and black a value of zero. An alpha value of 255 means opaque and 0 means transparent. So all we have to do is pull out the int pixel values for our grayscale image, shift them left, and we now have our proper alpha channel.</p>
<pre class="brush: java; title: ; notranslate">
Bitmap alpha = Bitmap.createBitmap(width, height, Config.ARGB_8888);
int[] alphaPix = new int[width * height];
alphaGray.getPixels(alphaPix, 0, width, 0, 0, width, height);

int count = width * height;
for (int i = 0; i &lt; count; ++i)
{
	alphaPix[i] = alphaPix[i] &lt;&lt; 8;
}
alpha.setPixels(alphaPix, 0, width, 0, 0, width, height);

Paint alphaP = new Paint();
alphaP.setAntiAlias(true);
alphaP.setXfermode(new PorterDuffXfermode(Mode.DST_IN));

c.drawBitmap(alpha, 0, 0, alphaP);
</pre>
<p>We use a <code>PorterDuffXfermode </code>of <code>DST_IN</code> because that takes the alpha channel from the image we give it, but the color from whatever was there before &#8211; just what we want. The final code looks like this:</p>
<pre class="brush: java; title: ; notranslate">
private Bitmap getARGBImage()
{
	BitmapFactory.Options opt = new BitmapFactory.Options();
	opt.inPreferredConfig = Config.ARGB_8888;

	Bitmap red = BitmapFactory.decodeResource(getResources(),
			R.drawable.red, opt);
	Bitmap green = BitmapFactory.decodeResource(getResources(),
			R.drawable.green, opt);
	Bitmap blue = BitmapFactory.decodeResource(getResources(),
			R.drawable.blue, opt);
	Bitmap alphaGray = BitmapFactory.decodeResource(getResources(),
			R.drawable.alpha, opt);

	int width = red.getWidth();
	int height = red.getHeight();

	Bitmap result = Bitmap.createBitmap(width, height, Config.ARGB_8888);
	result.eraseColor(Color.BLACK);

	Paint redP = new Paint();
	redP.setShader(new BitmapShader(red, TileMode.CLAMP, TileMode.CLAMP));
	redP.setColorFilter(new PorterDuffColorFilter(Color.RED, Mode.MULTIPLY));
	redP.setXfermode(new PorterDuffXfermode(Mode.SCREEN));

	Paint greenP = new Paint();
	greenP.setShader(new BitmapShader(green, TileMode.CLAMP,TileMode.CLAMP));
	greenP.setColorFilter(new PorterDuffColorFilter(Color.GREEN,Mode.MULTIPLY));
	greenP.setXfermode(new PorterDuffXfermode(Mode.SCREEN));

	Paint blueP = new Paint();
	blueP.setShader(new BitmapShader(blue, TileMode.CLAMP, TileMode.CLAMP));
	blueP.setColorFilter(new PorterDuffColorFilter(Color.BLUE,Mode.MULTIPLY));
	blueP.setXfermode(new PorterDuffXfermode(Mode.SCREEN));

	Canvas c = new Canvas(result);
	c.drawRect(0, 0, width, height, redP);
	c.drawRect(0, 0, width, height, greenP);
	c.drawRect(0, 0, width, height, blueP);

	Bitmap alpha = Bitmap.createBitmap(width, height, Config.ARGB_8888);
	int[] alphaPix = new int[width * height];
	alphaGray.getPixels(alphaPix, 0, width, 0, 0, width, height);

	int count = width * height;
	for (int i = 0; i &lt; count; ++i)
	{
		alphaPix[i] = alphaPix[i] &lt;&lt; 8;
	}
	alpha.setPixels(alphaPix, 0, width, 0, 0, width, height);

	Paint alphaP = new Paint();
	alphaP.setAntiAlias(true);
	alphaP.setXfermode(new PorterDuffXfermode(Mode.DST_IN));

	c.drawBitmap(alpha, 0, 0, alphaP);

	red.recycle();
	green.recycle();
	blue.recycle();
	alphaGray.recycle();
	alpha.recycle();

	return result;
}
</pre>
<p>Now just run, and marvel at your sweet new macaw with a palm frond mohawk.<br />
<div id="attachment_554" class="wp-caption aligncenter" style="width: 330px"><img src="http://kevindion.com/blog/wp-content/uploads/2011/01/device.png" alt="the final result, showing an image with transparency" title="final" width="320" height="480" class="size-full wp-image-554" /><p class="wp-caption-text">The final result</p></div></p>
]]></content:encoded>
			<wfw:commentRss>http://kevindion.com/2011/01/android-bitmap-blending-color-channels/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Android UI Tutorial &#8211; Odometer Part III: Building the Odometer</title>
		<link>http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-3/</link>
		<comments>http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-3/#comments</comments>
		<pubDate>Thu, 30 Dec 2010 00:36:57 +0000</pubDate>
		<dc:creator>Kevin Dion</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://kevindion.com/?p=507</guid>
		<description><![CDATA[[This is the third part of the tutortial so far. If you haven't already, check out Part 1 and Part 2 to get up to speed on where we are. You can also grab the source code we ended up with at the end of Part 2 to follow along.] Odometer Tutorial Part I: First [...]]]></description>
				<content:encoded><![CDATA[<p><em>[This is the third part of the tutortial so far. If you haven't already, check out <a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-1/">Part 1</a> and <a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-2/">Part 2</a> to get up to speed on where we are. You can also grab the <a href="http://kevindion.com/blog/wp-content/uploads/2010/12/OdometerTutorial2.zip">source code we ended up with at the end of Part 2</a> to follow along.]</em><br />
<span id="more-507"></span></p>
<h6>Odometer Tutorial</h6>
<ol>
<li><a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-1/">Part I: First Steps</a></li>
<li><a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-2/">Part II: Touch Input and Scrolling Numbers</a></li>
<li>Part III: Compound Widgets and Fixing Bugs <em>« You are here</em></li>
</ol>
<h2>Creating the Odometer Widget</h2>
<p>Now that we have our single digit spinner, we can create the actual odometer. Create a new class in the project, and name it <code>Odometer</code>. Instead of View, subclass from <code>TableLayout</code>. We will use the existing capabilities of <code>TableLayout</code> to automatically divide our widget into equal pieces for each spinner.</p>
<div id="attachment_514" class="wp-caption aligncenter" style="width: 552px"><img class="size-full wp-image-514" title="Odometer Class" src="http://kevindion.com/blog/wp-content/uploads/2010/12/odometer-class.png" alt="The class creation dialog for Odometer" width="542" height="651" /><p class="wp-caption-text">Odometer class creation</p></div>
<p>To define how our widget will display, we will use an xml layout file. Use the Android XML file wizard to add a new layout file as shown below.</p>
<div id="attachment_515" class="wp-caption aligncenter" style="width: 522px"><img class="size-full wp-image-515" title="Odometer Layout" src="http://kevindion.com/blog/wp-content/uploads/2010/12/odometer-layout.png" alt="The xml file creation dialog for the Odometer layout file" width="512" height="614" /><p class="wp-caption-text">Odometer layout file creation</p></div>
<p>Name the file <code>widget_odometer.xml</code>, and make sure to change the root element to <code>TableLayout</code>. Since this is going to be the catch-all layout for the widget, don&#8217;t select any qualifiers from the list. Once the file is created, open up the xml view (if it opens in the graphical view), and add the following to the <code>&lt;TableLayout&gt;</code> element:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;TableLayout
    xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:layout_width=&quot;fill_parent&quot;
    android:layout_height=&quot;fill_parent&quot;
    android:stretchColumns=&quot;*&quot;
    &gt;
    &lt;TableRow&gt;
        &lt;com.kdion.tutorial.odometer.OdometerSpinner
            android:id=&quot;@+id/widget_odometer_spinner_100k&quot;
            android:layout_width=&quot;fill_parent&quot;
            android:layout_height=&quot;fill_parent&quot;
            /&gt;
        &lt;com.kdion.tutorial.odometer.OdometerSpinner
            android:id=&quot;@+id/widget_odometer_spinner_10k&quot;
            android:layout_width=&quot;fill_parent&quot;
            android:layout_height=&quot;fill_parent&quot;
            /&gt;
        &lt;com.kdion.tutorial.odometer.OdometerSpinner
            android:id=&quot;@+id/widget_odometer_spinner_1k&quot;
            android:layout_width=&quot;fill_parent&quot;
            android:layout_height=&quot;fill_parent&quot;
            /&gt;
        &lt;com.kdion.tutorial.odometer.OdometerSpinner
            android:id=&quot;@+id/widget_odometer_spinner_100&quot;
            android:layout_width=&quot;fill_parent&quot;
            android:layout_height=&quot;fill_parent&quot;
            /&gt;
        &lt;com.kdion.tutorial.odometer.OdometerSpinner
            android:id=&quot;@+id/widget_odometer_spinner_10&quot;
            android:layout_width=&quot;fill_parent&quot;
            android:layout_height=&quot;fill_parent&quot;
            /&gt;
        &lt;com.kdion.tutorial.odometer.OdometerSpinner
            android:id=&quot;@+id/widget_odometer_spinner_1&quot;
            android:layout_width=&quot;fill_parent&quot;
            android:layout_height=&quot;fill_parent&quot;
            /&gt;
    &lt;/TableRow&gt;
&lt;/TableLayout&gt;
</pre>
<p>We now have the definition for a single row of 6 OdometerSpinners. The <code>android:stretchColumns="*"</code> tells the layout to size each of the columns (our spinners) the same. This saves us from needing to do that ourselves. We also set ids for each spinner, with names that help us keep track of which spinner is which digit (1s place, 10s place, etc).</p>
<p>Next, we need to tell our Odometer to use this layout file to draw itself. In the Odometer class, add an initialize() method like we did for OdometerSpinner where we can do our initialization independently regardless of which constructor is called. Add the following code:</p>
<pre class="brush: java; title: ; notranslate">
private static final int NUM_DIGITS = 6;
private OdometerSpinner[] mDigitSpinners;
//...
private void initialize()
{
	mDigitSpinners = new OdometerSpinner[NUM_DIGITS];
	
	// Inflate the view from the layout resource.
	String infService = Context.LAYOUT_INFLATER_SERVICE;
	LayoutInflater li;
	li = (LayoutInflater)getContext().getSystemService(infService);
	li.inflate(R.layout.widget_odometer, this, true);
	
	mDigitSpinners[0] = (OdometerSpinner) 
		findViewById(R.id.widget_odometer_spinner_1);
	mDigitSpinners[1] = (OdometerSpinner) 
		findViewById(R.id.widget_odometer_spinner_10);
	mDigitSpinners[2] = (OdometerSpinner) 
		findViewById(R.id.widget_odometer_spinner_100);
	mDigitSpinners[3] = (OdometerSpinner) 
		findViewById(R.id.widget_odometer_spinner_1k);
	mDigitSpinners[4] = (OdometerSpinner) 
		findViewById(R.id.widget_odometer_spinner_10k);
	mDigitSpinners[5] = (OdometerSpinner) 
		findViewById(R.id.widget_odometer_spinner_100k);
	
}
</pre>
<p>The first thing we do is to use a <code>LayoutInflater</code> to &#8216;inflate&#8217; the Odometer into the xml file we created. If you want to learn more about what exactly LayoutInflater does or other options you can use with it, check out <a href="http://developer.android.com/reference/android/view/LayoutInflater.html">LayoutInflater at Android Developers</a>. The other thing we do in initialize() is to get references to each of the spinners and store them in an array so we can use them later.</p>
<p>Next, we need to change the main Activity layout so that instead of the single OdometerSpinner, we see our Odometer. In <code>main.xml</code>, remove the TextView with the &#8216;hello, world&#8217; message and change OdometerSpinner to Odometer. The layout should now look like this:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:orientation=&quot;vertical&quot;
    android:layout_width=&quot;fill_parent&quot;
    android:layout_height=&quot;fill_parent&quot;
    &gt;
	&lt;com.kdion.tutorial.odometer.Odometer
	   android:id=&quot;@+id/odometer&quot;
	   android:layout_width=&quot;fill_parent&quot;
	   android:layout_height=&quot;fill_parent&quot;
	   /&gt;
&lt;/LinearLayout&gt;
</pre>
<p>Now, run the app and see our great new Odometer!</p>
<p>Or maybe not &#8211; things don&#8217;t look too good. </p>
<div id="attachment_509" class="wp-caption aligncenter" style="width: 330px"><img class="size-full wp-image-509" title="Odometer Portrait" src="http://kevindion.com/blog/wp-content/uploads/2010/12/I-E-1.png" alt="The first view of the Odometer widget, the numbers are way too big" width="320" height="480" /><p class="wp-caption-text">Our Odometer needs work</p></div>
<p>It looks a little better if you rotate the screen (Ctrl+F12 in the emulator), but the numbers in the spinners are way too big. You can still drag numbers up and down to change them, however &#8211; so at least that is good.</p>
<div id="attachment_510" class="wp-caption aligncenter" style="width: 490px"><img class="size-full wp-image-510" title="Odometer Landscape" src="http://kevindion.com/blog/wp-content/uploads/2010/12/I-E-2.png" alt="The odometer looks slightly better in landscape view" width="480" height="320" /><p class="wp-caption-text">The numbers are more visible in landscape</p></div>
<h2>Measurement and Sizing</h2>
<p>In order for our view to draw and size properly, we need more robust logic than we have currently. At the moment, we are filling up the screen with the odometer (set with a<code>android:layout_height="fill_parent"</code> in the <code>&lt;Odometer&gt;</code> element in main.xml), but we probably don&#8217;t really want a tall and skinny odometer. What we really want is for the widest widget possible, and then let the Odometer determine how tall it should be based on that. Change main.xml to this:</p>
<pre class="brush: xml; highlight: [9]; title: ; notranslate">
&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:orientation=&quot;vertical&quot;
    android:layout_width=&quot;fill_parent&quot;
    android:layout_height=&quot;fill_parent&quot;
    &gt;
	&lt;com.kdion.tutorial.odometer.Odometer
	   android:id=&quot;@+id/odometer&quot;
	   android:layout_width=&quot;fill_parent&quot;
	   android:layout_height=&quot;wrap_content&quot;
	   /&gt;
&lt;/LinearLayout&gt;
</pre>
<p>Now if you ran the app, you wouldn&#8217;t see anything because android is relying on our code to know how tall to make the widget, but since we haven&#8217;t done anything with that it has a height of 0. So let&#8217;s fix that. </p>
<h3>OdometerSpinner</h3>
<p>In OdometerSpinner, we are going to add some logic to onMeasure() so our spinners get sized correctly.</p>
<pre class="brush: java; title: ; notranslate">
public static final float IDEAL_ASPECT_RATIO = 1.5f;
//...
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
	super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	
	// get width and height size and mode
	int wSpec = MeasureSpec.getSize(widthMeasureSpec);
	int wMode = MeasureSpec.getMode(widthMeasureSpec);
	
	int hSpec = MeasureSpec.getSize(heightMeasureSpec);
	int hMode = MeasureSpec.getMode(heightMeasureSpec);
	
	int width = wSpec;
	int height = hSpec;
	
	// ideal height for the number display
	int idealHeight = (int) (wSpec * IDEAL_ASPECT_RATIO);
	
	if(idealHeight &lt; hSpec)
	{
		height = idealHeight;
	}
	
	setMeasuredDimension(width, height);
}
</pre>
<p>The two arguments passed to <code>onMeasure</code> are encoded ints that contain both size and &#8216;mode&#8217; information. To get the useful values, we need to call <code>MeasureSpec.getSize()</code> and <code>MeasureSpec.getMode()</code> on the passed width and height ints. The mode for height and width will be one of <code>UNSPECIFIED</code>, <code>AT_LEAST</code>, or <code>EXACTLY</code>, which lets us know how our parent wants us to size ourselves &#8211; <em>at most</em> n pixels, <em>exactly</em> n pixels, or <em>unspecified</em> (where the view can be any size it wants). </p>
<p>In this simple example, we don&#8217;t worry about the mode &#8211; we know that in our app we are constrained by width and need to set our height &#8211; but in an actual production widget you would want to account for the possible combinations of modes. For instance, how should our spinner calculate its size if it has to be short but can be as wide as it wants? But we won&#8217;t worry about that, because by setting all of our widgets to <code>layout_width="fill_parent"</code> in the layouts we get the end result we want (but in a somewhat &#8216;hacked&#8217; manner). </p>
<p>In our new onMeasure(), we use a pre-defined aspect ratio we want to have for our spinner to calculate what our <em>ideal</em> height would be based on the width we are passed. Then, if that ideal height is less than the height we are passed, we use that one instead. This will fix the super tall numbers problem. Feel free to experiment with different values for the the aspect ratio, but I have found 1.5-1.6 to be pretty good for the default font android uses.</p>
<h3>Odometer</h3>
<p>We also need to add an implementation for onMeasure() to Odometer, so that it knows how tall to make itself. </p>
<pre class="brush: java; title: ; notranslate">
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
	super.onMeasure(widthMeasureSpec, heightMeasureSpec);
	
	// get width and height size and mode
	int wSpec = MeasureSpec.getSize(widthMeasureSpec);
	int wMode = MeasureSpec.getMode(widthMeasureSpec);
	
	int hSpec = MeasureSpec.getSize(heightMeasureSpec);
	int hMode = MeasureSpec.getMode(heightMeasureSpec);
	
	// calculate max height from width
	float contentHeight = ((float)wSpec / NUM_DIGITS) 
			* OdometerSpinner.IDEAL_ASPECT_RATIO;
	
	int maxHeight = (int)Math.ceil(contentHeight);
	
	int width = wSpec;
	int height = hSpec;
	
	if(maxHeight &lt; hSpec)
	{
		height = maxHeight;
	}
	
	setMeasuredDimension(width, height);
}
</pre>
<p>Nothing too complex here, we do the same calculation based on the aspect ratio, only we use 1/6th the width passed to us, since that is the value the spinners will be getting for their width.</p>
<p>Now, if we run our app, we get a much better looking odometer.</p>
<div id="attachment_511" class="wp-caption aligncenter" style="width: 330px"><img class="size-full wp-image-511" title="Odometer Sized Portrait" src="http://kevindion.com/blog/wp-content/uploads/2010/12/II-B-1.png" alt="A view of the correctly sized odometer in portrait layout" width="320" height="480" /><p class="wp-caption-text">The Odometer now sizes correctly for portrait view</p></div>
<div id="attachment_512" class="wp-caption aligncenter" style="width: 490px"><img class="size-full wp-image-512" title="Odometer Sized Landscape" src="http://kevindion.com/blog/wp-content/uploads/2010/12/II-B-2.png" alt="A view of the correctly sized odometer in landscape view" width="480" height="320" /><p class="wp-caption-text">Landscape gives us a larger widget</p></div>
<p>Much better! However, as you rotated the screen from portrait to landscape and moved the digits around, you probably noticed that every time you rotate the screen, all of the spinners reset to zero! This is due to the fact that every time the configuration of the device changes (such as going from portrait to landscape), the Activity is actually <em>destroyed and restarted</em>. </p>
<p>From the <a href="http://developer.android.com/reference/android/app/Activity.html#ConfigurationChanges">Activity documentation</a>:</p>
<blockquote cite="http://developer.android.com/reference/android/app/Activity.html#ConfigurationChanges"><p>If the configuration of the device (as defined by the Resources.Configuration class) changes, then anything displaying a user interface will need to update to match that configuration. Because Activity is the primary mechanism for interacting with the user, it includes special support for handling configuration changes.</p>
<p>Unless you specify otherwise, a configuration change (such as a change in screen orientation, language, input devices, etc) will cause your current activity to be destroyed, going through the normal activity lifecycle process of onPause(), onStop(), and onDestroy() as appropriate.</p></blockquote>
<p>Every time you rotate the screen, the app is killed and restarted &#8211; so all of the digits go back to zero (the initial default value).</p>
<h2>Handling Activity Lifecycle Events</h2>
<p>In order to maintain the value of the odometer when the screen orientation is changed (or when the user leaves the app and comes back), we will use some of the provided methods of Activity to store the value of the odometer before it is destroyed by the configuration change process, and then reading that value back and setting the odometer value accordingly. Of course, in order to do this we need to have a way to read and set the value of our Odometer. </p>
<h3>Getting and Setting the value of OdometerSpinner</h3>
<p>OdometerSpinner already has a method to set the digit in <code>setCurrentDigit(int)</code>, so we just need to add a way to read the value to store later. So let&#8217;s go ahead and add one:</p>
<pre class="brush: java; title: ; notranslate">
public int getCurrentDigit()
{
	return mCurrentDigit;
}
</pre>
<p>Nice and simple.</p>
<h3>Get and Set in Odometer</h3>
<p>Now we need to add code to get and set the value of the odometer.<em>[Thanks to commenter Ted Hopp for giving me a better way to get and set the value of the individual spinners!]</em></p>
<pre class="brush: java; title: ; notranslate">
private int mCurrentValue;
//...
public int getValue()
{
	int value = 0;
		
	for(int i = NUM_DIGITS - 1; i &gt;= 0; --i)
	{
		value = 10 * value + mDigitSpinners[i].getCurrentDigit();
	}
	
	mCurrentValue = value;
	
	return mCurrentValue;
}

public void setValue(int value)
{
	for(int i = 0; i &lt; NUM_DIGITS; ++i)
	{
		mDigitSpinners[i].setCurrentDigit(value % 10);
		value /= 10;
	}
}
</pre>
<p>In the get method, we read the value of each spinner and multiply it by the proper factor based on its position, adding the results to get the value represented by the whole. It is important that in <code>initialize()</code> you stored the spinners in <em>increasing</em> place order, so that the digit in the ones place it at index zero in the array &#8211; otherwise you will get incorrect values from this get call.</p>
<p>The set method is mostly the inverse of the get method, taking in the desired value and parsing out each digit. I didn&#8217;t put in a range check because in this case the only way to set the value is through the odometer itself (so no out of range data), but in a more widely used widget you would probably want to put some sort of check here.</p>
<h3>Saving and Reading State</h3>
<p>Now that we have a way to get and set the odometer value, let&#8217;s add the code in the main activity to handle the lifecycle. Add a class member for the Odometer, and add an override of <code>onSaveInstanceState(Bundle)</code>, which is where we will add our code to store the value of the odometer. We will read the saved value in <code>onCreate()</code>.</p>
<pre class="brush: java; title: ; notranslate">
private static final String KEY_VALUE = &quot;com.kdion.tutorial.odometer.OdometerValue&quot;;

private Odometer mOdometer;

private int mOdometerValue;
//...
public void onCreate(Bundle savedInstanceState)
{
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main);
	
	mOdometer = (Odometer) findViewById(R.id.odometer);
	
	if(savedInstanceState != null)
	{
		mOdometerValue = savedInstanceState.getInt(KEY_VALUE);
		mOdometer.setValue(mOdometerValue);
	}
}

protected void onSaveInstanceState(Bundle outState)
{
	super.onSaveInstanceState(outState);
	
	mOdometerValue = mOdometer.getValue();
	outState.putInt(KEY_VALUE, mOdometerValue);
}
</pre>
<p>We create a unique string key that we will use to store our data &#8211; in this case I used &#8220;<code>com.kdion.tutorial.odometer.OdometerValue</code>&#8220;, following the Java namespace format &#8211; but you could use any string you wish. In onSaveInstanceState(), we read the odometer value, then put that value in the Bundle we are passed. From the <a href="http://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState(android.os.Bundle)">Android Docs for <code>saveInstanceState(Bundle)</code></a>:</p>
<blockquote cite="http://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState(android.os.Bundle)"><p>Called to retrieve per-instance state from an activity before being killed so that the state can be restored in onCreate(Bundle) or onRestoreInstanceState(Bundle) (the Bundle populated by this method will be passed to both)</p></blockquote>
<p>So the Bundle we saved our odometer value to in onSaveInstanceState() is passed to the onCreate() method when it is being used to continue execution of our app (like when the screen orientation changes). So we read the vale from the Bundle, and use it to set the odometer&#8217;s value. If there is not a value stored in the bundle (such as if this were a clean run of the app where there <em>was</em> no previous state), we don&#8217;t do anything.</p>
<p>There are a lot of different methods that you can (and should) use to save and restore your app&#8217;s state that deal with the Activity lifecycle. They all have slightly different uses and times they are better to be used over others. If you aren&#8217;t really familiar with the Activity lifecycle, it is a really good idea to read up on it in the <a href="http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle">Android Docs</a>. There is a lot of great information there.</p>
<p>Now, run the app and see how you can rotate the screen and the numbers will persist!</p>
<h2>Adding Change Listeners</h2>
<p>One thing that our current code limits us in is that we don&#8217;t know when the value of the odometer changes. If we were using this as part of a group of widgets that depended on having an up-to-date value from the odometer, it would fail with our current widget. To demonstrate this (before we fix it), add a TextView to the main activity layout (main.xml &#8211; I added it after/below the odometer, but you could do it above if you really wanted to):</p>
<pre class="brush: xml; title: ; notranslate">
&lt;TextView
   android:id=&quot;@+id/main_valuedisplay&quot;
   android:layout_width=&quot;fill_parent&quot;
   android:layout_height=&quot;wrap_content&quot;
   android:textSize=&quot;34dp&quot;
   android:textColor=&quot;#FFF&quot;
   android:gravity=&quot;center&quot;
   /&gt;
</pre>
<p>In addition, change the code in the main activity to show the odometer value in the TextView:</p>
<pre class="brush: java; title: ; notranslate">
private TextView mValueDisplay;
//...
public void onCreate(Bundle savedInstanceState)
{
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main);
	
	mOdometer = (Odometer) findViewById(R.id.odometer);
	mValueDisplay = (TextView) findViewById(R.id.main_valuedisplay);
	
	if(savedInstanceState != null)
	{
		mOdometerValue = savedInstanceState.getInt(KEY_VALUE);
		mOdometer.setValue(mOdometerValue);
	}
	updateOdometerValue();
}

private void updateOdometerValue()
{
	mOdometerValue = mOdometer.getValue();
	
	String text = String.format(&quot;%06d&quot;, mOdometerValue);
	mValueDisplay.setText(text);
}
</pre>
<p>We format the value of the odometer (the &#8220;<code>%06d</code>&#8220;) to be zero-padded so that it will look more like what the odometer shows. You can also add a call to <code>updateOdometerValue()</code> in <code>onSaveInstanceState()</code> if you wish, but since that code isn&#8217;t called until our activity is getting destroyed we won&#8217;t see the change anyways. Now run the app, and see that as you change the numbers of the odometer, the text view doesn&#8217;t update its display until you rotate the screen. This happens because we only read the odometer value in <code>onSaveInstanceState()</code> &#8211; so our activity is only updated when we destroy it!</p>
<div id="attachment_516" class="wp-caption aligncenter" style="width: 490px"><img class="size-full wp-image-516" title="Non-Synced Values" src="http://kevindion.com/blog/wp-content/uploads/2010/12/V-C.png" alt="A view of different values being displayed on the odometer and the value text view" width="480" height="320" /><p class="wp-caption-text">As you change the Odometer, the value displayed below does not change</p></div>
<p>In order for our activity to keep the TextView updated with the latest odometer value, it needs to know when that value has changed. The value of the odometer changes when any of its digits changes, so we need to know when any of the OdometerSpinners is changed. Add the following code to OdometerSpinner:</p>
<pre class="brush: java; highlight: [17,18]; title: ; notranslate">
public class OdometerSpinner extends View
{
	private OnDigitChangeListener mDigitChangeListener;
	//...
	public void setCurrentDigit(int digit)
	{
		int newVal = digit;
		
		if(newVal &lt; 0)
			newVal = 0;
		if(newVal &gt; 9)
			newVal = 9;
		
		int old = mCurrentDigit;
		mCurrentDigit = newVal;
		
		if(mCurrentDigit != old &amp;&amp; mDigitChangeListener != null)
			mDigitChangeListener.onDigitChange(this, mCurrentDigit);
		
		//...
	}
	//...
	public void setOnDigitChangeListener(OnDigitChangeListener listener)
	{
		mDigitChangeListener = listener;
	}
	//...
	public interface OnDigitChangeListener
	{
		abstract void onDigitChange(OdometerSpinner sender, int newDigit);
	}
}
</pre>
<p>First (last bit of code), we add a public interface defining how our Listener will be called &#8211; note that it is <em>inside</em> the OdometerSpinner class! We also add a member variable to store a listener (dealing with multiple/chained listeners is beyond the scope of this tutorial), and a method to set the listener. FInally, within the <code>setCurrentDigit()</code> method, we check that the current digit has changed, and if so call the listener method.</p>
<p>Next, we need to add essentially the same things to Odometer, but also listen for the spinner changes:</p>
<pre class="brush: java; title: ; notranslate">
public class Odometer extends TableLayout
{
	private OnValueChangeListener mValueChangeListener;
	//...
	private void initialize()
	{
		//...
		
		for(OdometerSpinner s : mDigitSpinners)
		{
			s.setOnDigitChangeListener(new OdometerSpinner.OnDigitChangeListener()
			{
				public void onDigitChange(OdometerSpinner s, int newDigit)
				{
					updateValue();
				}
			});
		}
	}
	
	private void updateValue()
	{
		int value = 0;
		
		for(int i = NUM_DIGITS - 1; i &gt;= 0; --i)
		{
			value = 10 * value + mDigitSpinners[i].getCurrentDigit();
		}
		
		int old = mCurrentValue;
		mCurrentValue = value;
		
		if(old != mCurrentValue &amp;&amp; mValueChangeListener != null)
			mValueChangeListener.onValueChange(this, mCurrentValue);
	}
	
	public void setValue(int value)
	{
		for(int i = 0; i &lt; NUM_DIGITS; ++i)
		{
			mDigitSpinners[i].setCurrentDigit(value % 10);
			value /= 10;
		}
	}
		
	public int getValue()
	{	
		return mCurrentValue;
	}
	
	public void setOnValueChangeListener(OnValueChangeListener listener)
	{
		mValueChangeListener = listener;
	}
	//...
	public interface OnValueChangeListener
	{
		abstract void onValueChange(Odometer sender, int newValue);
	}
}
</pre>
<p><em>[Commenter Ted Hopp pointed out that we could avoid allocating a new listener for each separate spinner by having <code>Odometer</code> implement <code>OnDigitChangeListener</code>. Another way would be to create a single member for the Listener and pass that one instance to each spinner. I find myself leaning toward that since it doesn't then expose the DigitChangeListener interface to the outside world.]</em></p>
<p>Again, we defined a public interface for other classes to use to listen for our value changes, as well as a method to set our listener. We also set the listener for each of the spinners in the odometer, and update our value whenever one does. Note that the code to read the value from the spinners was moved from the <code>getValue()</code> method to the new <code>updateValue()</code> method. Finally, if the value of the odometer is changed (either through updating based on a spinner change or from setValue), we call our listener.</p>
<p>Finally, we need to setup the change listener in our main activity to listen for changes to the odometer value:</p>
<pre class="brush: java; highlight: [9,10,11,12,13,14,15,16]; title: ; notranslate">
public void onCreate(Bundle savedInstanceState)
{
	super.onCreate(savedInstanceState);
	setContentView(R.layout.main);
	
	mOdometer = (Odometer) findViewById(R.id.odometer);
	mValueDisplay = (TextView) findViewById(R.id.main_valuedisplay);
	
	mOdometer.setOnValueChangeListener(new Odometer.OnValueChangeListener()
	{
		@Override
		public void onValueChange(Odometer sender, int newValue)
		{
			updateOdometerValue();
		}
	});
	
	if(savedInstanceState != null)
	{
		mOdometerValue = savedInstanceState.getInt(KEY_VALUE);
		mOdometer.setValue(mOdometerValue);
	}
	updateOdometerValue();
}
</pre>
<p>We simply call our <code>updateOdometerValue()</code> method, which reads the current odometer value and set the TextView. Now, when you run, you should see that as you change the odometer, the value displayed below also changes. </p>
<div id="attachment_517" class="wp-caption aligncenter" style="width: 490px"><img class="size-full wp-image-517" title="Synced Values" src="http://kevindion.com/blog/wp-content/uploads/2010/12/V-G.png" alt="An image showing the correctly updating value" width="480" height="320" /><p class="wp-caption-text">As you change the value on the Odometer, the value below updates correctly</p></div>
<p>One final bug we will deal with in this tutorial is best seen in portrait mode. If you drag a number too far without lifting your finger, you will quickly pass the single additional digit we draw either above or below in the spinner. Because of this, you won&#8217;t see any more digits in the spinner until you lift your finger and drag again. </p>
<div id="attachment_513" class="wp-caption aligncenter" style="width: 490px"><img class="size-full wp-image-513" title="Missing Numbers" src="http://kevindion.com/blog/wp-content/uploads/2010/12/long-scroll-no-number.png" alt="A view showing that if we drag a number too far, no more are drawn" width="480" height="319" /><p class="wp-caption-text">If you drag too far, there aren&#39;t any more numbers</p></div>
<p>To fix this, we need to make some changes to OdometerSpinner. Rather than drawing even more numbers (and thus tracking where to draw them, etc), we will simply check during the drag event if the number has been dragged enough to count as a digit change. We will use the height of the spinner as the threshold, since that is how far apart the digits are spaced. When a number is dragged that far, we will change the value of the spinner, which will reset the above and below numbers, allowing for additional dragging. The change we need to make looks like this:</p>
<pre class="brush: java; title: ; notranslate">
public boolean onTouchEvent(MotionEvent event)
{
	//... ACTION_DOWN ...
	else if(action == MotionEvent.ACTION_MOVE)
	{
		float currentY = event.getY();
		
		float delta = mTouchLastY - currentY;
		mTouchLastY = currentY;
		
		mDigitY -= delta;
		mDigitAboveY -= delta;
		mDigitBelowY -= delta;
		
		// calculate the overall delta (beginning to now)
		float totalDelta = mTouchStartY - currentY;
		
		// If we have scrolled an entire number, change numbers while 
		// keeping the scroll
		if(Math.abs(totalDelta) &gt; mHeight )
		{
			// need to either increase or decrease value
			float postDelta = Math.abs(totalDelta) - mHeight;
			
			if(totalDelta &gt; 0)
			{
				// go DOWN a number
				setCurrentDigit(mDigitBelow);
				mTouchStartY -= mHeight;
				
				mDigitY -= postDelta;
				mDigitBelowY -= postDelta;
				mDigitAboveY -= postDelta;
			}
			else
			{
				// go UP a number
				setCurrentDigit(mDigitAbove);
				mTouchStartY += mHeight;
				
				mDigitY += postDelta;
				mDigitBelowY += postDelta;
				mDigitAboveY += postDelta;
			}
		}
		
		invalidate();
		
		return true;
	}
	//... ACTION_UP ...
}
</pre>
<p>The first part of the code is the same as before, but now, after we have moved the digits, we check the total drag distance. If we have dragged the equivalent of one digit (the height of the spinner), we can go ahead and change the number appropriately. Depending on the sign of the total delta, we either increment or decrement the number. When we call <code>setCurrentDigit()</code>, it centers each digit. Since we wait until the next digit would be centered anyways to change, that is what we want. </p>
<p>However, it is possible that our code won&#8217;t get called exactly when the total scroll delta is equal to the height. In that case, we need to then offset each digit by the difference between the height and how far we actually scrolled so there isn&#8217;t any stutter during the drag. Note that we also offset <code>mTouchStartY</code> by the height so that the code in <code>ACTION_UP</code> behaves correctly once a digit has been passed. If we didn&#8217;t &#8216;reset&#8217; the start y-value, whenever the user dragged past a number or two and released, the digit would increment/decrement even if there was a digit perfectly centered.</p>
<p>Now, you can run the app and see that as you continue to scroll, not only do numbers continue to appear, but as you pass digits the value is also continually updated! </p>
<p>That concludes this tutorial &#8211; there are a number of ways this widget can be improved, both in appearance and in functionality. You could add a way for the numbers to automatically animate a scroll to a value when set programmatically, or make it look more realistic with better background graphics and shadows. </p>
<h6>Source</h6>
<p>Grab the <a href="http://kevindion.com/blog/wp-content/uploads/2010/12/OdometerTutorial3.zip">source for the completed Part 3 here</a>. (ZIP)</p>
<h6>Odometer Tutorial</h6>
<ol>
<li><a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-1/">Part I: First Steps</a></li>
<li><a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-2/">Part II: Touch Input and Scrolling Numbers</a></li>
<li>Part III: Compound Widgets and Fixing Bugs <em>« You are here</em></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-3/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Android UI Tutorial &#8211; Odometer Part II: Completing the Spinner</title>
		<link>http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-2/</link>
		<comments>http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-2/#comments</comments>
		<pubDate>Mon, 27 Dec 2010 16:53:16 +0000</pubDate>
		<dc:creator>Kevin Dion</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://kevindion.com/?p=484</guid>
		<description><![CDATA[[This is the second part in a three-part tutorial for creating an odometer widget in android. If you haven't already, check out Part 1 to get up to speed on where we are. You can also grab the source code we ended up with at the end of Part 1 to follow along.] Odometer Tutorial [...]]]></description>
				<content:encoded><![CDATA[<p><em>[This is the second part in a three-part tutorial for creating an odometer widget in android. If you haven't already, check out <a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-1/">Part 1</a> to get up to speed on where we are. You can also grab the <a href="http://kevindion.com/blog/wp-content/uploads/2010/12/OdometerTutorial1.zip">source code we ended up with at the end of Part 1</a> to follow along.]</em><br />
<span id="more-484"></span></p>
<h6>Odometer Tutorial</h6>
<ol>
<li><a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-1/">Part I: First Steps</a></li>
<li>Part II: Touch Input and Scrolling Numbers <em>« You are here</em></li>
<li><a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-3/">Part III: Compound Widgets and Fixing Bugs</a></li>
</ol>
<h2>Adding Interactivity</h2>
<p>Now that we have the basic display for our widget down, it is time to make it interactive by working with touch events. But before we do that, there is a small change we need to make that will make moving forward easier.</p>
<h3>A Numbers Game</h3>
<p>Up to this point, we have been treating the digit we are displaying as just a string. Since we were only drawing that one digit, and it wasn&#8217;t changing, it wasn&#8217;t a big deal &#8211; but now that we are going to be changing our numbers, we should change how we deal with the digit internally. We will still store the string version of the current digit to keep from having to convert it every time we draw, but now we will add an int to store the numeric value as well. We will also add a method, <code>setCurrentDigit(int)</code>, that will take in an int value and set the int and string values as appropriate.</p>
<pre class="brush: java; title: ; notranslate">
private int mCurrentDigit;
//...
public void setCurrentDigit(int digit)
{
	/*
	 *  Basic range limiting - in a production widget,
	 *  you might want to throw an exception if the number passed
	 *  if less than 0 or greater than 9
	 */
	int newVal = digit;

	if(newVal &lt; 0)
		newVal = 0;
	if(newVal &gt; 9)
		newVal = 9;

	mCurrentDigit = newVal;
	mDigitString = String.valueOf(mCurrentDigit);

	setDigitYValues();
	invalidate();
}
</pre>
<p>The new function <code>setCurrentDigit()</code> does some basic range checking so that we only deal with single-digit, non-negative numbers, but in a production environment, you would want to add some additional handling of these conditions so that you aren&#8217;t producing unexpected behavior (hey! i set the spinner to show -42! What&#8217;s up?). We set the digit value and the string representation, and then we call some new functions, <code>setDigitYValues()</code> and <code>invalidate()</code>. We will implement <code>setDigitYValues()</code> in a bit, but <code>invalidate()</code> is a method provided us by View, and it tells the system to redraw our widget. Since we changed the number we want to draw, we should probably make sure it gets shown, right?</p>
<pre class="brush: java; title: ; notranslate">
private void setDigitYValues()
{
	mDigitY = findCenterY(mCurrentDigit);
}

private float findCenterY(int digit)
{
	String text = String.valueOf(digit);
	Rect bounds = new Rect();
	mDigitPaint.getTextBounds(text, 0, text.length(), bounds);

	int textHeight = Math.abs(bounds.height());

	float result = mHeight - ((mHeight - textHeight) / 2);

	return result;
}
</pre>
<p>You will also need to add the two methods above, which sets the correct Y value for the digit we want to draw. The logic now in f<code>indCenterY()</code> was previously in <code>onSizeChanged()</code>, so you will also need to remove that, and just replace it with a call to <code>setDigitYValues()</code>. The last thing to do is to get rid of the old digit initialization in <code>initialize()</code>, and change it to call <code>setCurrentDigit()</code>. It should look something like this:</p>
<pre class="brush: java; title: ; notranslate">
private void initialize()
{
	//... other code here ...
	setCurrentDigit(4);
}
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
	//... other code here ...
	mDigitX = mWidth / 2;

	setDigitYValues();
}
</pre>
<p>Now, if we run our app, we can see the new number displayed &#8211; and see that it does still work!</p>
<div id="attachment_490" class="wp-caption aligncenter" style="width: 330px"><img class="size-full wp-image-490" title="New Digits" src="http://kevindion.com/blog/wp-content/uploads/2010/12/odom-4.png" alt="Our OdometerSPinner now displaying 4" width="320" height="480" /><p class="wp-caption-text">New Dig(it)s</p></div>
<h3>Touch Me, Feel Me</h3>
<p>Now that we have our handling for the current digit better setup, we can start changing it. Rather than trying to get perfect touch interactivity right off the bat, we will take small steps that gradually get us closer to having that scrolling interaction we are looking for. The first thing we will do is allow the user to &#8216;tap&#8217; the spinner to increment the digit that is displayed.</p>
<p>To handle user touch input, we need to override <code>onTouchEvent()</code>. This method is called when there is a touch motion event within our View, such as the user touching the screen, moving their finger around, or lifting up the finger. There are many different &#8216;Actions&#8217; that we can handle, and we determine which Action the passed in <code>MotionEvent</code> encompasses by calling <code>MotionEvent.getAction()</code>. <code>onTouchEvent()</code> also returns a bool, which should be <code>true</code> if the event passed was handled by our code, or <code>false</code> if not.</p>
<p>The two actions we will handle right now will be <code>ACTION_DOWN</code> and <code>ACTION_UP</code>, which correspond to the user putting a finger down on the screen over our View, and lifting that finger up, respectively. We don&#8217;t want to do anything when the user touches their finger down, so we just return <code>true</code>. When the user lifts their finger, however, we want to increment the number we display (wrapping after 9), so we change the current digit when that event occurs.</p>
<pre class="brush: java; title: ; notranslate">
public boolean onTouchEvent(MotionEvent event)
{
	// Pull out the Action value from the event for processing
	int action = event.getAction();

	if(action == MotionEvent.ACTION_DOWN)
	{
		return true;
	}
	else if(action == MotionEvent.ACTION_UP)
	{
		int newValue = mCurrentDigit + 1;

		if(newValue &gt; 9)
			newValue = 0;

		setCurrentDigit(newValue);

		return true;
	}
	return false;
}
</pre>
<p>If we run our app now, you can tap the spinner to increment the number (click in the emulator).</p>
<div id="attachment_491" class="wp-caption aligncenter" style="width: 330px"><img class="size-full wp-image-491" title="Touch To Change" src="http://kevindion.com/blog/wp-content/uploads/2010/12/odom-5.png" alt="After touching, our spinner now displays 5" width="320" height="480" /><p class="wp-caption-text">Touch to increment</p></div>
<h3>Such a Drag</h3>
<p>Now that we have some basic touch interaction, let&#8217;s add the next layer &#8211; dragging the digit. Now, if you drag your finger around the screen, the number doesn&#8217;t move, it just changes when you lift your finger. We want the user to drag the number up and down to change it, so we need to add that. To begin with, we will add some handling of the <code>ACTION_MOVE</code> action in <code>onTouchEvent()</code>, so that we can move the digit up or down.</p>
<p>We first add two new variables to OdometerSpinner, to store the Y value of where the user started the drag, and another to store the last y-value of the drag. Then, we set those in ACTION_DOWN, and handle changing the digit y-value based on where the user&#8217;s finger is while they drag it around.</p>
<pre class="brush: java; title: ; notranslate">
public boolean onTouchEvent(MotionEvent event)
{
	// Pull out the Action value from the event for processing
	int action = event.getAction();

	if(action == MotionEvent.ACTION_DOWN)
	{
		mTouchStartY = event.getY();
		mTouchLastY = mTouchStartY;

		return true;
	}
	else if(action == MotionEvent.ACTION_MOVE)
	{
		float currentY = event.getY();

		float delta = mTouchLastY - currentY;
		mTouchLastY = currentY;

		mDigitY -= delta;

		invalidate();

		return true;
	}
	//... ACTION_UP...
}
</pre>
<p>We also call invalidate() during the drag to redraw the View with the new location of the digit. If you run the app at this point, you will see that you can drag the digit up and down, and it moves around like you would expect. When you lift your finger, the number also increments.</p>
<div id="attachment_492" class="wp-caption aligncenter" style="width: 330px"><img class="size-full wp-image-492" title="Partial Scroll" src="http://kevindion.com/blog/wp-content/uploads/2010/12/odom-scroll-1.png" alt="Dragging the number now moves it along with our finger" width="320" height="480" /><p class="wp-caption-text">Dragging the digit</p></div>
<p>There are still improvements to be made, however. Regardless of which direction you drag the number, it only increases in value &#8211; and it also increases even if you tap without moving the number very much.</p>
<p>In order to tell if we should increase, decrease, or not change the digit we display, we need to add some conditional logic to the <code>ACTION_UP</code> handler. When the user lifts their finger, we compare the current y-value of their finger to the initial position (which we saved in <code>ACTION_DOWN</code>), and if it is above where they started, they have dragged the number up, so we should decrement the digit. If the y-value is below the starting point, we increment. We also check that the change in y (how far the user dragged) is above a certain threshold (in this case, one third the height of the spinner), or else we don&#8217;t change the value at all. Remember, because of how the graphics coordinate system is set up, higher y-values go <em>down</em>, so a negative <em>delta</em> means the user dragged <em>down</em>, so we <em>increase</em> the number (confused yet? It all makes sense, I promise!).</p>
<pre class="brush: java; title: ; notranslate">
public boolean onTouchEvent(MotionEvent event)
{
	// ... ACTION_DOWN and ACTION_MOVE ...
	else if(action == MotionEvent.ACTION_UP)
	{
		float currentY = event.getY();

		// delta: negative means a down 'scroll'
		float deltaY = mTouchStartY - currentY;

		int newValue = mCurrentDigit;

		if(Math.abs(deltaY) &gt; (mHeight / 3) )
		{
			// higher numbers are 'above' the current, so a scroll down
			// _increases_ the value
			if(deltaY &lt; 0)
			{
				++newValue;
				if(newValue &gt; 9)
					newValue = 0;
			}
			else
			{
				--newValue;
				if(newValue &lt; 0)
					newValue = 9;
			}
		}

		setCurrentDigit(newValue);

		return true;
	}
	return false;
}
</pre>
<p>Also note that we call <code>setCurrentDigit()</code> even if we didn&#8217;t change the value of the digit we are displaying, because it resets the position of the digit to the center of the screen, which we want to happen regardless of whether the number we are displaying changed or not. Run the app and see how much more intuitive changing the number is now. Simply drag up to decrease, and drag down to increase.</p>
<h3>Drawing More Numbers</h3>
<p>In a real Odometer, as a number is changing, you can see both the current value and the next, since the numbers are usually fairly close together vertically. Right now the user can&#8217;t see what the next number is until they lift their finger, so until they change the number a few times, they don&#8217;t know what result their action will have. What we need to do is display the numbers above and below the current number as the user drags it up or down.</p>
<p>In <code>OdometerSpinner</code>, add new variables for storing the two new digits &#8211; above and below- remembering that we track their integer value, string representation, and y-position. Next, in <code>setCurrentDigit()</code>, set the values of the above and below digits based on the current digit. </p>
<pre class="brush: java; title: ; notranslate">
private int mDigitAbove;
private int mDigitBelow;
private float mDigitAboveY;
private float mDigitBelowY;
private String mDigitAboveString;
private String mDigitBelowString;
//...
public void setCurrentDigit(int digit)
{
	// Basic range limiting
	int newVal = digit;
	
	if(newVal &lt; 0)
		newVal = 0;
	if(newVal &gt; 9)
		newVal = 9;
	
	mCurrentDigit = newVal;
	
	// Digit above - greater
	mDigitAbove = mCurrentDigit + 1;
	
	if(mDigitAbove &gt; 9)
		mDigitAbove = 0;
	
	// digit below - lower
	mDigitBelow = mCurrentDigit - 1;
	
	if(mDigitBelow &lt; 0)
		mDigitBelow = 9;

	mDigitString = String.valueOf(mCurrentDigit);
	mDigitAboveString = String.valueOf(mDigitAbove);
	mDigitBelowString = String.valueOf(mDigitBelow);
	
	setDigitYValues();
	invalidate();
}
</pre>
<p>We also need to calculate the starting y-values for our above and below digits, so add them to <code>setDigitYValues()</code>. We also need to draw our new digits, so add them to <code>onDraw()</code>.</p>
<pre class="brush: java; title: ; notranslate">
private void setDigitYValues()
{
	mDigitY = findCenterY(mCurrentDigit);
	mDigitAboveY = findCenterY(mDigitAbove) - mHeight;
	mDigitBelowY = mHeight + findCenterY(mDigitBelow);
}
//...
protected void onDraw(Canvas canvas)
{
	super.onDraw(canvas);
	
	mBGGrad.draw(canvas);
	
	canvas.drawText(mDigitString, mDigitX, mDigitY, mDigitPaint);

	canvas.drawText(mDigitAboveString, mDigitX, mDigitAboveY, mDigitPaint);
	canvas.drawText(mDigitBelowString, mDigitX, mDigitBelowY, mDigitPaint);
}
</pre>
<p>Finally, in the <code>ACTION_MOVE</code> handling, change the y-values for the above and below digits along with the current digit so they scroll into view as the current digit leaves. We can also now change the code in <code>ACTION_UP</code> to change to the above or below digit instead of doing the calculations there.</p>
<pre class="brush: java; highlight: [21,22,43,47]; title: ; notranslate">
public boolean onTouchEvent(MotionEvent event)
{
	// Pull out the Action value from the event for processing
	int action = event.getAction();
	
	if(action == MotionEvent.ACTION_DOWN)
	{
		mTouchStartY = event.getY();
		mTouchLastY = mTouchStartY;
		
		return true;
	}
	else if(action == MotionEvent.ACTION_MOVE)
	{
		float currentY = event.getY();
		
		float delta = mTouchLastY - currentY;
		mTouchLastY = currentY;
		
		mDigitY -= delta;
		mDigitAboveY -= delta;
		mDigitBelowY -= delta;
		
		invalidate();
		
		return true;
	}
	else if(action == MotionEvent.ACTION_UP)
	{
		float currentY = event.getY();
		
		// delta: negative means a down 'scroll'
		float deltaY = mTouchStartY - currentY;
		
		int newValue = mCurrentDigit;
		
		if(Math.abs(deltaY) &gt; (mHeight / 3) )
		{
			// higher numbers are 'above' the current, so a scroll down 
			// _increases_ the value
			if(deltaY &lt; 0)
			{
				newValue = mDigitAbove;
			}
			else
			{
				newValue = mDigitBelow;
			}
		}
		
		setCurrentDigit(newValue);
		
		return true;
	}
	return false;
}
</pre>
<p>Run the app and play around with it &#8211; it should work much like you would expect a spinner to work. You can drag the number up or down to change it, and the new number will scroll into view before being set.</p>
<div id="attachment_493" class="wp-caption aligncenter" style="width: 330px"><img class="size-full wp-image-493" title="Multiples" src="http://kevindion.com/blog/wp-content/uploads/2010/12/odom-scroll-34.png" alt="Dragging up and down now shows the multiple=" width="320" height="480" /><p class="wp-caption-text">Showing multiple digits</p></div>
<h6>Source</h6>
<p>Grab the <a href="http://kevindion.com/blog/wp-content/uploads/2010/12/OdometerTutorial2.zip">source for the completed Part II here</a>. (ZIP)</p>
<p>In part 3 of the tutorial, we will combine multiple spinners into an odometer, and handle some final cleanup.</p>
<h6>Odometer Tutorial</h6>
<ol>
<li><a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-1/">Part I: First Steps</a></li>
<li>Part II: Touch Input and Scrolling Numbers <em>« You are here</em></li>
<li><a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-3/">Part III: Compound Widgets and Fixing Bugs</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-2/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Custom UI Elements in Android: Odometer Tutorial Part I</title>
		<link>http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-1/</link>
		<comments>http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-1/#comments</comments>
		<pubDate>Sat, 25 Dec 2010 19:43:05 +0000</pubDate>
		<dc:creator>Kevin Dion</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Code]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[tutorial]]></category>

		<guid isPermaLink="false">http://kevindion.com/?p=422</guid>
		<description><![CDATA[Introduction This series of tutorials will provide an introduction to creating custom user interface elements in Android. By the end, you will have a complete custom widget that will allow input of numbers as if on an odometer. The user will slide the digits up and down, and change the digits. This first part of [...]]]></description>
				<content:encoded><![CDATA[<p><img src="http://kevindion.com/blog/wp-content/uploads/2010/12/odometer-final.png" alt="The finished Odometer" title="Final Product" width="320" height="89" class="size-full wp-image-481 aligncenter" /></p>
<h2>Introduction</h2>
<p>This series of tutorials will provide an introduction to creating custom user interface elements in Android. By the end, you will have a complete custom widget that will allow input of numbers as if on an odometer. The user will slide the digits up and down, and change the digits.</p>
<p>This first part of the tutorial will get our test project created, and start building out the basic element of our odometer &#8211; a single-digit spinner.<br />
<span id="more-422"></span></p>
<h6>Odometer Tutorial</h6>
<ol>
<li>Part I: First Steps <em>&#171 You are here</em></li>
<li><a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-2/">Part II: Touch Input and Scrolling Numbers</a></li>
<li><a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-3/">Part III: Compound Widgets and Fixing Bugs</a></li>
</ol>
<h2>Odometer Tutorial Part I: First Steps</h2>
<p>In this first step tutorial, we will get the foundation laid for our Odometer widget, and get through drawing the single-digit spinner, but we won&#8217;t get to interactivity yet.</p>
<h2>Creating the Project</h2>
<p>The first step is to create a new Android project in Eclipse. I used the following values:</p>
<p>Project Name: OdometerTutorial<br />
Build Target: 2.1<br />
Application Name: Odometer<br />
Package Name: com.kdion.tutorial.odometer<br />
Create Activity: OdometerTutorialMain<br />
min sdk: 7</p>
<div id="attachment_431" class="wp-caption aligncenter" style="width: 554px"><img class="size-full wp-image-431" title="Project Creation" src="http://kevindion.com/blog/wp-content/uploads/2010/12/project-setup1.png" alt="Settings for creating the project" width="544" height="718" /><p class="wp-caption-text">Project Settings</p></div>
<p><span style="color: #800000;">(Special Note: if you look at the settings in the image, you will see that I mis-typed &#8216;tutorial&#8217; as &#8216;turotial&#8217; in the Package name. This came back to bite me later, so I had to fix it after I took this screenshot. Don&#8217;t make the same mistake as me!)</span></p>
<p>You can use whatever values you wish for these, but just be aware that my code is based on using these values so if you use different values (say, for the Package Name), your code will need to change accordingly.</p>
<p>With your newly created project, go ahead and run it to make sure everything is playing nice with the emulator (or your dev phone)</p>
<div id="attachment_424" class="wp-caption aligncenter" style="width: 330px"><img class="size-full wp-image-424 " title="Running the Project" src="http://kevindion.com/blog/wp-content/uploads/2010/12/odometer-screen1.png" alt="Screenshot of running the newly created project" width="320" height="480" /><p class="wp-caption-text">Running the new project</p></div>
<h2>OdometerSpinner</h2>
<p>To begin our custom widget creation, we will first create the building blocks for the odometer. Our first class, OdometerSpinner, will represent a single digit in the final odometer. In case you aren&#8217;t familiar with the old non-digital odometers, each digit is on its own wheel/spinner that goes from 0 to 9 and wraps at 10. So as you are driving along, the digits on a spinner go &#8230;8-9-0-1&#8230; When a spinner goes from 9 to 0, the next spinner increments as well, but we won&#8217;t be implementing that behavior, since it would be a pain to input numbers if our widget worked that way. Also, be aware that I use the term &#8216;Spinner&#8217; in a different context than the standard Android &#8216;Spinner&#8217; UI class, which is more of a drop-down (or at least that&#8217;s how I think of it).</p>
<h3>Create the OdometerSpinner class</h3>
<p>Add a new class to your project, using the settings shown in the image below. You want to inherit from View, and I go ahead and have it auto-generate the default constructors for me, as well as any abstract methods (although there aren&#8217;t any when inheriting form View). I also have &#8216;Generate comments&#8217; checked, although you don&#8217;t have to. The important thing is to make a class names &#8216;OdometerSpinner&#8217; that subclasses android.view.View.</p>
<div id="attachment_432" class="wp-caption aligncenter" style="width: 552px"><img class="size-full wp-image-432" title="Creating the OdometerSpinner class" src="http://kevindion.com/blog/wp-content/uploads/2010/12/class-creation.png" alt="OdometerClass settings" width="542" height="651" /><p class="wp-caption-text">Creation settings for OdometerClass</p></div>
<p>Once you create the class, you will see that there are three different constructors generated for you, looking something like this:</p>
<pre class="brush: java; title: ; notranslate">
public OdometerSpinner(Context context)
{
    super(context);
}

public OdometerSpinner(Context context, AttributeSet attrs)
{
    super(context, attrs);
}

public OdometerSpinner(Context context, AttributeSet attrs, int defStyle)
{
    super(context, attrs, defStyle);
}
</pre>
<p>These are the three View constructors, and it is important you override them all in your custom classes. The first constructor is the one called when you create the class in code. The second and third come into play when your custom View is created from an xml file. The AttributeSet passed is the set of xml attributes defined &#8211; the defStyle in the third constructor has to do with applying a custom theme &#8211; I have never used it myself, but I figure it is best to override the constructor just in case.</p>
<h4>onMeasure() and onDraw()</h4>
<p>The next methods you will need to override in OdometerSpinner are onMeasure and onDraw. So go ahead and add them through Source &gt; Override/Implement Methods&#8230; and select onMeasure(int, int) and onDraw(Canvas). It is recommended that you override these methods whenever you are making your own custom View, so while we won&#8217;t do much with them right now, we&#8217;ll go ahead and get them in place. They should look something like this (comments are mine):</p>
<pre class="brush: java; title: ; notranslate">
protected void onDraw(Canvas canvas)
{
    super.onDraw(canvas);

    // TODO draw stuff
}

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    // MAKE SURE you call super.onMeasure(int, int) here!!!
    // At least until we implement our own measuring logic
}
</pre>
<h4>Adding OdometerSpinner to the Activity</h4>
<p>The next step is to add the OdometerSpinner to our Activity&#8217;s layout so we can see it when we run. Open up main.xml in the layout/ folder and add OdometerSpinner so it looks like this:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    android:orientation=&quot;vertical&quot;
    android:layout_width=&quot;fill_parent&quot;
    android:layout_height=&quot;fill_parent&quot;
    &gt;
	&lt;TextView
	    android:layout_width=&quot;fill_parent&quot;
	    android:layout_height=&quot;wrap_content&quot;
	    android:text=&quot;@string/hello&quot;
	    /&gt;
	&lt;com.kdion.tutorial.odometer.OdometerSpinner
	   android:id=&quot;@+id/single_spinner&quot;
	   android:layout_width=&quot;fill_parent&quot;
	   android:layout_height=&quot;fill_parent&quot;
	   /&gt;
&lt;/LinearLayout&gt;
</pre>
<p>Your syntax for OdometerSpinner might not be &#8216;<code>com.kdion.tutorial.odometer.OdometerSpinner</code>&#8216;, just change the package path to whatever you are using for your package. The important thing to note is to set the <code>layout_width</code> and <code>layout_height</code> to <code>fill_parent</code> (this has to do with not overriding the logic in onMeasure(), so until we put logic in there leave the width and height as fill_parent). I also just kept the default TextView with the &#8220;Hello, World!&#8221; message for grins, but you don&#8217;t have to.</p>
<p>Now, go ahead and run your app once again&#8230;but wait! Nothing shows up for our spinner! It is still there, we just haven&#8217;t yet drawn anything &#8211; so it is invisible. We will start drawing next.</p>
<h3>Drawing the Background</h3>
<p>The first thing we will do for drawing the background is to simply draw a solid rectangle to represent our OdometerSpinner. The simplest way to do this is to add the following drawing commands to OdometerSpinner&#8217;s <code>onDraw()</code> method:</p>
<pre class="brush: java; title: ; notranslate">
protected void onDraw(Canvas canvas)
{
	super.onDraw(canvas);

	int width = getMeasuredWidth();
	int height = getMeasuredHeight();

	Paint p = new Paint();
	p.setColor(Color.GREEN);

	canvas.drawRect(0, 0, width, height, p);
}
</pre>
<p>First, we get the width and height of our View by calling <code>getMeasuredWidth()</code> and <code>getMeasuredHeight()</code>. Next, we create a Paint object and set it a green color. Finally, we call drawRect() on the Canvas object passed to us. The first two zeros passed as arguments to the drawRect() function define the top left corner of the rectangle (if you are unfamiliar with computer graphics, the coordinate system used by <em>most</em> drawing packages has the origin at the top left corner of the screen, with increasing X-values going to the right and increasing Y-values going down).</p>
<p>Now, if we run our app, we see that there is now a nice big green rectangle taking up most of the screen.</p>
<div id="attachment_465" class="wp-caption aligncenter" style="width: 330px"><img class="size-full wp-image-465" title="Green Screen" src="http://kevindion.com/blog/wp-content/uploads/2010/12/greenscreen.png" alt="The app running with a bright green background" width="320" height="480" /><p class="wp-caption-text">Running our app with a green background</p></div>
<h4>Improving the drawing</h4>
<p>Before we move on to changing up the background to look more like an odometer, we need to change <em>how</em> we are drawing the background. As we have the code now, we query the width and height of our View, create a Paint object, and draw the rectangle. This is horribly inefficient, since <code>onDraw()</code> might be getting called many times a second (at least once we start having scrolling numbers and such). So what we need to do is move as much of the overhead as possible out of <code>onDraw()</code>. To do that, we will add new class member variables for the width, height, background rectangle, and background paint. In general, it is considered &#8216;best practice&#8217; in Android to move things to class members rather than re-calculate within the methods every time they are called. So, add the following variables to the OdometerSpinner class:</p>
<pre class="brush: java; title: ; notranslate">
public class OdometerSpinner extends View
{
	private float mWidth;
	private float mHeight;
	
	private RectF mBGRect;
	
	private Paint mBGPaint;
	//...
</pre>
<p>Now that we have those new variables, we need to initialize the Paint and RectF when the class is instantiated. So add the following <code>initialize()</code> method to OdometerSpinner, and add calls to <code>initialize()</code> in each of the 3 constructors.</p>
<pre class="brush: java; title: ; notranslate">
private void initialize()
{
	mBGRect = new RectF();
	
	mBGPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
	mBGPaint.setColor(Color.GREEN);
}
</pre>
<p>Now that we have the Paint saved in our class, we need to store the width and height of our widget. Instead of measuring the width and height every time we draw, we really only need to change our stored measurements when the size of the View changes. And wouldn&#8217;t you know it, there is a method you can override in View that is called every time the size changes &#8211; <code>onSizeChanged()</code>. Add an override for onSizeChanged in OdometerSpinner and put the following in it:</p>
<pre class="brush: java; title: ; notranslate">
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
	super.onSizeChanged(w, h, oldw, oldh);
	
	mWidth = w;
	mHeight = h;
	
	mBGRect.set(0, 0, w, h);
}
</pre>
<p>Here we set the width and height, and resize our saved rectangle to the new size &#8211; now we don&#8217;t need to perform the cumbersome recalculation every time we draw. Now just change the onDraw() code:</p>
<pre class="brush: java; title: ; notranslate">
protected void onDraw(Canvas canvas)
{
	super.onDraw(canvas);
	
	canvas.drawRect(mBGRect, mBGPaint);
}
</pre>
<p>Much nicer, isn&#8217;t it? You can go ahead and run and see that there is stil that big green screen.</p>
<h4>Drawing Gradients</h4>
<p>Now it is time to get rid of that bright green and replace it with a more realistic-looking gradient for the background. Using a gradient for the background will give us the illusion of depth on out spinner and make it look like we are showing part of a round object &#8211; just what we want. </p>
<p>At the top of OdometerSpinner, add a new member variable for this <code>GradientDrawable</code>, and add its initialization in <code>initialize()</code>. You can remove the <code>Paint</code> and <code>RectF</code> we were using before. Also, add the resizing logic in <code>onSizeChanged()</code>, and draw the gradient in <code>onDraw()</code>.</p>
<pre class="brush: java; title: ; notranslate">
private GradientDrawable mBGGrad;
//...
private void initialize()
{
	mBGGrad = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, 
				new int[] { 0xFF000000, 0xFFAAAAAA, 0xFF000000 });
}
//...
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
	super.onSizeChanged(w, h, oldw, oldh);
	
	mWidth = w;
	mHeight = h;

        mBGGrad.setBounds(0, 0, w, h);
}
//...
protected void onDraw(Canvas canvas)
{
	super.onDraw(canvas);
	
	mBGGrad.draw(canvas);
}
</pre>
<p>In <code>initialize()</code> we create a new <code>GradientDrawable</code>, setting the direction to go from top to bottom. We also pass in an array of ints, which are the ordered colors we want in the gradient. We tell the gradient to have black at the top, fade to a medium gray in the middle, and back to black. The format of the ints we pass is <code>0xAARRGGBB</code>, where <code>AA</code> is the alpha channel, and <code>RR</code>, <code>GG</code>, and <code>BB</code> are the red, green, and blue channels, respectively.</p>
<p>In <code>onSizeChanged()</code>, we tell the gradient its new dimensions. In <code>onDraw()</code>, we tell it to draw itself on the canvas. Now when you run the app, you should get a nice vertical gradient as shown below.</p>
<div id="attachment_464" class="wp-caption aligncenter" style="width: 330px"><img class="size-full wp-image-464" title="Background Gradient" src="http://kevindion.com/blog/wp-content/uploads/2010/12/gradient.png" alt="Our app running with a gradient background" width="320" height="480" /><p class="wp-caption-text">Our OdometerSpinner with the gradient background</p></div>
<h3>Throwing Numbers Around</h3>
<p>Now that we have a nice looking background, we need to have our odometer actually display some numbers. Rather than worry about all of the logic for changing numbers, having the values wrap, and so on &#8211; for now, we&#8217;ll just draw a single, hard-coded digit. </p>
<p><code>Canvas</code> has a nice method to draw a string, so we will need a <code>String</code> to hold the text representation of our digit (again, rather than convert int to <code>String</code> in every <code>onDraw(</code>) call, we store it as a separate variable). We will also need a <code>Paint</code> to control how the text looks, so we will add those variables. We will initialize them along with our other class members in <code>initialize()</code>, then draw them in <code>onDraw()</code>.</p>
<pre class="brush: java; title: ; notranslate">
private String mDigitString;
private Paint mDigitPaint;
//...
private void initialize()
{
	mBGGrad = new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, 
			new int[] { 0xFF000000, 0xFFAAAAAA, 0xFF000000 });

	mDigitString = &quot;8&quot;;
	
	mDigitPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
	mDigitPaint.setColor(Color.WHITE);
	mDigitPaint.setTextAlign(Align.CENTER);
}	
//...
protected void onDraw(Canvas canvas)
{
	super.onDraw(canvas);
	
	mBGGrad.draw(canvas);
	
	float digitX = mWidth / 2;
	float digitY = mHeight / 2;
	
	canvas.drawText(mDigitString, digitX, digitY, mDigitPaint);
}
</pre>
<p>I went ahead and used 8 as the digit we will display for now, since it is pretty easy to tell if it is centered on the screen when it is displayed. We also set up the Paint we use to draw the digit, telling it to center the text and color it white. In <code>onDraw()</code>, we draw the digit string in the center of the OdometerSpinner with the appropriate Paint. Running our app at this point gives us the following:</p>
<div id="attachment_467" class="wp-caption aligncenter" style="width: 330px"><img class="size-full wp-image-467" title="Small Number" src="http://kevindion.com/blog/wp-content/uploads/2010/12/smallnum.png" alt="The OdometerSpinner with a tiny number in the middle" width="320" height="480" /><p class="wp-caption-text">Drawing a tiny number</p></div>
<h4>Sizing</h4>
<p>As you can see, the text is very tiny, because Android by uses the default text size for the Paint since we did not specify a size. Since the size we want the digits to be will depend on the size of the spinner itself, we can put the following line of code in the onSizeChanged() call to set a proper text size.</p>
<pre class="brush: java; title: ; notranslate">
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
	//... other code here...
	mDigitPaint.setTextSize(h);
}
</pre>
<p>This sets the text height to be the height of the spinner, which should fill up the spinner with the digit, which is what we want. Running now gives us this:</p>
<div id="attachment_463" class="wp-caption aligncenter" style="width: 330px"><img class="size-full wp-image-463" title="Big Number" src="http://kevindion.com/blog/wp-content/uploads/2010/12/choppednum.png" alt="A large 8 drawn on the screen, but clipped at the top" width="320" height="480" /><p class="wp-caption-text">Big number, bad placement</p></div>
<h4>Placement</h4>
<p>The text is now big enough, but it is not placed correctly. In <code>onDraw()</code>, when we passed the Y-value to draw as <code>mHeight/2</code>, that actually specifies the <em>bottom</em> of the text. We can pass <code>mWidth/2</code> for the X-value and get it centered horizontally because Paint has a text alignment that we set as <code>CENTER</code>. In order to get our text centered vertically, we are going to have to perform a bit more calculation, measuring how tall the text we want to display is, and using that measurement to calculate where we need to place it to get it perfectly centered. Unfortunately, every digit (0-9) might have different heights, so we don&#8217;t want to use a single, hard-coded value. Fortunately, there are some built-in functions we can use to get the exact height of what we want to draw. Add two new variables, <code>mDigitX</code> and <code>mDigitY</code> to store the location we want to draw the digit, and add the following code to <code>onSizeChanged()</code> and <code>onDraw()</code></p>
<pre class="brush: java; title: ; notranslate">
private float mDigitX;
private float mDigitY;
//...
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
	super.onSizeChanged(w, h, oldw, oldh);
	
	mWidth = w;
	mHeight = h;
	
	mBGGrad.setBounds(0, 0, w, h);
	
	mDigitPaint.setTextSize(h);
	
	Rect bounds = new Rect();
	mDigitPaint.getTextBounds(mDigitString, 0, 1, bounds);
	
	int textHeight = Math.abs(bounds.height());
	
	mDigitX = mWidth / 2;
	mDigitY = mHeight - ((mHeight - textHeight) / 2);
}
//...
protected void onDraw(Canvas canvas)
{
	super.onDraw(canvas);
	
	mBGGrad.draw(canvas);
	
	canvas.drawText(mDigitString, mDigitX, mDigitY, mDigitPaint);
}
</pre>
<p>In <code>onSizeChanged()</code>, we use <code>getTextBounds()</code> to measure the smallest rectangle that would fit around <code>mDigitString</code> (characters <code>0</code> to <code>1</code>), putting those dimensions in the rectangle <code>bounds</code>. Then, we use the height of that rectangle (taking the absolute value since it isn&#8217;t guaranteed to be positive, and a negative number would mess with our calculations) to calculate exactly where to place the digit when we draw it. With the appropriate changes to the arguments in <code>onDraw()</code>, we now get the following when we run our app:</p>
<div id="attachment_466" class="wp-caption aligncenter" style="width: 330px"><img class="size-full wp-image-466" title="Correctly Placed" src="http://kevindion.com/blog/wp-content/uploads/2010/12/placednum.png" alt="The large number drawn centered on the screen" width="320" height="480" /><p class="wp-caption-text">Correctly placed, correctly sized number</p></div>
<p>That is all for Part I of this tutorial, in Part II we will add touch interaction and the ability to change numbers with our spinner. </p>
<h6>Source</h6>
<p>Here is the <a href="http://kevindion.com/blog/wp-content/uploads/2010/12/OdometerTutorial1.zip">source code for Part I</a>. (.zip file, Right-click > Save As&#8230;)</p>
<h6>Odometer Tutorial</h6>
<ol>
<li>Part I: First Steps <em>&#171 You are here</em></li>
<li><a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-2/">Part II: Touch Input and Scrolling Numbers</a></li>
<li><a href="http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-3/">Part III: Compound Widgets and Fixing Bugs</a></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://kevindion.com/2010/12/android-odometer-ui-tutorial-part-1/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Diving Boynton Beach</title>
		<link>http://kevindion.com/2010/10/diving-boynton-beach/</link>
		<comments>http://kevindion.com/2010/10/diving-boynton-beach/#comments</comments>
		<pubDate>Tue, 26 Oct 2010 23:52:35 +0000</pubDate>
		<dc:creator>Kevin Dion</dc:creator>
				<category><![CDATA[Photos]]></category>
		<category><![CDATA[Travel]]></category>
		<category><![CDATA[diving]]></category>

		<guid isPermaLink="false">http://kevindion.com/?p=418</guid>
		<description><![CDATA[This past Saturday I went diving down in Boynton Beach with the great guys at American Divers. I just got a new camera and couldn&#8217;t wait to try it out underwater! We did a three-tank dive at a few reefs, and actually ended up doing somewhat of a reverse dive profile with a really short [...]]]></description>
				<content:encoded><![CDATA[
<a href="http://kevindion.com/blog/wp-content/gallery/boynton_oct10/images/img_0117.jpg" title="" class="shutterset_singlepic333" >
	<img class="ngg-singlepic ngg-left" src="http://kevindion.com/blog/wp-content/gallery/cache/333__320x240_img_0117.jpg" alt="Turtle" title="Turtle" />
</a>

<p>This past Saturday I went diving down in Boynton Beach with the great guys at <a title="American Divers International" href="http://americandivers.net">American Divers</a>. I just got a new camera and couldn&#8217;t wait to try it out underwater! We did a three-tank dive at a few reefs, and actually ended up doing somewhat of a reverse dive profile with a really short dive first, with a longer second and third dive (depths for all were around 45-50 ft). But it worked out well, since the best reef by far was the last &#8211; with a lot of cool wildlife! Saw 3-4 turtles, a couple of rays, and a good number of bugs (lobster). Even saw my first eel! All in all it was a reminder of why I love diving so much &#8211; and a reminder that I really should get my nitrox cert&#8230;</p>

<a href="http://kevindion.com/blog/wp-content/gallery/boynton_oct10/images/img_0154.jpg" title="" class="shutterset_singlepic347" >
	<img class="ngg-singlepic ngg-center" src="http://kevindion.com/blog/wp-content/gallery/cache/347__320x240_img_0154.jpg" alt="img_0154" title="img_0154" />
</a>

]]></content:encoded>
			<wfw:commentRss>http://kevindion.com/2010/10/diving-boynton-beach/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Europe Photos Added</title>
		<link>http://kevindion.com/2010/10/europe-photos-added/</link>
		<comments>http://kevindion.com/2010/10/europe-photos-added/#comments</comments>
		<pubDate>Wed, 06 Oct 2010 01:41:11 +0000</pubDate>
		<dc:creator>Kevin Dion</dc:creator>
				<category><![CDATA[Travel]]></category>
		<category><![CDATA[Works]]></category>
		<category><![CDATA[Europe 2009]]></category>
		<category><![CDATA[Photos]]></category>

		<guid isPermaLink="false">http://kevindion.com/?p=395</guid>
		<description><![CDATA[I have put up a bunch of photos from my trip last summer to Europe. Destinations include Germany, Austria, Italy, and Switzerland. Few images have meaningful titles or details yet, but check them out anyways! Photos]]></description>
				<content:encoded><![CDATA[
<a href="http://kevindion.com/blog/wp-content/gallery/2009-europa/images/fussen_river_pano.jpg" title="" class="shutterset_singlepic260" >
	<img class="ngg-singlepic" src="http://kevindion.com/blog/wp-content/gallery/cache/260__480xfloat=center_fussen_river_pano.jpg" alt="fussen_river_pano" title="fussen_river_pano" />
</a>

<p>I have put up a bunch of photos from my trip last summer to Europe. Destinations include Germany, Austria, Italy, and Switzerland. Few images have meaningful titles or details yet, but check them out anyways!</p>
<p><a title="Kevin's Photos" href="http://kevindion.com/photos">Photos</a></p>
]]></content:encoded>
			<wfw:commentRss>http://kevindion.com/2010/10/europe-photos-added/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Presenting: SAAZenheimer Uberweizenbock Label</title>
		<link>http://kevindion.com/2010/09/presenting-saazenheimer-uberweizenbock-label/</link>
		<comments>http://kevindion.com/2010/09/presenting-saazenheimer-uberweizenbock-label/#comments</comments>
		<pubDate>Thu, 30 Sep 2010 01:13:22 +0000</pubDate>
		<dc:creator>Kevin Dion</dc:creator>
				<category><![CDATA[Works]]></category>
		<category><![CDATA[Beer]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[Labels]]></category>
		<category><![CDATA[SAAZ]]></category>

		<guid isPermaLink="false">http://kevindion.com/?p=328</guid>
		<description><![CDATA[This is the label I did up for the commemorative beer made specially for the 16th Commander SAAZ Interplanetary Homebrew Blastoff. The name: SAAZenheimer Uberweizenbock &#8211; a traditional style weizenbock brewed especially for the 2010 Interplanetary Homebrew Blastoff. SAAZ, of course, stands for the Space Coast Associates for the Advancement of Zymurgy &#8211; in other [...]]]></description>
				<content:encoded><![CDATA[<p style="text-align: center;"><a rel="attachment wp-att-331" href="http://kevindion.com/2010/09/presenting-saazenheimer-uberweizenbock-label/saaz-2010-2-2/"><img class="aligncenter size-large wp-image-331" title="SAAZenheimer Uberweizenbock Label" src="http://kevindion.com/blog/wp-content/uploads/2010/09/saaz-2010-21-1024x853.png" alt="SAAZenheimer Uberweizenbock Label" width="461" height="383" /></a></p>
<p>This is the label I did up for the commemorative beer made specially for the <a href="http://www.saaz.org">16th Commander SAAZ Interplanetary Homebrew Blastoff</a>.<span id="more-328"></span> The name: SAAZenheimer Uberweizenbock &#8211; a traditional style weizenbock brewed especially for the 2010 Interplanetary Homebrew Blastoff. SAAZ, of course, stands for the Space Coast Associates for the Advancement of Zymurgy &#8211; in other words the local homebrew club. Every year they host the Interplanetary Homebrew Blastoff, which is a national beer and mead competition.</p>
<p>I wanted to make a label that had a traditional feel, with blackletter styling and old-world flair. I also wanted it to have a good amount of extra detail since in the past they (SAAZ) have printed out the labels in letter size and given them as raffle prizes.</p>
]]></content:encoded>
			<wfw:commentRss>http://kevindion.com/2010/09/presenting-saazenheimer-uberweizenbock-label/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TED a Day</title>
		<link>http://kevindion.com/2010/09/ted-a-day/</link>
		<comments>http://kevindion.com/2010/09/ted-a-day/#comments</comments>
		<pubDate>Thu, 23 Sep 2010 15:56:55 +0000</pubDate>
		<dc:creator>Kevin Dion</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://kevindion.com/blog/?p=287</guid>
		<description><![CDATA[I absolutely Love TED. I think it might just be one of the greatest things on the web, and should be required viewing in every school. In fact, I could see an entire class devoted to watching and discussing TED talks. The variety of topics and level of prestige the presenters have is amazing, and [...]]]></description>
				<content:encoded><![CDATA[<p>I absolutely Love TED. I think it might just be one of the greatest things on the web, and should be required viewing in every school. In fact, I could see an entire class devoted to watching and discussing TED talks. The variety of topics and level of prestige the presenters have is amazing, and just being exposed to the amazing work in a domain you are unfamiliar with is extremely beneficial, regardless of what you do.<br />
<span id="more-287"></span><br />
I have known of TED for a while, when one of my friends would send me a a link to a particularly inspiring or engaging talk. Oddly, any time someone shares a TED talk, the comment usually goes something like this: “OMG you have to see this! it is AMAZING!”. And invariably, it was amazing, inspiring, and all of those other adjectives. What I did not realize until just a few days ago, however, was just how many talks there are &#8211; I didn’t realize that TED has been going on for decades, and they have an extensive back-catalog of archived videos (more than 700 to date). That is a lot of potential inspiration!</p>
<p>So in an effort to continue expanding my mind, I have decided to watch every single TED talk. While this may seem like a monstrous endeavor, I have found that at the very least over the last few days it has only taken a small change in my daily routine to knock out at least one talk a day, and usually a few more. At the moment I am working chronologically forward, going through the TED events starting from the earliest (1984) to the present.</p>
<p>So here’s to expanding my mind.</p>
]]></content:encoded>
			<wfw:commentRss>http://kevindion.com/2010/09/ted-a-day/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic page generated in 0.341 seconds. -->
<!-- Cached page generated by WP-Super-Cache on 2013-05-23 12:13:24 -->
