One way to listen for touch events is to set a touch event listener using the following View method:
public void setOnTouchListener(View.OnTouchListener l)
This method works the same way as setOnClickListener(View.OnClickListener). You provide an implementation of View.OnTouchListener, and your listener will be called every time a touch event happens.
However, because you are subclassing View, you can take a shortcut and override this View method instead:
public boolean onTouchEvent(MotionEvent event)
This method receives an instance of MotionEvent, a class that describes the touch event, including its location and its action. The action describes the stage of the event:
Action constants | Description |
---|---|
ACTION_DOWN
|
user’s finger touches the screen |
ACTION_MOVE
|
user moves finger on the screen |
ACTION_UP
|
user lifts finger off the screen |
ACTION_CANCEL
|
a parent view has intercepted the touch event |
In your implementation of onTouchEvent(MotionEvent), you can check the value of the action by calling the MotionEvent method:
public final int getAction()
Let’s get to it. In BoxDrawingView.java, add a log tag and then an implementation of onTouchEvent(MotionEvent) that logs a message for each of the four different actions.
Listing 31.5 Implementing BoxDrawingView
(BoxDrawingView.java
)
public class BoxDrawingView extends View { private static final String TAG = "BoxDrawingView"; ... @Override public boolean onTouchEvent(MotionEvent event) { PointF current = new PointF(event.getX(), event.getY()); String action = ""; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: action = "ACTION_DOWN"; break; case MotionEvent.ACTION_MOVE: action = "ACTION_MOVE"; break; case MotionEvent.ACTION_UP: action = "ACTION_UP"; break; case MotionEvent.ACTION_CANCEL: action = "ACTION_CANCEL"; break; } Log.i(TAG, action + " at x=" + current.x + ", y=" + current.y); return true; } }
Notice that you package your X and Y coordinates in a PointF object. You want to pass these two values together as you go through the rest of the chapter. PointF is a container class provided by Android that does this for you.
Run DragAndDraw and pull up Logcat. Touch the screen and drag your finger. You should see a report of the X and Y coordinates of every touch action that BoxDrawingView receives.
BoxDrawingView is intended to draw boxes on the screen, not just log coordinates. There are a few problems to solve to get there.
First, to define a box, you need two points: the origin point (where the finger was initially placed) and the current point (where the finger currently is).
To define a box, then, requires keeping track of data from more than one MotionEvent. You will store this data in a Box object.
Create a class named Box to represent the data that defines a single box.
Listing 31.6 Adding Box
(Box.java
)
public class Box { private PointF mOrigin; private PointF mCurrent; public Box(PointF origin) { mOrigin = origin; mCurrent = origin; } public PointF getCurrent() { return mCurrent; } public void setCurrent(PointF current) { mCurrent = current; } public PointF getOrigin() { return mOrigin; } }
When the user touches BoxDrawingView, a new Box will be created and added to a list of existing boxes (Figure 31.4).
Back in BoxDrawingView, use your new Box object to track your drawing state.
Listing 31.7 Adding drag lifecycle methods (BoxDrawingView.java
)
public class BoxDrawingView extends View { private static final String TAG = "BoxDrawingView"; private Box mCurrentBox; private List<Box> mBoxen = new ArrayList<>(); ... @Override public boolean onTouchEvent(MotionEvent event) { PointF current = new PointF(event.getX(), event.getY()); String action = ""; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: action = "ACTION_DOWN"; // Reset drawing state mCurrentBox = new Box(current); mBoxen.add(mCurrentBox); break; case MotionEvent.ACTION_MOVE: action = "ACTION_MOVE"; if (mCurrentBox != null) { mCurrentBox.setCurrent(current); invalidate(); } break; case MotionEvent.ACTION_UP: action = "ACTION_UP"; mCurrentBox = null; break; case MotionEvent.ACTION_CANCEL: action = "ACTION_CANCEL"; mCurrentBox = null; break; } Log.i(TAG, action + " at x=" + current.x + ", y=" + current.y); return true; } }
Any time an ACTION_DOWN motion event is received, you set mCurrentBox
to be a new Box with its origin as the event’s location.
This new Box is added to the list of boxes. (In the next section, when you implement custom drawing, BoxDrawingView will draw every Box within this list to the screen.)
As the user’s finger moves around the screen, you update mCurrentBox.mCurrent
. Then, when the touch is canceled or when the user’s finger leaves the screen, you null out mCurrentBox
to end your draw motion. The Box is complete; it is stored safely in the list but will no longer be updated about motion events.
Notice the call to invalidate() in the case of ACTION_MOVE. This forces BoxDrawingView to redraw itself so that the user can see the box while dragging across the screen. Which brings you to the next step: drawing the boxes to the screen.
18.218.254.122