<?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>Developer World &#187; x10</title>
	<atom:link href="http://developer.sonymobile.com/wp/tag/x10/feed/" rel="self" type="application/rss+xml" />
	<link>http://developer.sonymobile.com/wp</link>
	<description>Sony Ericsson Developer World</description>
	<lastBuildDate>Wed, 16 May 2012 13:16:03 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>How to take advantage of the pinch-to-zoom feature in your Xperia™ X10 apps &#8211; Part 2</title>
		<link>http://developer.sonymobile.com/wp/2011/04/12/how-to-take-advantage-of-the-pinch-to-zoom-feature-in-your-xperia%e2%84%a2-10-apps-part-2/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=how-to-take-advantage-of-the-pinch-to-zoom-feature-in-your-xperia%25e2%2584%25a2-10-apps-part-2</link>
		<comments>http://developer.sonymobile.com/wp/2011/04/12/how-to-take-advantage-of-the-pinch-to-zoom-feature-in-your-xperia%e2%84%a2-10-apps-part-2/#comments</comments>
		<pubDate>Tue, 12 Apr 2011 20:02:54 +0000</pubDate>
		<dc:creator>Ed Ort</dc:creator>
				<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[applications]]></category>
		<category><![CDATA[Community]]></category>
		<category><![CDATA[developers]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[x10]]></category>
		<category><![CDATA[xperia x10]]></category>
		<category><![CDATA[Zoom]]></category>

		<guid isPermaLink="false">http://blogs.sonyericsson.com/developerworld/?p=2149</guid>
		<description><![CDATA[Recently Sony Ericsson rolled out an update to the Android™ 2.1 operating system in its Xperia™ X10 phones. One of the important new features in the update is support for a multi-touch gesture called pinch-to-zoom. The first part of this two-part tutorial showed how to take advantage of the pinch-to-zoom feature in your apps. In [...]]]></description>
			<content:encoded><![CDATA[<p>Recently Sony Ericsson rolled out an update to the Android™ 2.1 operating system in its Xperia™ X10 phones. One of the important new features in the update is support for a multi-touch gesture called pinch-to-zoom. The <a title="Pinch to zoom feature in Xperia X10 tutorial - Part 1" href="http://blogs.sonyericsson.com/developerworld/2011/04/12/how-to-take-advantage-of-the-pinch-to-zoom-feature-in-your-xperia%e2%84%a2-10-apps-part-1/" target="_blank">first part of this two-part tutorial </a>showed how to take advantage of the pinch-to-zoom feature in your apps. In this second part of the tutorial, you’ll examine a code example that uses the pinch-to-zoom feature.<br />
<span id="more-2149"></span></p>
<p><strong>A pinch-to-zoom code example</strong></p>
<p>Now that we’ve looked at some of the basics of implementing pinch-to-zoom, let’s examine a pinch-to-zoom listener class named PinchZoomListener that puts those basics into action. The PinchZoomListener class is part of a project which you can download and use to build the application.</p>
<p><a title="Pinch to zoom sample project download" href="http://developer.sonyericsson.com/wportal/devworld/downloads/download/xperiax10pinchzoomcodeexampleszip?cc=gb&amp;lc=en" target="_blank">Download the pinch-to-zoom sample project (0.66 MB)</a></p>
<p>Remember that the application can recognize and process pinch-to-zoom gestures in Xperia™ X10 only if you install the pinch-to-zoom support in the device.</p>
<p><strong>Implementing the OnTouchListener interface</strong></p>
<p>The first thing to notice about PinchZoomListener is that it implements the View.OnTouchListener interface. This is indicated in the class definition, as shown below.</p>
<pre>public class PinchZoomListener implements View.OnTouchListener {

    …

{</pre>
<p>By implementing the View.OnTouchListener interface, PinchZoomListener is invoked when a user touches the screen.</p>
<p><strong>Registering the listener</strong></p>
<p>Remember that the listener needs to be registered by an activity. In the application, it is registered in an activity class, TutorialZoomActivity4, as follows:</p>
<pre>import com.sonyericsson.zoom.ImageZoomView;
import com.sonyericsson.zoom.PinchZoomListener;
import android.view.View;

public class TutorialZoomActivity4 extends Activity {

private ImageZoomView mZoomView;
private PinchZoomListener mPinchZoomListener;

   @Override
      public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);  

         mPinchZoomListener = new PinchZoomListener(getApplicationContext());
         mZoomView.setOnTouchListener(mPinchZoomListener);
         …
      }</pre>
<p>Notice the use of the ImageZoomView class. This class provides a View that is capable of drawing an image at different zoom levels. The ImageZoomControl class is explained in <a title="One finger zoom tutorial" href="http://blogs.sonyericsson.com/developerworld/2010/05/18/android-one-finger-zoom-tutorial-part-1/" target="_blank">Android one finger zoom tutorial- Part 1 that was published earlier in this blog</a>.</p>
<p>If you’re familiar with the one finger zoom sample project that accompanies the Android one finger zoom tutorial, you’ll notice that the pinch-to-zoom sample is architected quite similarly to the one finger zoom application. The major difference is the addition of the PinchZoomListener.</p>
<p><strong>Defining listener modes</strong></p>
<p>The next thing to notice in PinchZoomListener is that it defines three listener modes, each indicating a touch state. Here is the definition:</p>
<pre>private enum Mode {
    UNDEFINED, PAN, PINCHZOOM
}

private Mode mMode = Mode.UNDEFINED;</pre>
<p>The three listener mode constants and their meanings are:</p>
<ul>
<li>PAN. Indicates that the user touched the screen and moved his or her finger over the view.</li>
<li>PINCHZOOM. Indicates that the user touched the screen with a second finger while continuing to touch the screen with the first finger.</li>
<li>UNDEFINED. Indicates that the state is not PAN or PINCHZOOM. The user may or may not be touching the screen. Initially the listener sets the state to UNDEFINED.</li>
</ul>
<p><strong>Handling gestures</strong></p>
<p>The core of the processing is focused on handling user gestures. Recall from <a title="Pinch to zoom feature for Xperia X10 tutorial - Part 1" href="http://blogs.sonyericsson.com/developerworld/2011/04/12/how-to-take-advantage-of-the-pinch-to-zoom-feature-in-your-xperia%e2%84%a2-10-apps-part-1/" target="_blank">Part 1 of the tutorial</a> that the handler uses the onTouch() method in the View.OnTouchListener interface to process touch events. Here is part of the definition of the onTouch() method in PinchZoomListener.</p>
<pre>private VelocityTracker mVelocityTracker;
public boolean onTouch(View v, MotionEvent event) {
    final int action = event.getAction() &amp; MotionEvent.ACTION_MASK;
    final float x = event.getX();
    final float y = event.getY();
if (mVelocityTracker == null) {
    mVelocityTracker = VelocityTracker.obtain();
   }
        mVelocityTracker.addMovement(event);
   …
 }</pre>
<p>The onTouch() method uses the getAction() method to get the action from the MotionEvent object for the touch event. To get the action code, the onTouch() method performs a bitwise AND operation on the retrieved action and the ACTION_MASK constant. Then the method gets the X and Y coordinates of the touched position on the screen.</p>
<p>Notice the use of the VelocityTracker class. A VelocityTracker object will be used to track the velocity of the touch events.</p>
<p><strong>Handling ACTION_DOWN events</strong></p>
<p>After getting the action-related information from the MotionEvent object, your application needs to handle the action. Here is how the listener processes an ACTION_DOWN event, that is, when the user touches the screen with one finger:</p>
<pre>switch (action) {
    case MotionEvent.ACTION_DOWN:
        mZoomControl.stopFling();
        mDownX = x;
        mDownY = y;
        mX = x;
        mY = y;
        break;</pre>
<p>As its name implies, mZoomControl controls zooming in the application. It is assigned a DynamicZoomControl object that is provided in the setZoomControl() method of the application, as shown below.</p>
<pre>public void setZoomControl(DynamicZoomControl control) {
    mZoomControl = control;
}</pre>
<p>The DynamicZoomControl class is fully explained in <a title="Android one finger zoom tutorial - Part 4" href="http://blogs.sonyericsson.com/developerworld/2010/07/07/android-one-finger-zoom-tutorial-part-4/" target="_blank">Android one finger zoom tutorial – Part 4</a>, published earlier in this blog. The stopFling() method of DynamicZoomControl stops the animation associated with a fling gesture. A fling gesture means that the user dragged an object across the screen and then removed his or her finger from the screen. This result is an animation in which the object continues to move but slows over time. The ACTION_DOWN event invokes stopFling() to stop processing the fling gesture. This handles the situation where the user made a fling gesture before retouching the screen.</p>
<p>The mX and mY variables are used to record the X and Y coordinates, respectively, of the current touch on the screen. The mDownX and mDownY variables are used to record the X and Y coordinates, respectively, of the previous touch on the screen. The four variables are set to the coordinate values for the current ACTION_DOWN event.</p>
<p><strong>Handling ACTION_POINTER_DOWN events</strong></p>
<p>Now let’s look at the code in PinchZoomListener that processes an ACTION_POINTER_DOWN event.</p>
<pre>case MotionEvent.ACTION_POINTER_DOWN:
    if (event.getPointerCount() &gt; 1) {
        oldDist = spacing(event);
        if (oldDist &gt; 10f) {
            midPoint(mMidPoint, event);
            mMode = Mode.PINCHZOOM;
        }
    }
    break;</pre>
<p>In this case, the user should have two fingers touching the screen. If so, the listener calculates the distance between the two fingers using a spacing() method, as follows.</p>
<pre>private float spacing(MotionEvent event) {
    float x = event.getX(0) - event.getX(1);
    float y = event.getY(0) - event.getY(1);
    return FloatMath.sqrt(x * x + y * y);</pre>
<p>You might wonder why the distance between the fingers is tested, that is, if(oldDist &gt; 10f) {…}. The test is needed because the support in Android for multi-touch events such as pinch-to-zoom is still not optimal (see <em>Limitations</em> below). For example, sometimes Android incorrectly reports that two fingers are touching in almost the same position. To guard against anomalous situations such as this, the listener ignores events where the distance between the two fingers is less than a certain threshold (in this case 10f). If the distance is greater than the threshold, the listener finds the midpoint of the distance using a midPoint() method, as shown below. The midpoint will be used as the center point of the zoom.</p>
<pre>private void midPoint(PointF point, MotionEvent event) {
        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);
        point.set(x / 2, y / 2);</pre>
<p>With the two fingers now touching the screen, the listener sets the mode to PINCHZOOM.</p>
<p><strong>Handling ACTION_MOVE events</strong></p>
<p>Next, let’s look at the code in PinchZoomListener that handles an ACTION_MOVE event, where the user moves a finger across the screen.</p>
<pre>case MotionEvent.ACTION_MOVE:
    final float dx = (x - mX) / v.getWidth();
    final float dy = (y - mY) / v.getHeight();

    if (mMode == Mode.PAN) {
        mZoomControl.pan(-dx, -dy);
    } else if (mMode == Mode.PINCHZOOM) {
        float newDist = spacing(event);
        if (newDist &gt; 10f) {
            final float scale = newDist / oldDist;
            final float xx = mMidPoint.x / v.getWidth();
            final float yy = mMidPoint.y / v.getHeight();
            mZoomControl.zoom(scale, xx, yy);
            oldDist = newDist;
        }
    } else {
        final float scrollX = mDownX - x;
        final float scrollY = mDownY - y;

        final float dist =
            (float)Math.sqrt(scrollX * scrollX + scrollY * scrollY);

        if (dist &gt;= mScaledTouchSlop ){
            mMode = Mode.PAN;
        }
    }

    mX = x;
    mY = y;
    break;</pre>
<p>Here, the listener determines if the action is part of a pan gesture or a pinch-to-zoom gesture. In either case, it uses the dynamic zoom control to handle the gesture. The zoom action is handled by the zoom() method in the DynamicZoomControl class. The method performs its actions based on the scale of the zoom, an invariant X coordinate position, and an invariant Y coordinate position. The scale of the zoom and the invariant coordinates are defined as follows:</p>
<table width="100%">
<tbody>
<tr>
<td valign="top" width="35%">scale of the zoom</td>
<td valign="top" width="65%">The distance between the finger before the move (oldDist) divided by the distance between the fingers after the move (newDist).</td>
</tr>
<tr>
<td valign="top" width="35%">invariant X coordinate</td>
<td valign="top">The midpoint of the distance between the fingers divided by the width of the view.</td>
</tr>
<tr>
<td valign="top" width="35%">invariant Y coordinate</td>
<td valign="top" width="65%">The midpoint of the distance between the fingers divided by the height of the view.</td>
</tr>
</tbody>
</table>
<p>Here, the term <em>invariant</em> means the zoom method ensures that the coordinate value does not change during the course of the zoom.</p>
<p>Notice that the listener ignores the event if the mode is PINCHZOOM and the distance between the points is less than the space threshold. Again, this is to protect against anomalous results.</p>
<p>If the node is not PAN or PINCHZOOM, the listener tests to see if the move distance exceeds the distance at which the movement is considered scrolling in pixels. The listener sets the threshold from the current context using the getScaledTouchSlop() method. Here is the call to getScaledTouchSlop() in the listener.</p>
<pre>private final int mScaledTouchSlop;

public PinchZoomListener(Context context) {
    mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    …
}</pre>
<p>If the move distance does exceed the threshold, the listener sets the mode to PAN.</p>
<p><strong>Handling ACTION_POINTER_UP events</strong></p>
<p>The next event type to examine is ACTION_POINTER_UP, the case where the user releases one finger while continuing to touch the screen with the other finger. Here is the code that handles that case.</p>
<pre>case MotionEvent.ACTION_POINTER_UP:
    if(event.getPointerCount() &gt; 1 &amp;&amp;  mMode == Mode.PINCHZOOM){
       panAfterPinchTimeout = System.currentTimeMillis() + 100;
    }
    mMode = Mode.UNDEFINED;
    break;</pre>
<p>There is no zoom processing to perform for this event, so the listener simply sets a timeout threshold that it will use if the user subsequently pans across the screen. It then sets the mode to UNDEFINED.</p>
<p><strong>Handling ACTION_UP events</strong></p>
<p>Last, here is the way the ACTION_UP event is handled in the PinchZoomListener.</p>
<pre>case MotionEvent.ACTION_UP:
    if (mMode == Mode.PAN) {
        final long now = System.currentTimeMillis();
        if(panAfterPinchTimeout &lt; now){
           mVelocityTracker.computeCurrentVelocity(1000,
                  mScaledMaximumFlingVelocity);
           mZoomControl.startFling(
                  -mVelocityTracker.getXVelocity() / v.getWidth(),
                  -mVelocityTracker.getYVelocity() / v.getHeight());
        }
        } else if(mMode != Mode.PINCHZOOM) {
                  mZoomControl.startFling(0, 0);
        }
        mVelocityTracker.recycle();
        mVelocityTracker = null;</pre>
<p>The ACTION_UP event covers the situation where the user has one finger touching the screen and then removes it from the screen. If the user was panning across the screen before removing the finger, the listener checks to see that timeout threshold wasn’t reached. If the threshold wasn’t reached, the listener starts a fling animation by calling the startFling() method of the Dynamic ZoomControl object.</p>
<p>When it makes the call to startFling(), the listener specifies the velocity in the X and Y dimensions. These velocities are used by the method in calculating the physics of the animation. If the mode is not PAN or PINCHZOOM, the listener simply releases control by calling the startFling() method and specifying 0 for the velocities in the X and Y dimensions.</p>
<p><strong>Limitations</strong></p>
<p>Xperia™ X10 is not yet fully optimized for multi-touch. There are some cases where a pinch-to-zoom gesture generates unusual behavior. For example, a user might not see the expected zoom in or zoom out in the field of view if he or she crosses the X or Y axis during a pinch-to-zoom gesture. For instance, the zoom in or zoom out might appear jumpy, as illustrated below.</p>
<p><a href="http://blogs.sonyericsson.com/developerworld/files/2011/04/CrossAxis2sm.jpg"><img class="alignnone size-full wp-image-2189" src="http://blogs.sonyericsson.com/developerworld/files/2011/04/CrossAxis2sm.jpg" alt="crossing axis can be jumpy" width="152" height="114" /></a></p>
<p>In addition, a user might not see a smooth zoom in or zoom out movement if his or her fingers are aligned along the X or Y axis, as illustrated below.</p>
<p><a href="http://blogs.sonyericsson.com/developerworld/files/2011/04/SameAxis2sm.jpg"><img class="alignnone size-full wp-image-2191" src="http://blogs.sonyericsson.com/developerworld/files/2011/04/SameAxis2sm.jpg" alt="same axis issues" width="150" height="57" /></a></p>
<p>Note too that the all points addressable screen in the Xperia™ X10 consists of an intersection matrix of row and column sense elements, as illustrated below.</p>
<p><a href="http://blogs.sonyericsson.com/developerworld/files/2011/04/matrix.jpg"><img class="alignnone size-full wp-image-2194" src="http://blogs.sonyericsson.com/developerworld/files/2011/04/matrix.jpg" alt="dual layer" width="141" height="126" /></a></p>
<p>This arrangement is termed dual layer.</p>
<p>The dual layer hardware registers the touch position of a finger in terms of a row and column. For two fingers, it registers two rows and two columns. However, the hardware does not necessarily distinguish which finger is touching the screen at the registered row and column. Also, the hardware does not necessarily distinguish which recorded value represents the row and which the column. As a result, when two fingers touch the screen, as in a pinch-to-zoom gesture, there are four possibilities of how the combined event is registered.</p>
<p>Suppose, for example, that the first finger (let’s call it finger A) touches the screen at row 1 column 1, and the second finger (let’s call it finger B) touches the screen at row 3 column 6, as shown in the following image.</p>
<p><a href="http://blogs.sonyericsson.com/developerworld/files/2011/04/PinchToZoom_rowcolumn.jpg"><img class="alignnone size-full wp-image-2196" src="http://blogs.sonyericsson.com/developerworld/files/2011/04/PinchToZoom_rowcolumn.jpg" alt="pinch zoom row column issues" width="517" height="287" /></a></p>
<p>The hardware might register the fingers in any of the following four ways:</p>
<ul>
<li>Finger A at row 1 and column 1 and finger B at row 3 column 6.</li>
<li>Finger A at row 3 column 6 and finger B at row 1 column 1.</li>
<li>Finger A at row 3 column 1 and finger B at row 1 column 6.</li>
<li>Finger A at row 1 column 6 and finger B at row 3 column 1.</li>
</ul>
<p>This type of axis flipping is also a general problem reported about the multi-touch support in Android 2.1.</p>
<p>Beyond the axis flipping problem, Android 2.1 sometimes records touches by multiple fingers as one finger touch or as multiple fingers touching at almost the same position. So it’s important to check for anomalous results where you can, as the PinchZoomListener in this tutorial does. Recall that the PinchZoomListener tests whether the distance between the two fingers is below a specified distance (10f), and if that distance is below the threshold, the listener ignores the touch event.</p>
<p>If you’re interested in testing your Xperia™ X10 device for multi-touch, you can use the MultiTouch Visualizer 2 application, which you can find in Android Market.</p>
<p><strong>More information</strong></p>
<ul>
<li><a title="Pinch to zoom for Xperia X10 tutorial - Part 1" href="http://blogs.sonyericsson.com/developerworld/2011/04/12/how-to-take-advantage-of-the-pinch-to-zoom-feature-in-your-xperia%e2%84%a2-10-apps-part-1/" target="_blank">How to take advantage of the pinch-to-zoom feature in your Xperia™ 10 apps – Part 1&gt;&gt;&gt;</a></li>
<li><a title="Visualizer 2" href="https://market.android.com/details?id=org.metalev.multitouch.visualizer2" target="_blank">Learn more about Visualizer 2 in AndroidMarket&gt;&gt;&gt;</a></li>
<li><a title="Sony Ericsson Android development" href="http://developer.sonyericsson.com/wportal/devworld/technology/android/get-started?cc=gb&amp;lc=en" target="_blank">Learn more about Android development on Developer World&gt;&gt;&gt;</a></li>
<li><a title="Xperia X10 description" href="http://www.sonyericsson.com/cws/products/mobilephones/overview/xperiax10?cc=gb&amp;lc=en" target="_blank">See Xperia™ X10 on the consumer website&gt;&gt;&gt;</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://developer.sonymobile.com/wp/2011/04/12/how-to-take-advantage-of-the-pinch-to-zoom-feature-in-your-xperia%e2%84%a2-10-apps-part-2/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>How to take advantage of the pinch-to-zoom feature in your Xperia™ X10 apps &#8211; Part 1</title>
		<link>http://developer.sonymobile.com/wp/2011/04/12/how-to-take-advantage-of-the-pinch-to-zoom-feature-in-your-xperia%e2%84%a2-10-apps-part-1/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=how-to-take-advantage-of-the-pinch-to-zoom-feature-in-your-xperia%25e2%2584%25a2-10-apps-part-1</link>
		<comments>http://developer.sonymobile.com/wp/2011/04/12/how-to-take-advantage-of-the-pinch-to-zoom-feature-in-your-xperia%e2%84%a2-10-apps-part-1/#comments</comments>
		<pubDate>Tue, 12 Apr 2011 19:44:57 +0000</pubDate>
		<dc:creator>Ed Ort</dc:creator>
				<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[applications]]></category>
		<category><![CDATA[Community]]></category>
		<category><![CDATA[developers]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[x10]]></category>
		<category><![CDATA[Xperia]]></category>
		<category><![CDATA[Zoom]]></category>

		<guid isPermaLink="false">http://blogs.sonyericsson.com/developerworld/?p=2062</guid>
		<description><![CDATA[&#160; Recently Sony Ericsson rolled out an update to the Android™2.1 operating system in its Xperia™ X10 phones. One of the important new features in the update is support for a multi-touch gesture called pinch-to-zoom. In this first part of a two-part tutorial, you will learn how to take advantage of the pinch-to-zoom feature in [...]]]></description>
			<content:encoded><![CDATA[<p>&nbsp;</p>
<p>Recently Sony Ericsson rolled out an update to the Android™2.1 operating system in its Xperia™ X10 phones. One of the important new features in the update is support for a multi-touch gesture called pinch-to-zoom. In this first part of a two-part tutorial, you will learn how to take advantage of the pinch-to-zoom feature in your apps. In the <a title="Pinch to zoom feature in Xperia X10 tutorial - Part 2" href="http://blogs.sonyericsson.com/developerworld/2011/04/12/how-to-take-advantage-of-the-pinch-to-zoom-feature-in-your-xperia%E2%84%A2-10-apps-part-2/" target="_blank">second part of the tutorial</a>, you’ll examine a code example that uses the pinch-to-zoom feature.<br />
<span id="more-2062"></span><br />
The pinch-to-zoom feature allows users to zoom in and get a closer, more detailed view of a display, or zoom out and get a wider field of view. For example, in the side-by-side image below, the image on the left shows a display zoomed out for a wider field of view. The image on the right shows the display zoomed in for a closer view.</p>
<table width="80%" border="0">
<tbody>
<tr>
<td><a href="http://blogs.sonyericsson.com/developerworld/files/2011/04/X10_zoomout.jpg"><img class="size-full wp-image-2085" src="http://blogs.sonyericsson.com/developerworld/files/2011/04/X10_zoomout.jpg" alt="zoom out " width="214" height="85" /></a></td>
<td width="50%"><a href="http://blogs.sonyericsson.com/developerworld/files/2011/04/X10_zoomin1.jpg"><img class="alignright size-full wp-image-2089" src="http://blogs.sonyericsson.com/developerworld/files/2011/04/X10_zoomin1.jpg" alt="zoom in" width="214" height="85" /></a></td>
</tr>
</tbody>
</table>
<p><em>The pinch-to-zoom feature lets users zoom out for a wider field of view, or zoom in for a closer view.</em></p>
<p>Although not initially available in the Android 2.1 support in Xperia™ X10, pinch-to-zoom is now available as an over-the-air (OTA) enhancement. Note however that Xperia™ X10 is not yet fully optimized for multi-touch. There are some cases where a pinch-to-zoom gesture generates unusual behavior. For more information, see the Limitations section in Part 2 of the tutorial.</p>
<p>You can take advantage of the pinch-to-zoom feature in applications that you develop for Xperia™ X10. Two applications that are packaged with Xperia™ X10 already take advantage of the feature: the web browser and Google Maps. There are also other applications that make use of this feature, such as Angry Birds.</p>
<p>Below is a video that demonstrates the pinch-to-zoom feature in Xperia™ X10.</p>
<p><object width="425" height="350"><param name="movie" value="868sPlG3dCY"></param><param name="wmode" value="transparent" ></param><embed src="http://www.youtube.com/v/868sPlG3dCY" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object></p>
<p><strong>Providing pinch-to-zoom capability in your Xperia™ X10 application</strong></p>
<p>Your Xperia™ X10 application needs to do the following to provide pinch-to-zoom capability:</p>
<ul>
<li>Detect that the user has made a pinch-to-zoom gesture, such as moving two fingers across the screen. This is further explained below in Detecting and getting information about the gesture.</li>
<li>Get information about the gesture, such as what the action was and when it took place. This is further explained below in Detecting and getting information about the gesture.</li>
<li>Process the gesture, that is, respond to the user’s action, as appropriate. This is further explained below in Processing the gesture.</li>
</ul>
<p><strong>Detecting and getting information about the gesture</strong></p>
<p>When a user touches the screen, Android creates an Android™ <a title="Motion Event description" href="http://developer.android.com/reference/android/view/MotionEvent.html" target="_blank">MotionEvent</a> object to record information about the touch event, such as the action the user performed and the time when the user performed it. Your application needs to access the object in order to detect a pinch-to-zoom gesture and get information about it. This requires the application to first import the MotionEvent class as follows:</p>
<pre>import android.view.MotionEvent;</pre>
<p><strong>Processing the gesture</strong></p>
<p>To process the gesture, you create a callback handler. You can use the callback handler to take actions such as get information about a pinch-to-zoom gesture. However, first you need to register the callback handler using the <a title="setOnTouchListener method" href="http://developer.android.com/reference/android/view/View.html#setOnTouchListener(android.view.View.OnTouchListener)" target="_blank">setOnTouchListener()</a> method of the Android <a title="Android View class" href="http://developer.android.com/reference/android/view/View.html" target="_blank">View</a> class, as shown in the following code snippet.</p>
<pre>import android.view.View;
import android.view.View.OnTouchListener;
…
view.setOnTouchListener(onTouchListener);</pre>
<p>The parameter to view.setOnTouchListener represented by <code>onTouchListener</code> is the callback handler, that is, the touch listener that you want to register. The callback handler is attached to a specific view. Touch events that are posted to that view are processed by the associated callback handler.</p>
<p>The callback handler must implement Android’s <a title="View.OnTouchListener interface description" href="http://developer.android.com/reference/android/view/View.OnTouchListener.html" target="_blank">View.OnTouchListener</a> interface and use the <a title="onTouch method" href="http://developer.android.com/reference/android/view/View.OnTouchListener.html#onTouch%28android.view.View,%20android.view.MotionEvent%29" target="_blank">onTouch()</a> method defined in the interface to process the touch events. When you call the onTouch() method, you need to supply as parameters a View object and a MotionEvent object, as shown below.</p>
<pre>public class MyPinchZoom extends Activity implements OnTouchListener {

   public boolean onTouch(View v, MotionEvent event) {
      // Process touch events
   }

}</pre>
<p>A user starts a pinch-to-zoom gesture by touching the screen with one finger. This event is represented in the MotionEvent object by the constant ACTION_DOWN. When the user touches the screen with a second finger while continuing to touch the screen with the first finger, that event is recorded in the MotionEvent object by the constant ACTION_POINTER_DOWN. Other constants represent other actions taken during the pinch-to-zoom gesture. Here are the set of action-related constants for the pinch-to-zoom gesture:</p>
<table width="100%" border="0">
<tbody>
<tr>
<td width="35%">ACTION_DOWN</td>
<td width="65%">The user touched the screen.</td>
</tr>
<tr>
<td width="35%">ACTION_POINTER_DOWN</td>
<td width="65%">The user touched the screen with a second finger while continuing to touch the screen with the first finger.</td>
</tr>
<tr>
<td width="35%">ACTION_MOVE</td>
<td width="65%">The user moved a finger on the screen.</td>
</tr>
<tr>
<td width="35%">ACTION_POINTER_UP</td>
<td width="65%">The user released one finger from the screen while continuing to touch the screen with the other finger.</td>
</tr>
<tr>
<td width="35%">ACTION_UP</td>
<td width="65%">The user released the finger from the screen (only only one finger was touching the screen).</td>
</tr>
<tr>
<td>ACTION_CANCEL</td>
<td width="65%">The user cancelled the current gesture.</td>
</tr>
</tbody>
</table>
<p>After you register the callback handler, you can use it to call methods on the MotionEvent object to get information about a pinch-to-zoom related event. For example, the getAction() method returns the type of action performed: up down, move , or cancel. The following code snippet shows a call to the getAction() method.</p>
<pre>// Process touch events
      int action = event.getAction();</pre>
<p>The lowest 8 bits of the action returned by getAction() represent the action code. To access the action code, you can perform a bitwise AND operation (using the &amp; operator). The two operands of the AND operation are the result returned by getAction() and the MotionEvent constant, <a title="ACTION_MASK description" href="http://developer.android.com/reference/android/view/MotionEvent.html#ACTION_MASK" target="_blank">ACTION_MASK</a>. The ACTION_MASK constant is a bit mask of the part of the action code that represents the action itself. The following line of code performs a bitwise AND operation on the action returned by getAction() and the ACTION_MASK constant.</p>
<pre>int actionCode = action &amp; MotionEvent.ACTION_MASK;</pre>
<p>You can get the number of pointers (fingers) associated with the event by calling the getPointerCount() method of the MotionEvent object, as shown below.</p>
<pre>int pCount = event.getPointerCount();</pre>
<p>You can also retrieve the X and Y coordinates associated with the touch event by calling the <a title="getX() method description" href="http://developer.android.com/reference/android/view/MotionEvent.html#getX()" target="_blank">getX()</a> and <a title="getY() method description" href="http://developer.android.com/reference/android/view/MotionEvent.html#getY()" target="_blank">getY()</a> methods of the MotionEvent object, respectively. Here is an example that retrieves first the X coordinate and then the Y coordinate.</p>
<pre>float xcoord = event.getX;
float ycoord = event.getY;</pre>
<p>By identifying a pointer index as a parameter to getX() and getY(), you can retrieve the X and Y coordinates of a specific pointer. The following example shows how to get the X coordinate and Y coordinate of the first pointer.</p>
<pre>Float  xcoord = event.getX(0);   //Get the X coordinate for the first
                                 // pointer
Float  ycoord = event.getY(1);   //Get the Y coordinate for the second
                                 // pointer</pre>
<p><strong>More information</strong></p>
<ul>
<li><a title="Pinch to zoom for Xperia X10 tutorial - Part 2" href="http://blogs.sonyericsson.com/developerworld/2011/04/12/how-to-take-advantage-of-the-pinch-to-zoom-feature-in-your-xperia%E2%84%A2-10-apps-part-2/" target="_blank">How to take advantage of the pinch-to-zoom feature in your Xperia™ X10 apps – Part 2&gt;&gt;&gt;</a></li>
<li><a title="Sony Ericsson Android dvelopment resource pages" href="http://developer.sonyericsson.com/wportal/devworld/technology/android/get-started?cc=gb&amp;lc=en" target="_blank">Learn more about Android development on Developer World&gt;&gt;&gt;</a></li>
<li><a title="Xperia X10 description" href="http://www.sonyericsson.com/cws/products/mobilephones/overview/xperiax10?cc=gb&amp;lc=en" target="_blank">See Xperia™ 10 on the consumer website&gt;&gt;&gt;</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://developer.sonymobile.com/wp/2011/04/12/how-to-take-advantage-of-the-pinch-to-zoom-feature-in-your-xperia%e2%84%a2-10-apps-part-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Tutorial: Enabling HD video recording on the Xperia™ X10</title>
		<link>http://developer.sonymobile.com/wp/2010/11/17/enabling-hd-video-recording-on-the-xperia%e2%84%a2-x10/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=enabling-hd-video-recording-on-the-xperia%25e2%2584%25a2-x10</link>
		<comments>http://developer.sonymobile.com/wp/2010/11/17/enabling-hd-video-recording-on-the-xperia%e2%84%a2-x10/#comments</comments>
		<pubDate>Wed, 17 Nov 2010 09:15:19 +0000</pubDate>
		<dc:creator>Joe Padre</dc:creator>
				<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[android update 2.1]]></category>
		<category><![CDATA[hd video recording]]></category>
		<category><![CDATA[sony ericsson]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[x10]]></category>
		<category><![CDATA[Xperia]]></category>

		<guid isPermaLink="false">http://blogs.sonyericsson.com/developerworld/?p=1134</guid>
		<description><![CDATA[As the roll-out of the Android 2.1 update continues for Xperia™ X10, X10 mini and X10 mini pro users, one of the key features introduced with the update is HD video recording in 720p. Johan Abramsson, Developer Support Engineer at Sony Ericsson, has written a quick tutorial on how you can make use of HD [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://blogs.sonyericsson.com/developerworld/files/2010/11/HDVideoRecording1.jpg"><img class="alignleft size-full wp-image-1136" src="http://blogs.sonyericsson.com/developerworld/files/2010/11/HDVideoRecording1.jpg" alt="" width="143" height="97" /></a>As the roll-out of the Android 2.1 update continues for Xperia™ X10, X10 mini and X10 mini pro users, one of the key features introduced with the update is HD video recording in 720p. Johan Abramsson, Developer Support Engineer at Sony Ericsson, has written a quick tutorial on how you can make use of HD video recording from third party applications.</p>
<p>Read Johan’s tutorial <a href="http://developer.sonyericsson.com/cws/devworld/article/enablehdvideorecordingforthexperiax10?cc=gb&amp;lc=en" target="_blank">here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://developer.sonymobile.com/wp/2010/11/17/enabling-hd-video-recording-on-the-xperia%e2%84%a2-x10/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>New version of Sony Ericsson’s WebSDK Packager tool available</title>
		<link>http://developer.sonymobile.com/wp/2010/08/24/new-version-of-sony-ericsson%e2%80%99s-websdk-packager-tool-available/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=new-version-of-sony-ericsson%25e2%2580%2599s-websdk-packager-tool-available</link>
		<comments>http://developer.sonymobile.com/wp/2010/08/24/new-version-of-sony-ericsson%e2%80%99s-websdk-packager-tool-available/#comments</comments>
		<pubDate>Tue, 24 Aug 2010 17:22:13 +0000</pubDate>
		<dc:creator>Joe Padre</dc:creator>
				<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[sdk]]></category>
		<category><![CDATA[WebSDK Packager]]></category>
		<category><![CDATA[x10]]></category>

		<guid isPermaLink="false">http://blogs.sonyericsson.com/developerworld/?p=757</guid>
		<description><![CDATA[We are excited to introduce a new release of the WebSDK Packager tool, which now provides support for the Mac OS® X platform. WebSDK Packager version 1.2.1 is an all-in-one tool for building, simulating and packaging an application’s web components into a single native shell. The updated WebSDK Packager tool can be used across Windows [...]]]></description>
			<content:encoded><![CDATA[<p>We are excited to introduce a new release of the WebSDK Packager tool, which now provides support for the Mac OS® X platform. WebSDK Packager version 1.2.1 is an all-in-one tool for building, simulating and packaging an application’s web components into a single native shell. The updated WebSDK Packager tool can be used across Windows or Mac OS platforms for development and deployment of web applications on Android™ and Symbian™ devices. WebSDK Packager also provides developers with exciting sample applications that can run on all Sony Ericsson Android and Symbian phones, including the Xperia™ X10 and Xperia X10 mini pro.</p>
<p>Based on <a href="http://www.phonegap.com/" target="_blank">PhoneGap</a>’s open source framework, WebSDK Packager is a great tool for someone who has great expertise with HTML CSS and JavaScript and is adept at creating web applications, but is not familiar with the complexities of the mobile environment. WebSDK Packager allows Android™ developers to create mobile web applications by packaging their web components inside a native shell for Android. For Symbian developers, WebSDK Packager uses the PhoneGap API with web runtime (WRT) widgets. This truly enables developers to write code once and deploy on multiple platforms. With support of this tool on Windows and Mac OS platforms, developers can use the tool on their preferred platform.</p>
<p>For those not familiar with PhoneGap, it is an open source development framework for building cross-platform mobile apps that run on different mobile platforms including Android, Symbian, iPhone®, iPad®, Palm® and BlackBerry®. The PhoneGap open source code has been downloaded more than 250,000 times and there are thousands of PhoneGap-based apps in various platform stores. We also welcome the <a href="http://blog.symbian.org/2010/07/19/phone-gap/" target="_blank">recent announcement by Symbian</a> to integrate PhoneGap library in their web extensions package.</p>
<p>Sony Ericsson supports cross-platform web development and sees this as an important enabler for developers to create exciting new services across web and mobile. Sony Ericsson announced support for PhoneGap framework in 2009 and our WebSDK Packager as part of a broader approach to simplifying the application and services development on mobile devices for developers.</p>
<p>Additionally, we will begin referring to Sony Ericsson’s WebSDK as a “Packager”, which aligns with the true functionality of the product – a complete tool for developers to build, simulate and package their application’s web components into a single native shell.</p>
]]></content:encoded>
			<wfw:commentRss>http://developer.sonymobile.com/wp/2010/08/24/new-version-of-sony-ericsson%e2%80%99s-websdk-packager-tool-available/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>The Xperia™ X10 now available in the U.S. market</title>
		<link>http://developer.sonymobile.com/wp/2010/08/16/the-xperia%e2%84%a2-x10-now-available-in-the-u-s-market/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=the-xperia%25e2%2584%25a2-x10-now-available-in-the-u-s-market</link>
		<comments>http://developer.sonymobile.com/wp/2010/08/16/the-xperia%e2%84%a2-x10-now-available-in-the-u-s-market/#comments</comments>
		<pubDate>Mon, 16 Aug 2010 07:53:29 +0000</pubDate>
		<dc:creator>Tobias Nilsson</dc:creator>
				<category><![CDATA[News]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[at&t]]></category>
		<category><![CDATA[sdk]]></category>
		<category><![CDATA[sony ericsson]]></category>
		<category><![CDATA[x10]]></category>

		<guid isPermaLink="false">http://blogs.sonyericsson.com/developerworld/?p=695</guid>
		<description><![CDATA[The greatly anticipated Sony Ericsson Xperia™ X10 is now available to U.S. customers, as you might have seen in the announcement from AT&#38;T and Sony Ericsson.  This surely is welcome news if you&#8217;re in the U.S. and keen to get your hands on the Xperia™ X10. What&#8217;s especially interesting with AT&#38;T’s offer is the price – [...]]]></description>
			<content:encoded><![CDATA[<p>The greatly anticipated Sony Ericsson Xperia™ X10 is now available to U.S. customers, as you might have seen in <a title="AT&amp;T and Sony Ericsson announce the Xperia™ X10" href="http://www.att.com/gen/press-room?pid=18212&amp;cdvn=news&amp;newsarticleid=31014" target="_blank">the announcement from AT&amp;T and Sony Ericsson</a>.  This surely is welcome news if you&#8217;re in the U.S. and keen to get your hands on the Xperia™ X10. What&#8217;s especially interesting with AT&amp;T’s offer is the price – $149.99 on a two contract and minimum data plan. Sounds like a fair deal, right?</p>
<p>For developers, Sony Ericsson has put together an add-on for the Android™ SDK, including the splash screen, backgrounds, an Xperia™ X10 skin and some other stuff. Find out more on <a title="Sony Ericsson Developer World: AT&amp;T and Sony Ericsson announce the Xperia™ X10" href="http://developer.sonyericsson.com/wportal/devworld/article/dwx10attreleasenewspiece?cc=gb&amp;lc=en" target="_self">the Developer World web site</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://developer.sonymobile.com/wp/2010/08/16/the-xperia%e2%84%a2-x10-now-available-in-the-u-s-market/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Android one finger zoom tutorial – Part 4</title>
		<link>http://developer.sonymobile.com/wp/2010/07/07/android-one-finger-zoom-tutorial-part-4/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=android-one-finger-zoom-tutorial-part-4</link>
		<comments>http://developer.sonymobile.com/wp/2010/07/07/android-one-finger-zoom-tutorial-part-4/#comments</comments>
		<pubDate>Wed, 07 Jul 2010 13:10:19 +0000</pubDate>
		<dc:creator>andreas agvard</dc:creator>
				<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[x10]]></category>
		<category><![CDATA[x10 mini]]></category>
		<category><![CDATA[Zoom]]></category>

		<guid isPermaLink="false">http://blogs.sonyericsson.com/developerworld/?p=659</guid>
		<description><![CDATA[Welcome to the fourth and final part of the Android tutorial on how to make your own zoom control like the one used in Sony Ericsson X10 Mini in the Camera and Album applications. Click here to go to the prevoius part of this tutorial. As usual the source code is included, see below. Don&#8217;t [...]]]></description>
			<content:encoded><![CDATA[<p>Welcome to the fourth and final part of the Android tutorial on how to make your own zoom control like the one used in Sony Ericsson X10 Mini in the Camera and Album applications. Click <a href="http://blogs.sonyericsson.com/developerworld/2010/06/09/android-one-finger-zoom-tutorial-part-3/" target="_blank">here </a>to go to the prevoius part of this tutorial. As usual the source code is included, see below. Don&#8217;t forgett to download &#8216;Sony Ericsson Tutorials&#8217; from Android market to see demos of this and other tutorials in action.</p>
<p><a href="http://blogs.sonyericsson.com/developerworld/files/2010/07/TutorialZoom4.zip">[Download] One Finger Zoom sample project – Part 4 (220kb)</a></p>
<p>In this part we&#8217;ll focus on introducing dynamic behavior to our zoom such as fling and bounce by animating the zoom state. Dynamic behavior adds a lot in terms of looks, feedback and usability.</p>
<h3>Dynamics</h3>
<p>To implement dynamic behavior we&#8217;re going to subclass the Dynamics class introduced in <a href="http://blogs.sonyericsson.com/developerworld/2010/06/23/android-tutorial-making-your-own-3d-list-part-3/">the final part of the list tutorial</a>. Make sure to read through that tutorial if you want to know more about the Dynamics base class.</p>
<p>The Dynamics class is useful for applying dynamic behavior to a value, the class itself holds a position and a velocity and functionality for setting min and max positions. When subclassing Dynamics we must implement the onUpdate(int) method that is responsible for updating the state. This gives us control over the dynamic behavior and in our Dynamics sub-class we&#8217;ll implement basic friction and spring physics to handle fling and edge bounce. If you want to know more about spring physics then <a href="http://www.myphysicslab.com/">this</a> is a nice place to start.</p>
<p><object width="425" height="350"><param name="movie" value="ERj67oRHeeE"></param><param name="wmode" value="transparent" ></param><embed src="http://www.youtube.com/v/ERj67oRHeeE" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object></p>
<p><span id="more-659"></span></p>
<p>Let start by defining the necessary attributes for our dynamic behavior. The friction factor will be used to decelerate the fling animation when we are inside the pan limits, and the stiffness and damping factor will be used to simulate a spring pulling the zoom window back to the content if we are outside of the pan limits. To simplify setting up the physics we let the user specify a damping ratio which we then internally recalculate to the damping factor we use in our calculations. Damping ratio is easy to use, basically a value of less than 1 makes the animation overshoot while a value of 1 or more doesn&#8217;t. <a href="http://en.wikipedia.org/wiki/Damping">Here you can learn more about damping.</a><br />
[java]<br />
public class SpringDynamics extends Dynamics {</p>
<p>    private float mFriction;<br />
    private float mStiffness;<br />
    private float mDamping;</p>
<p>    public void setFriction(float friction) {<br />
        mFriction = friction;<br />
    }</p>
<p>    public void setSpring(float stiffness, float dampingRatio) {<br />
        mStiffness = stiffness;<br />
        mDamping = dampingRatio * 2 * (float)Math.sqrt(stiffness);<br />
    }<br />
[/java]<br />
Next we&#8217;ll implement the onUpdate() method which is responsible for updating the position and velocity of the Dynamics object. The input parameter, dt, is the number of milliseconds passed since the method was previously called. (By the way: dt stands for delta time. Delta is commonly used in mathematics and denotes change and thus: dt = change in time.)</p>
<p>To implement friction and spring physics we need to numerically solve the <a href="http://en.wikipedia.org/wiki/Classical_mechanics">equations of motion</a>. To do this we&#8217;ll use Euler integration. Although it has low accuracy and is prone to instability it works fine in a simple case like this. <a href="http://en.wikipedia.org/wiki/Numerical_ordinary_differential_equations">Here you can read more about numerical integration.</a><br />
[java]<br />
    @Override<br />
    protected void onUpdate(int dt) {<br />
        final float fdt = dt / 1000f;<br />
        final float a = calculateAcceleration();<br />
        mPosition += mVelocity * fdt + .5f * a * fdt * fdt;<br />
        mVelocity += a * fdt;<br />
    }[/java]<br />
Our algorithm requires us to be able to calculate the acceleration at the current position which we&#8217;ll do by using spring physics if we&#8217;re outside of the pan limits. If we are inside of the pan limits we&#8217;ll calculate the acceleration caused by friction as the product of the friction coefficient and the current velocity.<br />
[java]<br />
    private float calculateAcceleration() {<br />
        float acceleration;</p>
<p>        final float distanceFromSnap = getDistanceFromSnap();<br />
        if (distanceFromSnap != 0) {<br />
            acceleration = distanceFromSnap * mStiffness &#8211; mDamping * mVelocity;<br />
        } else {<br />
            acceleration = -mFriction * mVelocity;<br />
        }</p>
<p>        return acceleration;<br />
    }<br />
[/java]</p>
<h3>A dynamic zoom control</h3>
<p>Once we have the SpringDynamics implementation down we can improve our zoom control. As you might remember from the previous tutorials the zoom control is responsible for imposing limits and other similar logic. Let&#8217;s introduce our new SpringDynamics class to the zoom control by creating one of them for each dimension and setting fitting friction and spring coefficients.<br />
[java]<br />
    private final SpringDynamics mPanDynamicsX = new SpringDynamics();<br />
    private final SpringDynamics mPanDynamicsY = new SpringDynamics();</p>
<p>    public DynamicZoomControl() {<br />
        mPanDynamicsX.setFriction(2f);<br />
        mPanDynamicsY.setFriction(2f);<br />
        mPanDynamicsX.setSpring(50f, 1f);<br />
        mPanDynamicsY.setSpring(50f, 1f);<br />
    }<br />
[/java]<br />
Next, as we no longer will have hard limits on the pan but instead a spring pulling behavior when we are outside the pan limits, we need to modify the pan method:<br />
[java]<br />
    private static final float PAN_OUTSIDE_SNAP_FACTOR = .4f;<br />
    private float mPanMinX;<br />
    private float mPanMaxX;<br />
    private float mPanMinY;<br />
    private float mPanMaxY;</p>
<p>    public void pan(float dx, float dy) {<br />
        final float aspectQuotient = mAspectQuotient.get();</p>
<p>        dx /= mState.getZoomX(aspectQuotient);<br />
        dy /= mState.getZoomY(aspectQuotient);</p>
<p>        if (mState.getPanX() &gt; mPanMaxX &amp;&amp; dx &gt; 0 || mState.getPanX() &lt; mPanMinX &amp;&amp; dx &lt; 0) {<br />
            dx *= PAN_OUTSIDE_SNAP_FACTOR;<br />
        }<br />
        if (mState.getPanY() &gt; mPanMaxY &amp;&amp; dy &gt; 0 || mState.getPanY() &lt; mPanMinY &amp;&amp; dy &lt; 0) {<br />
            dy *= PAN_OUTSIDE_SNAP_FACTOR;<br />
        }</p>
<p>        final float newPanX = mState.getPanX() + dx;<br />
        final float newPanY = mState.getPanY() + dy;</p>
<p>        mState.setPanX(newPanX);<br />
        mState.setPanY(newPanY);</p>
<p>        mState.notifyObservers();<br />
    }<br />
[/java]<br />
We will no longer limit the pan by clamping it inside bounds when the user is panning around. Instead we&#8217;ll impose a penalty factor when panning outside the bounds giving the user clear visible feedback and a sense of struggling. This also means we&#8217;ll no longer need the limitPan() method we used in the previous tutorials. However, we still need to calculate the pan limits so we can do the pull back animation. We&#8217;ll replace the limitPan() method with a new method, updatePanLimits(), that calculates the pan limits depending on the zoom level, and then call that method every time the zoom level changes:<br />
[java]<br />
    private void updatePanLimits() {<br />
        final float aspectQuotient = mAspectQuotient.get();</p>
<p>        final float zoomX = mState.getZoomX(aspectQuotient);<br />
        final float zoomY = mState.getZoomY(aspectQuotient);</p>
<p>        mPanMinX = .5f &#8211; getMaxPanDelta(zoomX);<br />
        mPanMaxX = .5f + getMaxPanDelta(zoomX);<br />
        mPanMinY = .5f &#8211; getMaxPanDelta(zoomY);<br />
        mPanMaxY = .5f + getMaxPanDelta(zoomY);<br />
    }<br />
[/java]<br />
Next we&#8217;ll add functionality for starting a fling which will be called when the user lifts his or her finger from the screen after panning.<br />
[java]<br />
    private final Handler mHandler = new Handler();</p>
<p>    public void startFling(float vx, float vy) {<br />
        final float aspectQuotient = mAspectQuotient.get();<br />
        final long now = SystemClock.uptimeMillis();</p>
<p>        mPanDynamicsX.setState(mState.getPanX(), vx / mState.getZoomX(aspectQuotient), now);<br />
        mPanDynamicsY.setState(mState.getPanY(), vy / mState.getZoomY(aspectQuotient), now);</p>
<p>        mPanDynamicsX.setMinPosition(mPanMinX);<br />
        mPanDynamicsX.setMaxPosition(mPanMaxX);<br />
        mPanDynamicsY.setMinPosition(mPanMinY);<br />
        mPanDynamicsY.setMaxPosition(mPanMaxY);</p>
<p>        mHandler.post(mUpdateRunnable);<br />
    }<br />
[/java]<br />
In the startFling() method we prepare the dynamics objects for taking over control of the pan, setting to it the current pan and velocity values as well as the current time. We also set the min and max values of the dynamics object to the current pan limits. Finally we post a runnable that is responsible for updating and animating the zoom state.<br />
[java]<br />
    private static final float REST_VELOCITY_TOLERANCE = 0.004f;<br />
    private static final float REST_POSITION_TOLERANCE = 0.01f;<br />
    private static final int FPS = 50;</p>
<p>    private final Runnable mUpdateRunnable = new Runnable() {<br />
        public void run() {<br />
            final long startTime = SystemClock.uptimeMillis();<br />
            mPanDynamicsX.update(startTime);<br />
            mPanDynamicsY.update(startTime);<br />
            final boolean isAtRest = mPanDynamicsX.isAtRest(REST_VELOCITY_TOLERANCE, REST_POSITION_TOLERANCE) &amp;&amp; mPanDynamicsY.isAtRest(REST_VELOCITY_TOLERANCE, REST_POSITION_TOLERANCE);<br />
            mState.setPanX(mPanDynamicsX.getPosition());<br />
            mState.setPanY(mPanDynamicsY.getPosition());</p>
<p>            if (!isAtRest) {<br />
                final long stopTime = SystemClock.uptimeMillis();<br />
                mHandler.postDelayed(mUpdateRunnable, 1000 / FPS &#8211; (stopTime &#8211; startTime));<br />
            }</p>
<p>            mState.notifyObservers();<br />
        }<br />
    };<br />
[/java]<br />
In the runnable that is posted for animating the pan values we update the dynamics objects and then we set the updated pan values to the ZoomState. Then we check if the animation should go on or by checking if the the dynamics objects are both at rest. If the dynamics objects are not at rest we post this runnable again using a delay calculated to try to meet a chosen FPS. Finally we notify observers of the ZoomState object that it&#8217;s been changed, ultimately causing the zoom view to invalidate.</p>
<p>Lastly our dynamic zoom control will need a method for stopping the fling that will be used when the user puts his or her finger down on the screen again after making a fling. Since our fling animation uses a runnable posted on a handler we simply remove the runnable from the handler, thus no longer triggering the automatic zoom state updates and effectively stopping the animation.<br />
[java]<br />
    public void stopFling() {<br />
        mHandler.removeCallbacks(mUpdateRunnable);<br />
    }<br />
[/java]</p>
<h3>Utilizing our new tools</h3>
<p>Alright, so we have implemented dynamic behavior to our zoom control, now lets start using the new methods we created so we can start flinging and bouncing around! To do this we will build on our OnTouchListener implementation and make sure the onTouch method calls our new methods in all the correct places.</p>
<p>The startFling method we declared above takes two velocity parameters, one for each dimension, and luckily the Android platform has just the right tools for calculating the velocity of a fling motion. The onTouch method is will use VelocityTracker for tracking the velocity of motion events by simply adding the events to the tracker object:<br />
[java]<br />
    private VelocityTracker mVelocityTracker;</p>
<p>    public boolean onTouch(View v, MotionEvent event) {<br />
        final int action = event.getAction();<br />
        final float x = event.getX();<br />
        final float y = event.getY();</p>
<p>        if (mVelocityTracker == null) {<br />
            mVelocityTracker = VelocityTracker.obtain();<br />
        }<br />
        mVelocityTracker.addMovement(event);<br />
[/java]<br />
The handling of down events will be largely unchanged with the exception of the call to the stopFling() method on the zoom control, stopping any current animation when the user puts his or her finger back on the screen:<br />
[java]<br />
        switch (action) {<br />
            case MotionEvent.ACTION_DOWN:<br />
                mZoomControl.stopFling();<br />
                v.postDelayed(mLongPressRunnable, mLongPressTimeout);<br />
                mDownX = x;<br />
                mDownY = y;<br />
                mX = x;<br />
                mY = y;<br />
                break;<br />
[/java]<br />
The handling of move events is unchanged since the previous part of the tutorial so lets skip ahead to the handling of up events. Here we will use the velocity tracker for calculating the velocity of the fling event if we were panning around. If we were not panning but instead perhaps zooming or simply in an undefined state, for example if the user has done a quick tap on the screen, we&#8217;ll start a fling without velocity. Even if we don&#8217;t give the fling any initial velocity it is important that we start it since the pan values might be outside of the limits which makes the springs physics accelerate the pan values, pulling them back within limits.<br />
[java]<br />
            case MotionEvent.ACTION_UP:<br />
                if (mMode == Mode.PAN) {<br />
                    mVelocityTracker.computeCurrentVelocity(1000, mScaledMaximumFlingVelocity);<br />
                    mZoomControl.startFling(-mVelocityTracker.getXVelocity() / v.getWidth(), -mVelocityTracker.getYVelocity() / v.getHeight());<br />
                } else {<br />
                    mZoomControl.startFling(0, 0);<br />
                }<br />
                mVelocityTracker.recycle();<br />
                mVelocityTracker = null;<br />
                v.removeCallbacks(mLongPressRunnable);<br />
                mMode = Mode.UNDEFINED;<br />
                break;</p>
<p>            default:<br />
                mVelocityTracker.recycle();<br />
                mVelocityTracker = null;<br />
                v.removeCallbacks(mLongPressRunnable);<br />
                mMode = Mode.UNDEFINED;<br />
                break;</p>
<p>        }</p>
<p>        return true;<br />
    }<br />
[/java]<br />
Note that the fling uses the negative of the calculated velocities similar to how the negative of the dx and dy values are used in the part of the switch statement that handles move events.</p>
<p>And with that, we&#8217;re done! We now have a dynamic zoom with a nicely separated software architecture, and while this is where this tutorial series will end I hope it can be the start of experiments and projects for some of you. In this tutorial series we&#8217;ve limited ourselves to zooming in images, but the code can be used to zoom in any application. To do this you need to write your own View subclass and implement the drawing in this view to honor the values you get from ZoomState.</p>
<p>Please feel free to share your zoom experiences, and don&#8217;t hesitate to ask any questions.</p>
<p>Good luck!</p>
]]></content:encoded>
			<wfw:commentRss>http://developer.sonymobile.com/wp/2010/07/07/android-one-finger-zoom-tutorial-part-4/feed/</wfw:commentRss>
		<slash:comments>25</slash:comments>
		</item>
		<item>
		<title>Xperia™ news!</title>
		<link>http://developer.sonymobile.com/wp/2010/06/17/xperia%e2%84%a2-news/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=xperia%25e2%2584%25a2-news</link>
		<comments>http://developer.sonymobile.com/wp/2010/06/17/xperia%e2%84%a2-news/#comments</comments>
		<pubDate>Thu, 17 Jun 2010 11:34:25 +0000</pubDate>
		<dc:creator>Tobias Nilsson</dc:creator>
				<category><![CDATA[News]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[mobile]]></category>
		<category><![CDATA[sony ericsson]]></category>
		<category><![CDATA[x10]]></category>
		<category><![CDATA[x10 mini]]></category>
		<category><![CDATA[Xperia]]></category>

		<guid isPermaLink="false">http://blogs.sonyericsson.com/developerworld/?p=635</guid>
		<description><![CDATA[As many of you probably saw yesterday Sony Ericsson announced a new product in the Xperia™ product line called X8. X8 brings together features from both X10 and X10 mini and will retail for under €200 in most markets. The X8 comes with a HVGA screen and a 3.2 mega pixel camera. Below is a [...]]]></description>
			<content:encoded><![CDATA[<p>As many of you probably saw yesterday Sony Ericsson announced a new product in the Xperia™ product line called X8. X8 brings together features from both X10 and X10 mini and will retail for under €200 in most markets. The X8 comes with a HVGA screen and a 3.2 mega pixel camera. Below is a short video showing what the X8 looks like.</p>
<p>More info on X8 and the Q3 update to Android 2.1 for X10 and X10 mini/pro can be found on our sister blog, the <a href="http://blogs.sonyericsson.com/products/2010/06/16/x8-announcement-and-xperia-updates/" target="_blank">Sony Ericsson Product Blog</a></p>
<p><object width="425" height="350"><param name="movie" value="Tkklt6kUCyE"></param><param name="wmode" value="transparent" ></param><embed src="http://www.youtube.com/v/Tkklt6kUCyE" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object></p>
]]></content:encoded>
			<wfw:commentRss>http://developer.sonymobile.com/wp/2010/06/17/xperia%e2%84%a2-news/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Android one finger zoom tutorial – Part 3</title>
		<link>http://developer.sonymobile.com/wp/2010/06/09/android-one-finger-zoom-tutorial-part-3/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=android-one-finger-zoom-tutorial-part-3</link>
		<comments>http://developer.sonymobile.com/wp/2010/06/09/android-one-finger-zoom-tutorial-part-3/#comments</comments>
		<pubDate>Wed, 09 Jun 2010 15:24:11 +0000</pubDate>
		<dc:creator>andreas agvard</dc:creator>
				<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[x10]]></category>
		<category><![CDATA[x10 mini]]></category>
		<category><![CDATA[Zoom]]></category>

		<guid isPermaLink="false">http://blogs.sonyericsson.com/developerworld/?p=552</guid>
		<description><![CDATA[Welcome to the third part of the Android tutorial on how to make your own zoom control like the one used in Sony Ericsson X10 Mini in the Camera and Album applications. Click here to read the second part of the tutorial. Don&#8217;t forget to go to Android Market and download Sony Ericsson Tutorials, the [...]]]></description>
			<content:encoded><![CDATA[<p>Welcome to the third part of the Android tutorial on how to make your own zoom control like the one used in Sony Ericsson X10 Mini in the Camera and Album applications. Click <a href="http://blogs.sonyericsson.com/developerworld/2010/05/26/android-one-finger-zoom-tutorial-part-2" target="_blank">here</a> to read the second part of the tutorial.</p>
<p>Don&#8217;t forget to go to Android Market and download <strong>Sony Ericsson Tutorials</strong>, the app that collects all sample apps in this and other Sony Ericsson tutorials. Get the QR-code for the app <a href="http://blogs.sonyericsson.com/developerworld/2010/05/27/qr-code-for-sony-ericsson-tutorials-app-on-android-market/" target="_blank">here</a>. Below is a link to the source code of part 3, prepared for you to set up your own project in e.g. Eclipse.</p>
<p><a href="http://blogs.sonyericsson.com/developerworld/files/2010/06/TutorialZoom33.zip">[Download] One Finger Zoom sample project &#8211; Part 3 (215kb)</a></p>
<p>This tutorial part will focus on introducing a new way of interacting with the zoom, a new input paradigm as our designers would say. In the previous tutorial we laid the ground for exactly this when we created a new class for controlling the zoom state. Separating the state control from the input control handled by an OnTouchListener implementation.</p>
<p><object width="425" height="350"><param name="movie" value="ERj67oRHeeE"></param><param name="wmode" value="transparent" ></param><embed src="http://www.youtube.com/v/ERj67oRHeeE" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object></p>
<p><span id="more-552"></span></p>
<p>Besides being nice for easier code overview this separation is great for us in at least two other ways:</p>
<p>First it it is very useful for trying out different way of controlling the zoom, something I had great use for in the X10 mini project. Here at Sony Ericsson we have the privilege of working with several great and driven designers wanting to test out every idea for controlling the zoom you can think of. Being able to easily switch between variants meant we could evaluate several good designs before settling.</p>
<p>Secondly it means we can easily have different ways of controlling the zoom on different devices. Multiple screen support is something you should always think of when designing for android. For small devices like the X10 mini one handed touch is very useful, while larger screens will be operated with two hands most of the time, one hand holding the phone and another free to touch the screen.</p>
<h3>Implementing a new paradigm</h3>
<p>As you might remember, the zoom control in the previous tutorial parts had options menu alternatives for switching between pan and zoom mode. Having menu alternatives such as these are usually not a good idea as in many Android applications the options menu is already overflowing. Also you don&#8217;t want navigation more than one click away.</p>
<p>Our new input paradigm will be similar to the one on the X10 mini. We&#8217;ll use long press to enter zoom mode and vertical touch dragging for zooming in and out. Pan will be left unchanged, activated simply by dragging.</p>
<p>We&#8217;ll start the implementation of a new listener by modifying the BasicZoomListener class we&#8217;ve built on so far, removing all references to the ControlType enum and renaming the class to LongPressZoomListener. Then we create a new constructor for the listener.</p>
<p>[java]<br />
    private final int mScaledTouchSlop;<br />
    private final int mLongPressTimeout;</p>
<p>    public LongPressZoomListener(Context context) {<br />
        mLongPressTimeout = ViewConfiguration.getLongPressTimeout();<br />
        mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();<br />
    }<br />
[/java]</p>
<p>To implement long press we need to define the long press timeout, that is the duration before a press turns into a long press. We also need to define the touch slop, that is the amount touch can wander before we decide it&#8217;s a drag event rather than a long press. As luck might have it the system defines good default values we can use for this. The touch slop value is even screen size dependent.</p>
<p>To implement the long touch logic we first need to define a flag for holding the classification of the current touch event. The classification mode have three states, undefined means the touch hasn&#8217;t been classified yet and can become either pan or zoom. Pan means the user has started dragging and has entered pan mode while zoom means the user has made a long press and entered zoom mode where vertical touch movement zooms in or out.</p>
<p>[java]<br />
    private enum Mode {<br />
        UNDEFINED, PAN, ZOOM<br />
    }</p>
<p>    private Mode mMode = Mode.UNDEFINED;<br />
[/java]</p>
<p>Next up we will re-implement the onTouch callback method to classify long press and drag and interpret these as either zoom or pan.</p>
<p>[java]<br />
    private final Runnable mLongPressRunnable = new Runnable() {<br />
        public void run() {<br />
            mMode = Mode.ZOOM;<br />
        }<br />
    };</p>
<p>    public boolean onTouch(View v, MotionEvent event) {<br />
        final int action = event.getAction();<br />
        final float x = event.getX();<br />
        final float y = event.getY();</p>
<p>        switch (action) {<br />
            case MotionEvent.ACTION_DOWN:<br />
                v.postDelayed(mLongPressRunnable, mLongPressTimeout);<br />
                mDownX = x;<br />
                mDownY = y;<br />
                mX = x;<br />
                mY = y;<br />
                break;<br />
[/java]</p>
<p>Starting with the action down event, this is largely unchanged from our previous tutorials with the exception of posting a runnable on the view handler. This runnable, called mLongPressRunnable, is posted with a delay meaning it will run once the delay time has passed, in this case when the long press duration has passed. The runnable simply sets the mode to zoom mode, meaning once the long press duration has passed we&#8217;ll enter zoom mode.</p>
<p>[java]<br />
            case MotionEvent.ACTION_MOVE: {<br />
                final float dx = (x &#8211; mX) / v.getWidth();<br />
                final float dy = (y &#8211; mY) / v.getHeight();</p>
<p>                if (mMode == Mode.ZOOM) {<br />
                    mZoomControl.zoom(<br />
                        (float)Math.pow(20, -dy),<br />
                        mDownX / v.getWidth(),<br />
                        mDownY / v.getHeight());<br />
                } else if (mMode == Mode.PAN) {<br />
                    mZoomControl.pan(-dx, -dy);<br />
                } else {<br />
                    final float scrollX = mDownX &#8211; x;<br />
                    final float scrollY = mDownY &#8211; y;</p>
<p>                    final float dist = (float)<br />
                        Math.sqrt(scrollX * scrollX + scrollY * scrollY);</p>
<p>                    if (dist &gt;= mScaledTouchSlop) {<br />
                        v.removeCallbacks(mLongPressRunnable);<br />
                        mMode = Mode.PAN;<br />
                    }<br />
                }</p>
<p>                mX = x;<br />
                mY = y;<br />
                break;<br />
            }<br />
[/java]</p>
<p>The action move event handling code is also similar to what we had in the previous tutorials. What&#8217;s new is the else block where we check if the the touch has wandered far enough to be classified as a drag. This is done by calculating the distance from the touch down location using the Pythagorean theorem and comparing it to the touch slop we got for the view configuration. If the touch has wandered far enough we set the pan mode flag and we remove the mLongPressRunnable from the view callback queue, making sure we don&#8217;t enter zoom mode after the long press timeout duration has passed.</p>
<p>[java]<br />
            default:<br />
                v.removeCallbacks(mLongPressRunnable);<br />
                mMode = Mode.UNDEFINED;<br />
                break;</p>
<p>        }</p>
<p>        return true;<br />
    }<br />
[/java]</p>
<p>Finally we add support for handling the remaining touch events, such as action up and action cancel. In these cases we remove the long press callback and reset the mode flag, putting the component back into default state.</p>
<h3>Tactile feedback</h3>
<p>Now we have implemented a nicer input paradigm, making it easy and swift to zoom and pan. It is however it&#8217;s a bit confusing to the user that no indication is given when they enter zoom or pan mode. To fix this we&#8217;ll use tactile feedback, giving the user a quick vibration when entering zoom mode.</p>
<p>Using the vibrator on Android devices is easy. We get access to the vibrator service through the context we get in the constructor like this.</p>
<p>[java]<br />
    private final Vibrator mVibrator;</p>
<p>    public LongPressZoomListener(Context context) {<br />
        mLongPressTimeout = ViewConfiguration.getLongPressTimeout();<br />
        mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();<br />
        mVibrator = (Vibrator)context.getSystemService(&#8220;vibrator&#8221;);<br />
    }<br />
[/java]</p>
<p>Then we modify our runnable to give a quick vibration when entering zoom mode, 50 ms gives a nice and short feedback.</p>
<p>[java]<br />
    private static final long VIBRATE_TIME = 50;</p>
<p>    private final Runnable mLongPressRunnable = new Runnable() {<br />
        public void run() {<br />
            mMode = Mode.ZOOM;<br />
            mVibrator.vibrate(VIBRATE_TIME);<br />
        }<br />
    };<br />
[/java]</p>
<p>This is all the java code required for tactile feedback, however if you try to run this you&#8217;ll get an exception due to the application not having permission to use the vibrator service. This is easily remedied by adding the line below in the manifest.</p>
<p>[xml]<br />
&lt;uses-permission android:name=&#8221;android.permission.VIBRATE&#8221; /&gt;<br />
[/xml]</p>
<p>And we&#8217;re done! Our zoom is starting to feel quite nice and useful, we&#8217;ve got a nice input paradigm and a zoom that behaves reasonably. This is the perfect time for you readers to try out your own take on a input paradigm if you want to, or maybe you&#8217;ve got ideas on how to improve the one we&#8217;ve just implemented.</p>
<p>Next up we&#8217;ll implement a very nice addition to our zoom, fling and bounce. Currently a downside with the zoom is that it feels stiff and panning far requires a lot of dragging. This will all change in the next tutorial where we introduce a more dynamic behavior.</p>
<p>Until then, good luck!</p>
]]></content:encoded>
			<wfw:commentRss>http://developer.sonymobile.com/wp/2010/06/09/android-one-finger-zoom-tutorial-part-3/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Android one finger zoom tutorial &#8211; Part 2</title>
		<link>http://developer.sonymobile.com/wp/2010/05/26/android-one-finger-zoom-tutorial-part-2/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=android-one-finger-zoom-tutorial-part-2</link>
		<comments>http://developer.sonymobile.com/wp/2010/05/26/android-one-finger-zoom-tutorial-part-2/#comments</comments>
		<pubDate>Wed, 26 May 2010 12:09:58 +0000</pubDate>
		<dc:creator>andreas agvard</dc:creator>
				<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[x10]]></category>
		<category><![CDATA[x10 mini]]></category>
		<category><![CDATA[Zoom]]></category>

		<guid isPermaLink="false">http://blogs.sonyericsson.com/developerworld/?p=433</guid>
		<description><![CDATA[Welcome to the second part of the Android tutorial on how to make your own zoom control like the one used in Sony Ericsson X10 Mini in the Camera and Album applications. Click here to read the first part of the tutorial. Don&#8217;t forget to go to Android Market and download Sony Ericsson Tutorials, the [...]]]></description>
			<content:encoded><![CDATA[<p>Welcome to the second part of the Android tutorial on how to make your own zoom control like the one used in Sony Ericsson X10 Mini in the Camera and Album applications. Click <a href="http://blogs.sonyericsson.com/developerworld/2010/05/18/android-one-finger-zoom-tutorial-part-1" target="_blank">here</a> to read the first part of the tutorial.</p>
<p>Don&#8217;t forget to go to Android Market and download <strong>Sony Ericsson Tutorials</strong>, the app that collects all sample apps in this and other Sony Ericsson tutorials.</p>
<p>In this part of the tutorial we will build on the zoom application we started in part 1. As you might remember, in part 1 we finished with a zoom application that didn&#8217;t have any limits, we could zoom and pan into the void and back. In this tutorial we will introduce limits and we will also make sure that the pan always follows the finger as one would expect, as we in part 1 could see panning following the finger differently depending on the current zoom level. Below is a link to the source code for step 2 and the video showing what you will learn in the one finger zoom tutorial series.</p>
<p><a href="http://blogs.sonyericsson.com/developerworld/files/2010/05/TutorialZoom2.zip">[Download] One Finger Zoom sample project &#8211; Part 2 (218kb)</a></p>
<p><object width="425" height="350"><param name="movie" value="ERj67oRHeeE"></param><param name="wmode" value="transparent" ></param><embed src="http://www.youtube.com/v/ERj67oRHeeE" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object></p>
<h3>The aspect quotient</h3>
<p>Remember this picture from part 1?</p>
<div id="attachment_282" class="wp-caption aligncenter" style="width: 428px"><a href="http://blogs.sonyericsson.com/developerworld/files/2010/05/TutorialZoomState.png"><img class="size-full wp-image-282" src="http://blogs.sonyericsson.com/developerworld/files/2010/05/TutorialZoomState.png" alt="" width="418" height="131" /></a><p class="wp-caption-text">Images illustrating how the zoom state works, the dashed gray area represents what is shown in the view and the patterned area represents the content. On the left: Zoom is 1, pan-x and pan-y are both 0.5, in this state the image fits the screen perfectly. In the middle: Zoom is 2, pan-x and pan-y are still both 0.5, less content is now shown on the screen but will be scaled up. To the right: Zoom is 3, pan-x is 0.7 and pan-y is 0.833, we now see less of the image, only the top right corner, scaled up.</p></div>
<p><span id="more-433"></span><br />
In the rightmost image we&#8217;re at the top right pan limit, this means the maximum pan values are 0.7 and 0.833 on the x- and y-axis respectively when the zoom level is 3. The actual limits are dependent on the level of zoom in that particular dimension, and the level of zoom in a particular dimension depends on the aspect quotient (quotient between content aspect ratio and view aspect ratio, refer to part 1 for more explanation). And to make a long story short, in order to apply limits to the pan, as well as making pan follow the finger correctly, we need the aspect quotient.</p>
<p>Currently though, from part 1, our aspect quotient is only available in the ImageZoomView. So first let us change this and create an object that holds the aspect quotient and extends Observable as a means of easily notifying whom ever wishes to observe.</p>
<p>[java]<br />
public class AspectQuotient extends Observable {</p>
<p>    private float mAspectQuotient;</p>
<p>    public float get() {<br />
        return mAspectQuotient;<br />
    }</p>
<p>    public void updateAspectQuotient(float viewWidth, float viewHeight, float contentWidth,<br />
            float contentHeight) {<br />
        final float aspectQuotient = (contentWidth / contentHeight) / (viewWidth / viewHeight);</p>
<p>        if (aspectQuotient != mAspectQuotient) {<br />
            mAspectQuotient = aspectQuotient;<br />
            setChanged();<br />
        }<br />
    }<br />
}<br />
[/java]</p>
<p>Pretty straight forward, the calculations are the same as in part 1 and just as the ZoomState observable we leave it up to the client updating the aspect quotient to notify observers. The next step is to update ImageZoomView to hold a AspectQuotient object (as opposed to just a float as in part 1), and give others access to it. Of course we also need to update the code that currently modifies the aspect quotient, but I&#8217;ll leave code like this out of the tutorial as it&#8217;s straightforward and just re-factors existing functionality. Please see the code in the project linked below for reference.</p>
<h3>Enforcing limits and following fingers</h3>
<p>Alright, so now we&#8217;re able to access the aspect quotient that we need for implementing limits and follow finger through a neat and tidy Observable. The next step is to add the actual code that does these things, but where?</p>
<p>Currently the responsibility for modifying the ZoomState lies with the OnTouchListener, while we could add the functionality here I am reluctant too as I want the OnTouchListener implementation to be responsible for the touch paradigm used and not the specifics on how the ZoomState is controlled. For example, I want to easily be able to implement an OnTouchListener for multi-touch, and when I do I don&#8217;t want to update or even duplicate the functionality that applies limits, or as we&#8217;ll see in later parts, animates a state.</p>
<p>Another idea would be to add the functionality to the ZoomState, but I want the ZoomState to be simple, it&#8217;s responsibility is to provide an interface for accessing the state and getting callbacks when it changes.</p>
<p>So, the way to implement the new functionality is through a new class that sits in between the OnTouchListener implementation and the ZoomState. Let&#8217;s call this new class BasicZoomControl, as we&#8217;ll implement a first and quite basic component for controlling the ZoomState. And let&#8217;s start with creating the skeleton of this class, we know we need access to the aspect quotient and we want a callback when this changes where we enforce limits, we also know we will be controlling a zoom state so lets add that and a get method for access.</p>
<p>[java]<br />
public class BasicZoomControl implements Observer {</p>
<p>    private AspectQuotient mAspectQuotient;</p>
<p>    private final ZoomState mState = new ZoomState();</p>
<p>    public ZoomState getZoomState() {<br />
        return mState;<br />
    }</p>
<p>    public void setAspectQuotient(AspectQuotient aspectQuotient) {<br />
        if (mAspectQuotient != null) {<br />
            mAspectQuotient.deleteObserver(this);<br />
        }</p>
<p>        mAspectQuotient = aspectQuotient;<br />
        mAspectQuotient.addObserver(this);<br />
    }</p>
<p>    public void update(Observable observable, Object data) {<br />
        limitZoom();<br />
        limitPan();<br />
    }</p>
<p>}<br />
[/java]</p>
<p>Secondly we want methods for applying zoom and pan and apply the logics needed to keep within limits, have pan follow finger and also we&#8217;ll be making the content under the coordinate of the touch down event invariant during zooming, meaning we can zoom into specific parts of the content and not just the current center.</p>
<p>[java]<br />
    public void zoom(float f, float x, float y) {<br />
        final float aspectQuotient = mAspectQuotient.get();</p>
<p>        final float prevZoomX = mState.getZoomX(aspectQuotient);<br />
        final float prevZoomY = mState.getZoomY(aspectQuotient);</p>
<p>        mState.setZoom(mState.getZoom() * f);<br />
        limitZoom();</p>
<p>        final float newZoomX = mState.getZoomX(aspectQuotient);<br />
        final float newZoomY = mState.getZoomY(aspectQuotient);</p>
<p>        mState.setPanX(mState.getPanX() + (x &#8211; .5f) * (1f / prevZoomX &#8211; 1f / newZoomX));<br />
        mState.setPanY(mState.getPanY() + (y &#8211; .5f) * (1f / prevZoomY &#8211; 1f / newZoomY));</p>
<p>        limitPan();</p>
<p>        mState.notifyObservers();<br />
    }</p>
<p>    public void pan(float dx, float dy) {<br />
        final float aspectQuotient = mAspectQuotient.get();</p>
<p>        mState.setPanX(mState.getPanX() + dx / mState.getZoomX(aspectQuotient));<br />
        mState.setPanY(mState.getPanY() + dy / mState.getZoomY(aspectQuotient));</p>
<p>        limitPan();</p>
<p>        mState.notifyObservers();<br />
    }<br />
[/java]</p>
<p>The zoom method takes a zoom factor and the position of the touch down event as parameters and then updates the state of both the zoom level and the pan position. By storing the previous zoom levels it is possible to calculate new pan parameters that satisfies keeping the down coordinate invariant when zooming. The pan method is similar to how panning was done in part 1 but now the dx and dy values are divided by the level of zoom in the respective coordinate, doing this makes pan follow the finger as we now take the zoom level in consideration. Finally we need to implement the methods that limit the zoom and pan values:</p>
<p>[java]<br />
    private static final float MIN_ZOOM = 1;</p>
<p>    private static final float MAX_ZOOM = 16;</p>
<p>    private void limitZoom() {<br />
        if (mState.getZoom() &lt; MIN_ZOOM) {<br />
            mState.setZoom(MIN_ZOOM);<br />
        } else if (mState.getZoom() &gt; MAX_ZOOM) {<br />
            mState.setZoom(MAX_ZOOM);<br />
        }<br />
    }</p>
<p>    private float getMaxPanDelta(float zoom) {<br />
        return Math.max(0f, .5f * ((zoom &#8211; 1) / zoom));<br />
    }</p>
<p>    private void limitPan() {<br />
        final float aspectQuotient = mAspectQuotient.get();</p>
<p>        final float zoomX = mState.getZoomX(aspectQuotient);<br />
        final float zoomY = mState.getZoomY(aspectQuotient);</p>
<p>        final float panMinX = .5f &#8211; getMaxPanDelta(zoomX);<br />
        final float panMaxX = .5f + getMaxPanDelta(zoomX);<br />
        final float panMinY = .5f &#8211; getMaxPanDelta(zoomY);<br />
        final float panMaxY = .5f + getMaxPanDelta(zoomY);</p>
<p>        if (mState.getPanX() &lt; panMinX) {<br />
            mState.setPanX(panMinX);<br />
        }<br />
        if (mState.getPanX() &gt; panMaxX) {<br />
            mState.setPanX(panMaxX);<br />
        }<br />
        if (mState.getPanY() &lt; panMinY) {<br />
            mState.setPanY(panMinY);<br />
        }<br />
        if (mState.getPanY() &gt; panMaxY) {<br />
            mState.setPanY(panMaxY);<br />
        }<br />
    }<br />
[/java]</p>
<p>Limiting the zoom is quite straightforward, we use 1 (within screen bounds) and 16 (quite a lot of zoom, more than enough for most images on most screens) and simply clamp the value. For the pan we do the same but the problem with pan is that the limits change based on the level of zoom we&#8217;re currently at. For example, at a zoom level of 1 we don&#8217;t want the user to be able to pan at all (as the content fits the view perfectly).</p>
<h3>Stringing it all together</h3>
<p>Alright, we&#8217;re almost done with part 2 of the zoom tutorial. What&#8217;s left is adapting the Activity and the OnTouchListener implementations to the new solutions. This means changing so that the OnTouchListener implementation doesn&#8217;t manipulate the ZoomState directly but instead calls methods in BasicZoomControl, and setting it all up correctly in the Activity implementation. Please check out the project link below if you want to know the specifics of this code, or even better if you want to start playing around with the code on your own!</p>
<p>Now we have a zoom that works good, we&#8217;ve fixed the blemishes from part 1 but we&#8217;ve got some work left to make it really useful. First thing that comes in my mind is the input method, changing mode in the options menu is a poor choice. Because of this we&#8217;ll look into implementing a new OnTouchListener and a new input paradigm in the next tutorial.</p>
<p><a href="http://blogs.sonyericsson.com/developerworld/files/2010/05/TutorialZoom2.zip">[Download] One Finger Zoom sample project &#8211; Part 2 (218kb)</a></p>
<p>Good Luck!</p>
]]></content:encoded>
			<wfw:commentRss>http://developer.sonymobile.com/wp/2010/05/26/android-one-finger-zoom-tutorial-part-2/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Android one finger zoom tutorial – Part 1</title>
		<link>http://developer.sonymobile.com/wp/2010/05/18/android-one-finger-zoom-tutorial-part-1/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=android-one-finger-zoom-tutorial-part-1</link>
		<comments>http://developer.sonymobile.com/wp/2010/05/18/android-one-finger-zoom-tutorial-part-1/#comments</comments>
		<pubDate>Tue, 18 May 2010 14:32:50 +0000</pubDate>
		<dc:creator>andreas agvard</dc:creator>
				<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[tutorial]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[x10]]></category>
		<category><![CDATA[x10 mini]]></category>
		<category><![CDATA[Zoom]]></category>

		<guid isPermaLink="false">http://blogs.sonyericsson.com/developerworld/?p=265</guid>
		<description><![CDATA[Welcome to this first Android tutorial on how to make your own one finger zoom control like the one found in the Camera and Album applications in the Sony Ericsson X10 Mini. The Tutorial is divided into four parts, each part adding new features. Below is a link to download the source code for part 1 [...]]]></description>
			<content:encoded><![CDATA[<p>Welcome to this first Android tutorial on how to make your own one finger zoom control like the one found in the Camera and Album applications in the Sony Ericsson X10 Mini. The Tutorial is divided into four parts, each part adding new features. Below is a link to download the source code for part 1 of the tutorial, prepared for you to set up your own project in e.g. Eclipse.</p>
<p><a href="http://blogs.sonyericsson.com/developerworld/files/2010/05/TutorialZoom11.zip">[Download] One Finger Zoom sample project (211kb)</a></p>
<p>Don&#8217;t miss to download the <strong>Sony Ericsson Tutorials</strong> app on <strong>Android Market</strong> where all applications in this and other Sony Ericsson tutorials are available. With the SonyEricsson tutorials app you can easily try out the different parts of the tutorial and see what the end result will be.</p>
<p>Below is a video showing what you will be able to do after seeing all steps of the tutorial. There following parts of the turoital will be added over the next few weeks.</p>
<p> <object width="425" height="350"><param name="movie" value="ERj67oRHeeE"></param><param name="wmode" value="transparent" ></param><embed src="http://www.youtube.com/v/ERj67oRHeeE" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object></p>
<p><span id="more-265"></span></p>
<p>There will be some math in this tutorial, but there is no need to get discouraged if it is too much for you. Please feel free to skip the math part and accept that they work.</p>
<p>In this first part of the tutorial we are going to make a basic, interactive, zoom control that allows us to zoom into an image. This will be achieved by implementing three classes representing the following: 1) our own <a href="http://developer.android.com/reference/android/view/View.html">View</a> class for drawing images with zoom applied to it, 2) a class holding the state of the zoom, and 3) a class implementing the <a href="http://developer.android.com/reference/android/view/View.OnTouchListener.html">OnTouchListener</a> interface allowing the user to interact with the zoom. As we progress in the tutorial we will see the benefits of dividing our application into different classes this way, as it makes it easy to change specific parts of the behavior.</p>
<div id="attachment_277" class="wp-caption aligncenter" style="width: 273px"><a href="http://blogs.sonyericsson.com/developerworld/files/2010/05/StaticStructureTutorialZoom_11.png"><img class="size-full wp-image-277" src="http://blogs.sonyericsson.com/developerworld/files/2010/05/StaticStructureTutorialZoom_11.png" alt="" width="263" height="187" /></a><p class="wp-caption-text">Basic zoom listener</p></div>
<h3>Defining a zoom state</h3>
<p>The first thing we need to do is to define how the zoom state should work. The goal for our definition should be to make it easy to understand, simple to interact with and do math on. There is probably other decent ways to do this, but I&#8217;ll show you one that worked for me.</p>
<p>We&#8217;ll define the zoom state using three parameters.</p>
<ul>
<li>The amount of zoom, that is how much the content has been scaled. We let a zoom value of 1 define the state where the content fits the view perfectly in at least one dimension.</li>
<li>The amount of pan in dimension-x (pan-x), that is how much the content is moved in respect to the width of the screen. We let the pan value define where the center position of the zoom-view (the view defining which part of the content we see) is in relation to the content. For example, a pan-x value of 0.5 would mean the center of the content would be visible in the center of the view, while a value of 0 would mean the left hand side of the content would be visible in the center of the screen.</li>
<li>The amount of pan in dimension-y (pan-y), like the pan-x except how the content is moved in relation to the height of the screen.</li>
</ul>
<div id="attachment_282" class="wp-caption aligncenter" style="width: 428px"><a href="http://blogs.sonyericsson.com/developerworld/files/2010/05/TutorialZoomState.png"><img class="size-full wp-image-282" src="http://blogs.sonyericsson.com/developerworld/files/2010/05/TutorialZoomState.png" alt="" width="418" height="131" /></a><p class="wp-caption-text">Images illustrating how the zoom state works, the dashed gray area represents what is shown in the view and the patterned area represents the content. On the left: Zoom is 1, pan-x and pan-y are both 0.5, in this state the image fits the screen perfectly. In the middle: Zoom is 2, pan-x and pan-y are still both 0.5, less content is now shown on the screen but will be scaled up. To the right: Zoom is 3, pan-x is 0.7 and pan-y is 0.833, we now see less of the image, only the top right corner, scaled up.</p></div>
<h3>Implementation</h3>
<p>Now that we&#8217;ve got the basics laid down, let&#8217;s move on to coding and let&#8217;s start by implementing the state. Since we know the view will want to determine when the state has been modified we can implement the state as an instance of <a href="http://developer.android.com/reference/java/util/Observable.html">Observable</a>, allowing us to easily notify other objects of state changes.<br />
﻿﻿[java]<br />
public class ZoomState extends Observable {</p>
<p>    private float mZoom;<br />
    private float mPanX;<br />
    private float mPanY;</p>
<p>    public float getPanX() {<br />
        return mPanX;<br />
    }</p>
<p>    public float getPanY() {<br />
        return mPanY;<br />
    }</p>
<p>    public float getZoom() {<br />
        return mZoom;<br />
    }</p>
<p>    public void setPanX(float panX) {<br />
        if (panX != mPanX) {<br />
            mPanX = panX;<br />
            setChanged();<br />
        }<br />
    }</p>
<p>    public void setPanY(float panY) {<br />
        if (panY != mPanY) {<br />
            mPanY = panY;<br />
            setChanged();<br />
        }<br />
    }</p>
<p>    public void setZoom(float zoom) {<br />
        if (zoom != mZoom) {<br />
            mZoom = zoom;<br />
            setChanged();<br />
        }<br />
    }<br />
}<br />
[/java]</p>
<p>As simple as that, get/set methods for the available parameters and a call to setChanged() every time a parameter is changed. The client changing the values is responsible for calling notifyObservers() after modifying the state. Now that we have a state class, let&#8217;s move on to the view, we will extend the basic View class and implement the Observer interface in order to be able to observe and get callbacks when the state changes.</p>
<p>[java]<br />
public class ImageZoomView extends View implements Observer {</p>
<p>    private Bitmap mBitmap;<br />
    private ZoomState mState;</p>
<p>    public ImageZoomView(Context context, AttributeSet attrs) {<br />
        super(context, attrs);<br />
    }</p>
<p>    public void setImage(Bitmap bitmap) {<br />
        mBitmap = bitmap;</p>
<p>        invalidate();<br />
    }</p>
<p>    public void setZoomState(ZoomState state) {<br />
        if (mState != null) {<br />
            mState.deleteObserver(this);<br />
        }</p>
<p>        mState = state;<br />
        mState.addObserver(this);</p>
<p>        invalidate();<br />
    }</p>
<p>    @Override<br />
    protected void onDraw(Canvas canvas) {<br />
    }</p>
<p>    public void update(Observable observable, Object data) {<br />
        invalidate();<br />
    }</p>
<p>}<br />
[/java]<br />
Now we have created the basic structure of the view, and as this will be a zoom view for images we will be using a <a href="http://developer.android.com/reference/android/graphics/Bitmap.html">Bitmap</a> object as the zoom content. We can set a bitmap to zoom in, we can set a zoom state to observe and we call invalidate when the content or the state changes. Next step we want to draw the bitmap, for this we need a <a href="http://developer.android.com/reference/android/graphics/Paint.html">Paint</a> object and two <a href="http://developer.android.com/reference/android/graphics/Rect.html">Rect</a> objects representing rectangles; one for the part of the content we wish to draw and one for the area of the view we wish to draw on.</p>
<p>[java]<br />
    private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);<br />
    private final Rect mRectSrc = new Rect();<br />
    private final Rect mRectDst = new Rect();</p>
<p>    @Override<br />
    protected void onDraw(Canvas canvas) {<br />
        if (mBitmap != null &amp;&amp; mState != null) {<br />
            canvas.drawBitmap(mBitmap, mRectSrc, mRectDst, mPaint);<br />
        }<br />
    }<br />
[/java]<br />
As you can see there is a drawBitmap method on the <a href="http://developer.android.com/reference/android/graphics/Canvas.html">Canvas</a> object that takes two rectangle objects as input parameters just the way we need it to, and since we will be scaling the bitmap we should enable the bitmap filtering on the paint object. Finally we need to set up the rectangles correctly, and then we&#8217;re done.</p>
<p>To set up the rectangles so that the visible part of the source image is cropped correctly and drawn at the correct location on the view we need to introduce a new variable. The reason for this is that if the content does not have the same aspect ratio (width / height) as the view, then a zoom value of 1 will mean that only one of the dimensions fit the view. In the other dimension the content will only partially cover the view, which means it will correspond to a zoom value lower than 1 in that particular dimension. The solution for this is to introduce a variable we will call the aspect quotient, that is the quotient between the aspect ratio of the content and the aspect ratio of the view. By using this value we can separate the zoom value on the x-axis and on the y-axis, and to do so we will add help methods to the ZoomState class to calculate them.</p>
<p>[java]<br />
    public float getZoomX(float aspectQuotient) {<br />
        return Math.min(mZoom, mZoom * aspectQuotient);<br />
    }</p>
<p>    public float getZoomY(float aspectQuotient) {<br />
        return Math.min(mZoom, mZoom / aspectQuotient);<br />
    }<br />
[/java]<br />
As the aspect quotient is defined by the view characteristics and content we will store this value in the ZoomView, and update its value whenever changes are made to the view or content (the content in our case being a bitmap). Such changes are for example if the view changes in size due to the user changing the phone orientation, or if the content changes because of the user switching between zoomable images in an album application.</p>
<p>[java]<br />
    private float mAspectQuotient;</p>
<p>    private void calculateAspectQuotient() {<br />
        if (mBitmap != null) {<br />
            mAspectQuotient =<br />
                (((float)mBitmap.getWidth()) / mBitmap.getHeight()) /<br />
                (((float)getWidth()) / getHeight());<br />
        }<br />
    }</p>
<p>    public void setImage(Bitmap bitmap) {<br />
        mBitmap = bitmap;</p>
<p>        calculateAspectQuotient();</p>
<p>        invalidate();<br />
    }</p>
<p>    @Override<br />
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {<br />
        super.onLayout(changed, left, top, right, bottom);</p>
<p>        calculateAspectQuotient();<br />
    }<br />
[/java]<br />
With the help of these methods we can now implement the onDraw method correctly. We&#8217;ll do this by first calculating how much scaling to apply in relation to the full size of the content (zoomX, zoomY). Then we calculate the source rectangle to crop the portion of the content that should be visible, allowing the calculations to crop outside of the content boundaries. Finally we need to recalculate the source rectangle as the drawBitmap function does not support cropping outside of our bitmap content, and then we recalculate the destination rectangle to compensate for this.</p>
<div id="attachment_284" class="wp-caption aligncenter" style="width: 454px"><a href="http://blogs.sonyericsson.com/developerworld/files/2010/05/zoom1.png"><img class="size-full wp-image-284" src="http://blogs.sonyericsson.com/developerworld/files/2010/05/zoom1.png" alt="" width="444" height="268" /></a><p class="wp-caption-text">Images showing how the zooming is done by setting up source and destination rectangles. The leftmost image shows the content with the crop area shown with a dashed square, this is how the source rectangle is initially calculated. To its right is the result shown in the zoom view, the dashed square here represents the destination rectangle that now covers the entire zoom view.</p></div>
<p style="text-align: center"><em><br />
</em></p>
<div id="attachment_285" class="wp-caption aligncenter" style="width: 454px"><a href="http://blogs.sonyericsson.com/developerworld/files/2010/05/zoom2.png"><img class="size-full wp-image-285" src="http://blogs.sonyericsson.com/developerworld/files/2010/05/zoom2.png" alt="" width="444" height="268" /></a><p class="wp-caption-text">The next step is to recalculate the rectangles so that the source rectangle fits inside of the content without changing the result shown on the screen. The left most image now shows the content with the zoom window completely inside the content. To its right is the result shown in the zoom view, the dashed square represents the destination rectangle that has now been recalculated so that it doesn&#039;t cover the parts of the view where nothing should be drawn.</p></div>
<p>[java]<br />
    @Override<br />
    protected void onDraw(Canvas canvas) {<br />
        if (mBitmap != null &amp;&amp; mState != null) {<br />
            final int viewWidth = getWidth();<br />
            final int viewHeight = getHeight();<br />
            final int bitmapWidth = mBitmap.getWidth();<br />
            final int bitmapHeight = mBitmap.getHeight();</p>
<p>            final float panX = mState.getPanX();<br />
            final float panY = mState.getPanY();<br />
            final float zoomX = mState.getZoomX(mAspectQuotient) * viewWidth / bitmapWidth;<br />
            final float zoomY = mState.getZoomY(mAspectQuotient) * viewHeight / bitmapHeight;</p>
<p>            // Setup source and destination rectangles<br />
            mRectSrc.left = (int)(panX * bitmapWidth &#8211; viewWidth / (zoomX * 2));<br />
            mRectSrc.top = (int)(panY * bitmapHeight &#8211; viewHeight / (zoomY * 2));<br />
            mRectSrc.right = (int)(mRectSrc.left + viewWidth / zoomX);<br />
            mRectSrc.bottom = (int)(mRectSrc.top + viewHeight / zoomY);<br />
            mRectDst.left = getLeft();<br />
            mRectDst.top = getTop();<br />
            mRectDst.right = getRight();<br />
            mRectDst.bottom = getBottom();</p>
<p>            // Adjust source rectangle so that it fits within the source image.<br />
            if (mRectSrc.left &lt; 0) {<br />
                mRectDst.left += -mRectSrc.left * zoomX;<br />
                mRectSrc.left = 0;<br />
            }<br />
            if (mRectSrc.right &gt; bitmapWidth) {<br />
                mRectDst.right -= (mRectSrc.right &#8211; bitmapWidth) * zoomX;<br />
                mRectSrc.right = bitmapWidth;<br />
            }<br />
            if (mRectSrc.top &lt; 0) {<br />
                mRectDst.top += -mRectSrc.top * zoomY;<br />
                mRectSrc.top = 0;<br />
            }<br />
            if (mRectSrc.bottom &gt; bitmapHeight) {<br />
                mRectDst.bottom -= (mRectSrc.bottom &#8211; bitmapHeight) * zoomY;<br />
                mRectSrc.bottom = bitmapHeight;<br />
            }</p>
<p>            canvas.drawBitmap(mBitmap, mRectSrc, mRectDst, mPaint);<br />
        }<br />
    }<br />
[/java]<br />
So great, we have our view and state classes working, now let&#8217;s make a simple OnTouchListener so we can get to play around with the zoom as soon as possible! The listener will change the zoom or pan values of the state when the user touches the screen. For simplicity we&#8217;ll either zoom or pan when touch starts, which to do will be determined by an attribute we&#8217;ll call control type. Other than that we need to store the coordinates of the previous touch event so we know how much the touch has moved between each move event we receive.</p>
<p>For the zoom value we use the distance moved in the y-dimension, using this as the exponent when calculating the exponentiation of of a well chosen number, and then multiply the result with the current zoom level. Multiplying with an exponentiation is practical since if we where to for example move 10 pixels in the y-dimension, doing so gives the same result if it&#8217;s obtained by one or several move events. This is because, where we get the same result when moving a + b pixels as we get if we first move a pixels and then b pixels</p>
<p>For the pan we will simply add the distance moved in to their respective pan values, making the pan move relative to the touch movement. Finally we call notifyObservers() on the state in order to make the view redraw it&#8217;s content.</p>
<p>[java]<br />
public class SimpleZoomListener implements View.OnTouchListener {</p>
<p>    public enum ControlType {<br />
        PAN, ZOOM<br />
    }</p>
<p>    private ControlType mControlType = ControlType.ZOOM;</p>
<p>    private ZoomState mState;</p>
<p>    private float mX;<br />
    private float mY;</p>
<p>    public void setZoomState(ZoomState state) {<br />
        mState = state;<br />
    }</p>
<p>    public void setControlType(ControlType controlType) {<br />
        mControlType = controlType;<br />
    }</p>
<p>    public boolean onTouch(View v, MotionEvent event) {<br />
        final int action = event.getAction();<br />
        final float x = event.getX();<br />
        final float y = event.getY();</p>
<p>        switch (action) {<br />
            case MotionEvent.ACTION_DOWN:<br />
                mX = x;<br />
                mY = y;<br />
                break;</p>
<p>            case MotionEvent.ACTION_MOVE: {<br />
                final float dx = (x &#8211; mX) / v.getWidth();<br />
                final float dy = (y &#8211; mY) / v.getHeight();</p>
<p>                if (mControlType == ControlType.ZOOM) {<br />
                    mState.setZoom(mState.getZoom() * (float)Math.pow(20, -dy));<br />
                    mState.notifyObservers();<br />
                } else {<br />
                    mState.setPanX(mState.getPanX() &#8211; dx);<br />
                    mState.setPanY(mState.getPanY() &#8211; dy);<br />
                    mState.notifyObservers();<br />
                }</p>
<p>                mX = x;<br />
                mY = y;<br />
                break;<br />
            }</p>
<p>        }</p>
<p>        return true;<br />
    }</p>
<p>}<br />
[/java]<br />
Alright, so we&#8217;re almost done. Now we just need an <a href="http://developer.android.com/reference/android/app/Activity.html">Activity</a> and a layout to go with this and we&#8217;re done, the layout is quite straight forward, we simply add our new ZoomView and tell it to fill it&#8217;s parent.</p>
<p>[xml]<br />
&lt;?xml version=&#8221;1.0&#8243; encoding=&#8221;utf-8&#8243;?&gt;<br />
&lt;com.sonyericsson.zoom.ImageZoomView<br />
    xmlns:android=&#8221;http://schemas.android.com/apk/res/android&#8221;<br />
    android:id=&#8221;@+id/zoomview&#8221;<br />
    android:layout_width=&#8221;fill_parent&#8221;<br />
    android:layout_height=&#8221;fill_parent&#8221;<br />
    /&gt;<br />
[/xml]<br />
In the activity we set up our zoom components in the onCreate() method, creating a new zoom state, decoding the bitmap we want to zoom in, creating the listener and connecting it to the state, and finally setting up the view. We also implement the onDestroy method and make sure to recycle bitmaps and remove listeners.</p>
<p>Finally we implement onCreateOptionsMenu() and onOptionsItemSelected() respectively enabling the user to change input method using the options menu and to reset the zoom to its original state.</p>
<p>[java]<br />
public class TutorialZoomActivity1 extends Activity {</p>
<p>    private static final int MENU_ID_ZOOM = 0;<br />
    private static final int MENU_ID_PAN = 1;<br />
    private static final int MENU_ID_RESET = 2;</p>
<p>    private ImageZoomView mZoomView;<br />
    private ZoomState mZoomState;<br />
    private Bitmap mBitmap;<br />
    private SimpleZoomListener mZoomListener;</p>
<p>    @Override<br />
    public void onCreate(Bundle savedInstanceState) {<br />
        super.onCreate(savedInstanceState);</p>
<p>        setContentView(R.layout.main);</p>
<p>        mZoomState = new ZoomState();</p>
<p>        mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image800x600);</p>
<p>        mZoomListener = new SimpleZoomListener();<br />
        mZoomListener.setZoomState(mZoomState);</p>
<p>        mZoomView = (ImageZoomView)findViewById(R.id.zoomview);<br />
        mZoomView.setZoomState(mZoomState);<br />
        mZoomView.setImage(mBitmap);<br />
        mZoomView.setOnTouchListener(mZoomListener);</p>
<p>        resetZoomState();<br />
    }</p>
<p>    @Override<br />
    protected void onDestroy() {<br />
        super.onDestroy();</p>
<p>        mBitmap.recycle();<br />
        mZoomView.setOnTouchListener(null);<br />
        mZoomState.deleteObservers();<br />
    }</p>
<p>    @Override<br />
    public boolean onCreateOptionsMenu(Menu menu) {<br />
        menu.add(Menu.NONE, MENU_ID_ZOOM, 0, R.string.menu_zoom);<br />
        menu.add(Menu.NONE, MENU_ID_PAN, 1, R.string.menu_pan);<br />
        menu.add(Menu.NONE, MENU_ID_RESET, 2, R.string.menu_reset);<br />
        return super.onCreateOptionsMenu(menu);<br />
    }</p>
<p>    @Override<br />
    public boolean onOptionsItemSelected(MenuItem item) {<br />
        switch (item.getItemId()) {<br />
            case MENU_ID_ZOOM:<br />
                mZoomListener.setControlType(ControlType.ZOOM);<br />
                break;</p>
<p>            case MENU_ID_PAN:<br />
                mZoomListener.setControlType(ControlType.PAN);<br />
                break;</p>
<p>            case MENU_ID_RESET:<br />
                resetZoomState();<br />
                break;<br />
        }</p>
<p>        return super.onOptionsItemSelected(item);<br />
    }</p>
<p>    private void resetZoomState() {<br />
        mZoomState.setPanX(0.5f);<br />
        mZoomState.setPanY(0.5f);<br />
        mZoomState.setZoom(1f);<br />
        mZoomState.notifyObservers();<br />
    }<br />
}<br />
[/java]<br />
And voila, we&#8217;re done! You can now start to play around with the zoom control on your own. It&#8217;s basic but works perfectly well for zooming around in images. A few things will probably seem to be lacking at the moment: first panning around doesn&#8217;t follow the finger correctly, and there is no limits or snap to functionality to keep us within reasonable bounds. These things, and others, will be the topic of the next parts of this zoom tutorial.</p>
<div id="attachment_286" class="wp-caption aligncenter" style="width: 433px"><a href="http://blogs.sonyericsson.com/developerworld/files/2010/05/StaticStructureTutorialZoom_2.png"><img class="size-full wp-image-286" src="http://blogs.sonyericsson.com/developerworld/files/2010/05/StaticStructureTutorialZoom_2.png" alt="" width="423" height="246" /></a><p class="wp-caption-text">Class diagram showing how the zoom components interact</p></div>
<p>If you want to learn more about the zoom I suggest playing around with the code a bit, try changing how to zoom is controlled by the listener and try zooming into your own images.</p>
<p>Good luck!</p>
<p><a href="http://blogs.sonyericsson.com/developerworld/files/2010/05/TutorialZoom11.zip">[Download] One Finger Zoom sample project (211kb)</a></p>
]]></content:encoded>
			<wfw:commentRss>http://developer.sonymobile.com/wp/2010/05/18/android-one-finger-zoom-tutorial-part-1/feed/</wfw:commentRss>
		<slash:comments>32</slash:comments>
		</item>
	</channel>
</rss>

