• No se han encontrado resultados

3.2.1.6 LOS LIBROS VII, VIII Y IX: LA TEORIA DE LOS NUMEROS.

In document ESCUELA DE ATENAS (página 53-55)

3 EUCLIDES Y APOLONIO 3.1 INTRODUCCIÓN.

3.2.1.6 LOS LIBROS VII, VIII Y IX: LA TEORIA DE LOS NUMEROS.

Next we have to get the pure event type minus the additional pointer index that is

encoded in the integer returned by MotionEvent.getAction(). We just need to mask the

pointer index out:

int action = event.getAction() & MotionEvent.ACTION_MASK;

OK, that was easy. Sadly you’ll only understand it if you know what that pointer index is and that it is actually encoded in the action.

What’s left is to decode the event type as we did before. I already said that there are a few new event types, so let’s go through them:

MotionEvent.ACTION_POINTER_DOWN: This event happens for any additional finger that touches the screen after the first finger touches. The first finger will still produce a MotionEvent.ACTION_DOWN event.

MotionEvent.ACTION_POINTER_UP: This is analogous the previous action. This gets fired when a finger is lifted up from the screen and more than one finger is touching the screen. The last finger on the screen to go up will produce a

MotionEvent.ACTION_UP event. This finger doesn’t necessarily have to be the first finger that touched the screen.

Luckily we can just pretend that those two new event types are the same as the old MotionEvent.ACTION_UP and MotionEvent.ACTION_DOWN events.

The last difference is the fact that a single MotionEvent can have data for multiple

events. Yes, you read that right. For this to happen, the merged events have to have the

same type. In reality this will only happen for the MotionEvent.ACTION_MOVE event, so we

only have to deal with this fact when processing said event type. To check how many

events are contained in a single MotionEvent, we use the

MotionEvent.getPointerCount() method, which tells us for how many fingers the MotionEvent contains coordinates for. We then can fetch the pointer identifier and

coordinates for the pointer indices 0 to MotionEvent.getPointerCount() – 1 via the

MotionEvent.getX(), MotionEvent.getY(), and MotionEvent.getPointerId() methods.

In Practice

Let’s write an example for this fine API. We want to keep track of ten fingers at most (there’s no device yet that can track more, so we are on the safe side here). Android will assign pointer identifiers from 0 to 9 to these fingers in the sequence they touch the screen. So we keep track of each pointer identifier’s coordinates and touch state

CHAPTER 4: Android for Game Developers

134

(touching or not), and output this information to the screen via a TextView. Let’s call our

test activity MultiTouchTest. Listing 4–4 shows the complete code.

Listing 4–4. MultiTouchTest.java; Testing the Multitouch API package com.badlogic.androidgames; import android.app.Activity; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.widget.TextView;

public class MultiTouchTest extends Activity implements OnTouchListener { StringBuilder builder = new StringBuilder();

TextView textView;

float[] x = new float[10]; float[] y = new float[10];

boolean[] touched = new boolean[10]; private void updateTextView() { builder.setLength(0);

for(int i = 0; i < 10; i++) { builder.append(touched[i]); builder.append(", "); builder.append(x[i]); builder.append(", "); builder.append(y[i]); builder.append("\n"); } textView.setText(builder.toString()); }

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

textView = new TextView(this);

textView.setText("Touch and drag (multiple fingers supported)!"); textView.setOnTouchListener(this);

setContentView(textView); }

@Override

public boolean onTouch(View v, MotionEvent event) {

int action = event.getAction() & MotionEvent.ACTION_MASK;

int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;

int pointerId = event.getPointerId(pointerIndex); switch (action) {

case MotionEvent.ACTION_DOWN:

case MotionEvent.ACTION_POINTER_DOWN: touched[pointerId] = true;

x[pointerId] = (int)event.getX(pointerIndex); y[pointerId] = (int)event.getY(pointerIndex); break;

case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_CANCEL: touched[pointerId] = false;

x[pointerId] = (int)event.getX(pointerIndex); y[pointerId] = (int)event.getY(pointerIndex); break;

case MotionEvent.ACTION_MOVE:

int pointerCount = event.getPointerCount(); for (int i = 0; i < pointerCount; i++) { pointerIndex = i;

pointerId = event.getPointerId(pointerIndex); x[pointerId] = (int)event.getX(pointerIndex); y[pointerId] = (int)event.getY(pointerIndex); } break; } updateTextView(); return true; } }

We implement the OnTouchListener interface as before. To keep track of the coordinates

and touch state of the ten fingers, we add three new member arrays that will hold that

information for us. The arrays x and y hold the coordinates for each pointer ID, and the

array touched stores whether the finger with that pointer ID is down or not.

Next, I took the freedom to create a little helper method that will output the current state of the fingers to the TextView. It simply iterates through all the ten finger states and

concatenates them via a StringBuilder. The final text is set to the TextView.

The onCreate() method sets up our activity and registers it as an OnTouchListener with the TextView. We already know that part by heart.

Now for the scary part: the onTouch() method. We start off by getting the event type by

masking the integer returned by event.getAction(). Next we extract the pointer index

and fetch the corresponding pointer identifier from the MotionEvent, as discussed

earlier.

The heart of the onTouch() method is that big nasty switch statement, which we already

used in a reduced form to process single-touch events. We group all the events into three categories on a high level:

A touch-down event happened (MotionEvent.ACTION_DOWN,

MotionEvent.ACTION_PONTER_DOWN). We set the touch state for the

pointer identifier to true, and also save the current coordinates of that

CHAPTER 4: Android for Game Developers

136

A touch-up event happened (MotionEvent.ACTION_UP,

MotionEvent.ACTION_POINTER_UP, MotionEvent.CANCEL). We set the

touch state to false for that pointer identifier and save its last known

coordinates.

One or more fingers were dragged across the screen

(MotionEvent.ACTION_MOVE). We check how many events are contained

in the MotionEvent and then update the coordinates for the pointer

indices 0 to MotionEvent.getPointerCount() - 1. For each event, we

fetch the corresponding pointer identifier and update the coordinates. Once the event is processed, we update the TextView via a call to the updateView()

method we defined earlier. Finally we return true, indicating that we processed the

touch event.

Figure 4–7 shows the output of the activity after I touch two fingers on my Nexus One and drag them around a little.

There are a few things we can observe when we run this example:

If we start it on a device or emulator with an Android version lower than 2.0, we get a nasty exception, as we’re use an API that is not available on those earlier versions. We can work around that by determining which Android version the application is running on, using the single-touch code on devices with Android 1.5 and 1.6, and using the multitouch code on devices with Android 2.0 or newer. We’ll get back to that in the next chapter.

There’s no multitouch on the emulator. The API is there if we create an emulator running Android version 2.0 or higher, but we only have a single mouse. And even if we had two mice, it wouldn’t make a difference.

Touch two fingers down, lift the first one, and touch it down again. The second finger will keep its pointer identifier after the first finger is lifted. When the first finger is touched down for the second time, it gets the first free pointer identifier, which is 0 in this case. Any new finger that touches the screen will get the first free pointer identifier. That’s a rule to remember.

If you try this on a Nexus One or a Droid, you will notice some strange behavior when your cross two fingers on one axis. This is due to the fact that the screens of those devices do not fully support the tracking of individual fingers. It’s a big problem, but we can work around it somewhat by designing our UIs with some care. We’ll have another look at the issue in a later chapter. The phrase to keep in mind is don’t cross the streams!

And that’s how multitouch processing works on Android. It is a pain in the buttocks, but once you untangle all the terminology and come to peace with the bit twiddling, it becomes somewhat OK to use.

NOTE: I’m sorry if this made your head explode. This section was rather heavy duty. Sadly, the official documentation for the API is extremely lacking, and most people “learn” the API by simply hacking away at it. I suggest you play around with the preceding code example until you fully grasp what’s going on.

In document ESCUELA DE ATENAS (página 53-55)