Handling Touch Events

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.

Tracking across motion events

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

Figure 31.4  Objects in DragAndDraw

Figure shows Objects in DragAndDraw.

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.254.122