Posts Tagged 'touch'

Multitouch Panning and Zooming Examples for Android

I have done some work to help me understand multitouch features in Android. I built a simple demo with examples of pinch zooming and panning. Most of the examples involve using drawing operations on the canvas of a view.

zoom-pan-blog-01zoom-pan-01

About two years ago, I started some work on moving images in Android. I started with the “Making Sense of Multitouch” blog article and ended up doing some simple things with moving images and then drag-drop based on the Android Launcher (see references for links). It’s interesting to me that I have gone back to that same article for a different purpose. I am working on apps where the basic zoom and pan operations are needed.

Overview of the Examples

1 Basic Multitouch

The first example does the basics of multitouch as explained in the Multitouch article. An image displays on the screen. You can touch the image and move it around. You can also use two fingers to do pinch-zoom and zoom out.

zoom-pan-04

I did this as the first step because I wanted to be sure that I understood the Multitouch article and had a working example.

To understand how this example works, go through the Mulitouch article and then get the source code for my examples (see below). My adaptation of the Multitouch article is in class PanZoomView.

/**
 * This view supports both zooming and panning.
 * What gets shown in the view depends on the onDraw method provided by subclasses.
 * The default onDraw method defined displays R.drawable.zoom_view_sample.
 */
public class PanZoomView extends View

The key things to understand in the article and the code are these:

  • How the canvas moves around. See the call to “canvas.translate(x, y)” in method onDraw.
  • How the canvas is scaled during a pinch-zoom gesture. See the call to :canvas.scale(mScaleFactor, mScaleFactor)” in method onDraw.
  • How onTouchEvent handles touch and multitouch events. It sets variables so the calls to translate and scale work.

2 Canvas

In the second section, there are two examples that do essentially the same thing for handling panning and zooming as the first one. The difference is that instead of an image being moved around and zoomed, it is a circle and a rectangle. Those are drawn on the canvas in the onDraw method of the view.

zoom-pan-05 zoom-pan-06

I did these as the next step in understanding how to build my views for my own app. Zooming an image was step one. Zooming a custom canvas was step 2. To help me understand pinch zoom and the point around which zooming occurs, I display a small circle at the point of focus for the scaling of the view. Place two fingers on the screen and look for that dot, halfway between your two fingers.

The Circles example supports both panning and zooming. The Rectangles example supports only zooming. I found myself getting a bit confused while trying to understand for translation on the canvas and scaling so both examples were useful to me.

3. Pan Zoom Listener

PanZoomListener is an interesting example. It comes from Java Pan / Zoom listener for Android (reference 2 below). Rather than doing a custom view every time you want to have panning and zooming, which is what all the other examples do, you simply set up a listener on a view you already have working.

FrameLayout f = new FrameLayout(context);
FrameLayout.LayoutParams fp = new FrameLayout.LayoutParams (...);
imageView imageView = new ImageView (this);
view.addView (imageView, fp);
view.setOnTouchListener(new PanAndZoomListener(view, imageView, Anchor.TOPLEFT));

That may look like a lot to do, but it’s not nearly as complicated as what you do when you build a view of your own with custom handling of panning and zooming.

It works pretty well, but there are a few things that you’d need to adjust because not all of the operations are completely smooth. I definitely like the idea. I adapted the code and built my own example.

What you see in the example I did is an image. As soon as you touch it, it resizes and centers itself. After that, you can use pinch zoom on it. The focus of the zoom is the point between your two fingers.

zoom-pan-09 zoom-pan-10

4. Image Squares

This example was another step toward getting the right panning and zooming effects in the app that I am working on. In the app, I want to be able to draw a grid of squares on a canvas. In the example, I don’t draw the entire grid but simply some squares along a diagonal from top to bottom. This example supports zooming but not moving the canvas around as you touch the screen with one finger. The focus point of the zoom is shown with a small circle again.

zoom-pan-07

You cannot move the canvas around with a simple touch. However, if you start a pinch-zoom gesture and then move both fingers around the image does shift. I never figured out exactly what was causing that. So watch out for that if build on this example.

5. Fixed Point Zoom

Since I was having a few problems with the canvas jumping around a bit, I decided to try using a fixed point for zooming. The last  example zooms in and out around the center dot shown on the screen.

zoom-pan-11 zoom-pan-14

The code structure of this example is a bit different than the earlier ones. All of the views in these examples are subclasses of a PanZoomView class. The view used in this example is LargeGridView. It defines its own drawOnCanvas method like all the rest do, but it also defines its own onDraw method. By the time I got here, I found that the flexible framework I built in PanZoomView to explore the different aspects of panning and zooming wasn’t quite right for fixed point zooming.

Fixed point zoom is what I have ended up using in a couple of apps.

Conclusion

I have done this article a bit differently than some of my earlier blog tutorials. I have not included as much code and explanations.  I assume that most readers will dig into the code as they need to. Download the source code using the links in the next section.

What I wanted to do here is show the progression of my thinking and understanding as I worked on an interesting problem. If you are new to Android, I suggest following a similar progression. Start simple and add more complex features as your understanding increases.

Source Code

You can download the source code for this demo from  the wglxy.com website. Click here: download zip file from wglxy.com. The zip is attached at the bottom of that page. After you import the project into Eclipse, it’s a good idea to use the Project – Clean menu item to rebuild the project.

This demo app was compiled with Android 4.2 (API 17). It works in all API levels from API 10 on up.

Source code is also available as a zip file from Google Drive.

References

Making Sense of Multitouch – I recommend this to anyone looking for a good starting point for touch handling in Android.

Java / Pan Zoom Listener for Android - Article that describes the PanZoomListener. Definitely, take a look at this one.

Moving Views in Android – Part 1 – a simple example that moves an image when you click a button

Moving Views in Android – Part 2, Drag and Drop – an example based on the Launcher code of Android 2.2.

Android Images With Clickable Areas – Part 1

I want to display images in Android and have different clickable regions within the image. I have read about two different ways of doing this.  The first is a bit like image maps in HTML. You define areas on the screen, using a list of coordinates, and connect the areas to actions. The second method  involves overlaying a hotspot image on top of the real image being displayed. The overlay image has exactly the same size as the first image. It uses different colored regions  to indicate the hotspots. When the user clicks on the primary image, code runs to check the pixel at the corresponding point in the hotspot image. In this, the first of two articles about image maps in Android, I will explain what I learned for the second technique.

Let’s start with what the app looks like when you touch the screen. Circles appear that indicate roughly where the clickable regions on the screen are. When you touch one of the regions, the image changes.

For example, when you touch the image near the end of the space ship , the app changes to make it look like you started the space ship.

Touching the image a second time, returns you to the initial view of the app, which is the first picture without the orange circles showing.

The way this app is set up with a FrameView and ImageView gives you an easy way to handle scaling. I used a 800 x 480 image for this demo app. It scales automatically to all screen sizes, and it is high enough resolution that it looks good on a tablet. Here’s what it looks like in a portrait orientation.

In a real app, you might disallow portrait orientation because it has a very small usable view. I left it in for testing reasons. I wanted to know that the image and the hotspot image were scaling correctly.

How It Works

Most of what goes on in this app is straightforward stuff in Android: clickable images, changing the drawable of an ImageView, etc. The really interesting part for me was getting the scaling done automatically. Two parts of that were tricky: (1) getting the layout just right so the images would scale correctly on all Android devices and tablets; (2) getting the clickable regions to work and also to scale automatically.

Two images are used in the main layout for this app. They are shown below. The first is the view of the starbase that you have seen above. The second image has several rectangles on it. If you were to overlay the second image on top of the first, you would see that they are close to the stops were the circles were drawn to indicate clickable regions.

The overlay image has exactly the same size as the first image. It uses different colored regions  to indicate the hotspots. When the app is running, the overlay image is used to locate a hotspot, which is a region on the screen that you want to define an onClick handler for. When the user clicks on the primary image, code runs to check the pixel at the corresponding point in the hotspot image. I will explain that a bit later. First let’s look at how the images are laid out on the screen.

The main layout for this app uses a FrameView. It is defined to fill up the entire screen. Within it are two image views. Views defined this way in a FrameView share the space of the frame. The second one appears on top of the first one.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:id="@+id/my_frame"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 android:background="@color/background" >
<ImageView 
 android:id="@+id/image_areas"
 android:layout_width="fill_parent" 
 android:layout_height="fill_parent" 
 android:scaleType="fitCenter"
 android:visibility="invisible"
 android:src="@drawable/p2_ship_mask" 
 />
 <ImageView
 android:id="@+id/image"
 android:layout_width="fill_parent" 
 android:layout_height="fill_parent" 
 android:scaleType="fitCenter"
 android:src="@drawable/p2_ship_default"
 />

</FrameLayout>

Notice that the image view that shows the masks image is invisible. When the app runs, you do not see it, but the layout manager has included it in the layout. Since both ImageViews use “fill_parent” (same as “match_parent”), they expand to fill the parent view. Scaling is done according to the rules for “fitCenter”, which means centering the image both horizontally and vertically and scaling until either the width or the height matches the parent view’s dimension.

With this layout in place, all that is needed is code in the Activity to handle the screen being touched. The main activity implements View.onTouchListener. The method required by that interface is the onTouch method. Here is that method, shortened a bit to make it easier to explain.

public boolean onTouch (View v, MotionEvent ev) {
 final int action = ev.getAction();
 // (1) 
 final int evX = (int) ev.getX();
 final int evY = (int) ev.getY();
 switch (action) {
 case MotionEvent.ACTION_DOWN :
   if (currentResource == R.drawable.p2_ship_default) {
   nextImage = R.drawable.p2_ship_pressed;
   } 
   break;
 case MotionEvent.ACTION_UP :
   // On the UP, we do the click action.
   // The hidden image (image_areas) has three different hotspots on it.
   // The colors are red, blue, and yellow.
   // Use image_areas to determine which region the user touched.
   // (2)
   int touchColor = getHotspotColor (R.id.image_areas, evX, evY);
   // Compare the touchColor to the expected values. 
   // Switch to a different image, depending on what color was touched.
   // Note that we use a Color Tool object to test whether the 
   // observed color is close enough to the real color to
   // count as a match. We do this because colors on the screen do 
   // not match the map exactly because of scaling and
   // varying pixel density.
   ColorTool ct = new ColorTool ();
   int tolerance = 25;
   nextImage = R.drawable.p2_ship_default;
   // (3)
   if (ct.closeMatch (Color.RED, touchColor, tolerance)) {
      // Do the action associated with the RED region
      nextImage = R.drawable.p2_ship_alien;
   } else {
     //...
   }
   break;
  } // end switch
  if (nextImage > 0) {
    imageView.setImageResource (nextImage);
    imageView.setTag (nextImage);
  }
  return true;
}

There is a lot going on in this method:

(1) The coordinates of the touch point come from the event object. The coordinates are relative to the view.

(2) Given the coordinates of the touch, we look up the color of a pixel at the corresponding point in the mask image (the one with the colored rectangles). The layout rules for a FrameView and identically sized images ensure that the points correspond. The code for that follows:

public int getHotspotColor (int hotspotId, int x, int y) {
  ImageView img = (ImageView) findViewById (hotspotId);
  img.setDrawingCacheEnabled(true); 
  Bitmap hotspots = Bitmap.createBitmap(img.getDrawingCache()); 
  img.setDrawingCacheEnabled(false);
  return hotspots.getPixel(x, y);
}

(3) The color for point is not necessarily the exact color value we used in the hotspot image. The reason for this is that the colors could change a bit as the image is scaled. We use a new ColorTool object to test for a matching color.

public boolean closeMatch (int color1, int color2, int tolerance) {
 if ((int) Math.abs (Color.red (color1) - Color.red (color2)) > tolerance ) 
    return false;
 if ((int) Math.abs (Color.green (color1) - Color.green (color2)) > tolerance ) 
    return false;
 if ((int) Math.abs (Color.blue (color1) - Color.blue (color2)) > tolerance ) 
    return false;
 return true;
} // end match

That’s all the code parts. Let’s summarize what happens as the app runs.

  • The user touches the screen.
  • Because that Activity has an onTouch handler, the method onTouch gets called.
  • The code there looks at the event argument to get the x-y position of the touch.
  • These are coordinates relative to the origin of the ImageView, which is itself embedded in the FrameView.
  • Given the x-y location of the touch, the code locates the hidden hotspot image and finds the pixel at the corresponding location.
  • It then takes the color there and finds the best match for it in the following colors: WHITE, RED, BLUE, YELLOW.
  • It then takes the action defined for that color.

For this demo, I used a simple image editing program: Mac Paintbrush. I took an image and made a copy so I could see where I wanted to add the clickable regions.  I added the three rectangles for the colors first. Then I added white over everything else. I saved that as PNG file.

Source Code

You can download the source code form the wglxy.com website. Click here: download sources from wglxy.com. The zip is attached to the bottom of that page. After you import the project into Eclipse, it’s a good idea to use the Project – Clean menu item to rebuild the project.

This demo app was compiled with Android 2.3.3 (API 10). It works in all API levels after API 8.

Conclusion

This way of adding clickable regions is easy to do and takes full advantage of Android’s handling of different screen sizes, screen densities, and orientations. You do not incur much overhead for the second overlay image. In my case, it was only 4 KB. The original image, as a png, was about 70 KB. For simple transitions from one activity to another, this method works out pretty well.

In my next note, I will take a look at another method of doing image maps and redo this example.

References

  • Overlay to make parts of image clickable – discussion in the Android Developers group. This is where I learned about the overlay image method.
  • ImageMaps for Android – This is a very good example of doing images maps by defining regions in xml files. It displays a US map where you can touch the different states. Something like this will be the subject of my next article on image maps.
  • freepik spaceship – The spaceship image came from the freepik website. It is free for noncommercial use.

Moving Views In Android – Part 3, Drop Zones

DragViewV2 at startI have come to the end of my work on drag-drop in Android. I now understand how views can be placed on the screen and moved by touching them. I also worked out how you can have different drop behavior for different areas of the screen. In my example, I called those areas DropZones. As you drag views around the screen, the drop zones light up to indicate that they are places where you can drop objects.

The figures  show what the app looks like at various points. Figure 1 is what it looks like when it launches. There are two red drop zones and one orange drop zone. There are two images initially visible. Figure 2 shows the menu where you can add other objects and enable and disable the orange drop zone. The other two show the background color changes in the drop zones as you move over them. Green means the drop will be accepted. White means it will not.

DragViewV2 menu

Figures 2-4

How It Works

The classes in this app are derived from the Android Launcher application. In an earlier note (see “Moving Views In Android – Part 2“), I had written up how the Android Launcher app supports moveable views using the touch interface. The Launcher object model includes objects like DragLayer, DragSource, DragController, and DropTarget. The one object I did not explore fully was the DropTarget. That’s what this note (Part 3) is about.

Here’s how DropTargets figure into the Android Launcher. In the Launcher, when you start dragging an icon, check out what happens at the bottom of the screen. You should see that a Trash icon appears. If you drag an icon over that icon, you will see a change in the color of the view being dragged. The red color gives you a good indication that something will happen if you drop the icon there. Since it is a trash icon, you know that it means the icon being dragged will be removed from the workspace. In code, what’s going on is that the Trash view implements the DropTarget interface.

The DropTarget interface defines the following methods:

 boolean acceptDrop (DragSource source, int x, int y, int xOffset,
             int yOffset,DragView dragView, Object dragInfo);
  Rect estimateDropLocation (DragSource source, int x, int y, int xOffset,
             int yOffset,DragView dragView, Object dragInfo, Rect recycle);
  void onDragEnter (DragSource source, int x, int y, int xOffset,
             int yOffset,DragView dragView, Object dragInfo);
  void onDragOver (DragSource source, int x, int y, int xOffset,
             int yOffset,DragView dragView, Object dragInfo);
  void onDragExit (DragSource source, int x, int y, int xOffset,
             int yOffset,DragView dragView, Object dragInfo);
  void onDrop (DragSource source, int x, int y, int xOffset,
             int yOffset,DragView dragView, Object dragInfo);

acceptDrop is there so the drop target has a way to tell the DragController that it is willing to accept a dropped object. The other methods are there so the drop target knows what’s going on during the move of the object. Each time you enter the area defined by the drop target view, onDragEnter is called. Each time you leave, onDragExit is called. That makes those the perfect spot to provide the user with feedback by highlighting the view or the drop target itself. In the Launcher, the view turns red when over a drop target. In my example, the background color changes.

In my example, I added a new class, DropZone, to implement the interface. I set up onDragEnter and onDragExit to make changes in the background color so the user gets a sense of where the dragged object will land. As you run the app, be sure to note the difference between the two red areas on the screen and the orange area. The orange drop zone is set up initially so that it will not accept drops. Try dropping something there and you will see that the dragged object returns to its starting location. Then try again, after using the menu to enable the orange drop zone. Note that the enter color becomes green and that you can drop something there.

Source Code

The source code for this demo application is available in two places:  (1) download from Wglxy.com. (2) download from Google Docs.  If you find that the app does not build in Eclipse, be sure to do a clean build by using the “Clean” item on the Project menu.

This work was done on a MacBook running Eclipse Galileo and Android 2.2.

Moving Views In Android – Part 2, Drag and Drop

This is part 2 of my investigation into how to get views on an Android screen that a user can move by touching them and dragging them. For Moving Views in Android – Part 1, I had a couple of images positioned on the screen and got the images to move on the screen. It turns out that the views themselves were not moving, just the images within the bounds of the view. In the work that I describe here, I actually have the views moving. The key was to have a custom ViewGroup to coordinate drag and drop events for all views within the layout.

The figures below show what the app looks like in the emulator. Figure 1 shows the initial state of the app. Two images and a text view are in the upper left of the screen. The other figures show the views being dragged around the screen. Red highlighting indicates which view is moving.

Figures 1-2

Dragging an image

Figures 3-4:

Dragging the second image Moving the text view

The solution feels pretty good — and it should, considering it is derived from the Android Launcher code. I had searched a long time for good touch and drag-drop examples, and never found any that were as complete as what the Android developers had done. What I like is the good object model and the clear separation of responsibilities among the classes. More on that follows in the next section.

(For more on drag-drop, see “Drag-Drop for Android GridView V4“.) 

How It Works

The Launcher is the activity that manages the workspace you see on your Android phone. Besides being able to launch apps, it allows you to change the layout of icons on the screen. You touch an icon, wait for the feedback (vibration and the change in size of the icon) that you have touched a moveable icon, and then you reposition the icon on the screen.

I made a few simplifying assumptions as I built my example activity object.

  1. The example app allows you to move views, but only on a single screen. The Launcher allows you to place app icons on many screens that you get to by scrolling left and right.
  2. The example app supports only one place where you can drop views. The Launcher supports multiple DropTargets.
  3. The example app allows views to be dropped anywhere, even on top of another view. The Launcher maintains a grid of icons and icons can be dropped only in the grid.
  4. The Launcher has animation and scaling code that is used to show a bitmap of the view being moved. The example app simply shows the bitmap.

The following classes have been adapted for my example DragView application.

DragActivity – The Launcher class was turned into my DragActivity class. It is the main activity of the app.

DragLayer – implements a custom ViewGroup, which coordinates movement of views on the screen. The Launcher DragLayer is a subclass of FrameLayout. Mine is a subclass of MyAbsoluteLayout, which is a clone of the recently deprecated AbsoluteLayout class. AbsoluteLayouts allow views to be positioned to specific absolute x and y positions.

DragController – This object is the controller that does most of the work to support dragging and dropping.

DragSource – defines where a drag operation begins.

DropTarget – defines where an object can be dropped.

DragView – This is the view that you see moving around on the screen during a drag operation. It is not the actual view you want to move, but simply a bitmap that looks like the view.

In this object model, here is how an object moves on the screen. The user touches a view on the screen. The view touched signals an event, onLongClick, to its listener, which is the DragActivity. The activity turns it over to its DragController object. The controller initiates a drag operation by first getting a bitmap copy of the view, storing it in a DragView, and displaying it on the screen. As the user moves the view, the DragLayer fields all the touch events in its onTouchEvent method. It relays those events to the DragController. It watches for the end of the touch (ACTION_UP), but in the mean time, it handles the motion events by shifting the DragView to the new touch position. When the touch finally ends, the controller checks its set of DropTargets to see which one is active and if it will accept the view being dropped there. If it does, the controller calls the onDrop method of the target. In this application, the DragLayer also implements the DropTarget interface. So the onDrop call goes to the DragLayer and it takes the view being dragged and repositions it to the drop location.

The full source code is available below. A few code sections are worth noting now.

Each view that can be moved relays its onLongClick events to the DragActivity. The connection is made in the setupViews method of DragActivity.

private void setupViews() {
...
ImageView i1 = (ImageView) findViewById (R.id.Image1);
ImageView i2 = (ImageView) findViewById (R.id.Image2);
i1.setOnLongClickListener(this);
i2.setOnLongClickListener(this);
...
}

The onLongClick handler calls on the DragController to initiate the drag sequence.

public boolean onLongClick(View v) {
    trace ("onLongClick in view: " + v);
    // Let the DragController initiate a drag-drop sequence.
    // Use the dragInfo to pass along the object being dragged.
    Object dragInfo = v;
    mDragController.startDrag (v, mDragLayer, dragInfo, DragController.DRAG_ACTION_MOVE);
    return true;
}

While the drag is happening, the DragLayer passes all touch events to its DragController. The onTouchEvent method in DragController moves the DragView and its bitmap. That creates the appearance of the view moving on the screen. The move method in DragView uses updateViewLayout, which causes the layout for the screen to be redone, using whatever new x-y positions are in effect.

void move(int touchX, int touchY) {
        // This is what was done in the Launcher code.
        WindowManager.LayoutParams lp = mLayoutParams;
        lp.x = touchX - mRegistrationX;
        lp.y = touchY - mRegistrationY;
        mWindowManager.updateViewLayout(this, lp);
}

At the end of the drag, the onDrop method of DragLayer is called. It repositions the actual view. Note that it too calls to updateViewLayout. That is what causes the DragLayer view to recalculate its layout and redraw all its views.

public void onDrop (DragSource source,
         int x, int y, int xOffset, int yOffset,
        DragView dragView, Object dragInfo)
{
    View v = (View) dragInfo;
    int w = v.getWidth ();
    int h = v.getHeight ();
    int left = x - xOffset;
    int top = y - yOffset;
    DragLayer.LayoutParams lp = new DragLayer.LayoutParams (w, h, left, top);
    this.updateViewLayout(v, lp);
}

Potential Improvements

My adaptation is not quite as clean as it should be. Roughly speaking, in MVC (model-view-controller) terms, the DragActivity is the model, the DragLayer is the view, and the DragController is the controller. It’s not clear to me that I have stayed true to MVC in this example. I think the handlers for click and long click events might be better off in the controller.

This example uses a clone of the deprecated AbsoluteLayout class. Now that I understand the Launcher code better, I see that it might be better in a real application to have the DragLayer view determine exactly where views are allowed to be dropped. That would be similar to the way it was done in the Launcher app. Its Workspace class allows drops only at empty spaces in a grid. (Note: I did something for GridViews. See “Drag-Drop for an Android GridView“.) 

If you want to see an example where views are added dynamically to the DragLayer, see “Moving Views In Android – Part 3: Drop Zones“. It also shows how to give users more visual feedback as objects are being dragged around the screen.

Acknowledgements

Thanks go to the Android development team. With all the code being open source, it makes it easy for all of us to learn from their designs. A post in the Android Developers group about doing drag and drop without AbsoluteLayout pointed me in the right direction.

Source Code

The source code is available as an Eclipse project. it is available in two places:  (1) download from Wglxy.com. (2) download from Google Docs.  Do a “clean build” in Eclipse if it reports errors immediately after importing the project.

If you find that the app does not build in Eclipse, be sure to do a clean build by using the “Clean” item on the Project menu. 

This work was done on a MacBook running Eclipse Galileo and Android 2.2 (API 8).  

Moving Views in Android – Part 1

I am building an Android app where I want to have a lot of views on the screen and allow the user to move them around by touching and dragging them. I’m not there yet, but I have made some progress. So far, I have learned enough about Android touch events to get a view to move when it is touched. I’ve also learned how to hide and show views.

I have been working with ImageView objects. The app looks like Figure 1 when it starts. After a few touches (clicks in the emulator), the image views’ positions have been displaced relative to their original position. That is shown in Figure 2.

Screenshot from HideAndMoveImages in HideAndMove after a few clicks

Figures 1 – 2

How It Works

In the Android developer website, I had found an article about touch events and moving objects: Making Sense of Multitouch. It showed how to build a simple custom view that tracks touch events and moves an icon within the view. The key bit of code was in its override of the onDraw method. The method translate the position of the canvas by an x-y amount before it draws the icon that is being moved about the screen.

public void onDraw (Canvas canvas) {
   super.onDraw (canvas);
   canvas.save ();
   canvas.translate (mPosX, mPosY); 
   mIcon.draw (canvas);
   canvas.restore ();
 }

I wanted to work with ImageView objects so I did a similar thing in a custom subclass of ImageView. MyImageView has an onDraw method that looks like the following:

public void onDraw(Canvas canvas) {
  canvas.translate(mPosX, mPosY);
  super.onDraw(canvas);
}

An ImageView object already knows how to draw itself so all I had to do was translate the canvas position and call the superclass method. The instance variables mPosX and mPosY are used to keep track of the x and y displacements of the view from its original position.

I set up the HideAndMove activity so it handles any touch events on the ImageViews. It was actually a pretty simple handler. All it did was change the position of the view a little in the x and y position. It did this using the changePos method in MyImageView.

public boolean onTouch (View v, MotionEvent ev) {
  MyImageView iv = (MyImageView) v;
  float dx = 10f;
  float dy = -20f;
  iv.changePos (dx, dy);
  return false;
 }

In MyImageView there is a changePos method that takes the delta x and y values and changes the position of the view. Its code is shown below. It sets the mPosX and mPosY values and calls the invalidate method for the view. That last call is what makes the view redraw itself, and as we have already seen above, the onDraw method translates the canvas by the mPosX and mPosY values before drawing its image.

public void changePos (float dx, float dy){
  mPosX += dx;
  mPosY += dy;
  this.invalidate ();
}

That’s all I have so far for doing drag and drop on views.

Hiding images turns out to be very easy. Check the onClick handler defined in the HideAndMove activity. It has a line that changes the visibility of the first image view. I had expected this to be more difficult because I had done a few web searches and found lots of cases where Android developers were having trouble hiding and showing views in their apps.

public void onClick(View v) {
  if (mV1 != null)
    mV1.setVisibility (View.INVISIBLE); // View.GONE hides and removes view from the layout.
}

I feel like I’ve made good progress. As I’ve said in my earlier posts in this blog, I don’t know if I’ve found the absolute best way to do things, and with this particular topic, I have so much more to learn about views. If anyone has suggestions or tips, please post a comment.

For Part 2 of this project, I am going to work on the following: (1) adding a background image on which the images move; (2) getting the images to follow the touch point as it moves; (3) and having the images not be clipped by the rectangle in which they are placed.

Source Code

Source code for this example is available in my Google Docs. Download the HideAndMove zip file.

If you find that the app does not build in Eclipse, be sure to do a clean build by using the “Clean” item on the Project menu.



Follow

Get every new post delivered to your Inbox.

Join 73 other followers