Как правильно обрабатывать сенсорные события в Android? - PullRequest
4 голосов
/ 31 мая 2011

Масштаб проекта

Когда пользователь касается экрана Android двумя пальцами, нарисуйте «Рамку» в каждом месте касания с «курсором» для каждого кадра.Каждый кадр - это пользовательский слайдер, курсор которого будет перемещаться вверх и вниз.Вполне вверх будет 100%, средний будет 0%, а вниз будет -100%.Это будет использоваться для управления маленькими моторами, подобно повороту бака, каждое касание управляет отдельным мотором (посылая сигналы через Bluetooth).После двух касаний и все прорисовано, я хочу иметь возможность оторвать любой палец, НО держать курсор в том месте, где он был в последний раз, в то время как другой палец может свободно перемещать курсор.Когда последний палец снят, все «прячется» и сбрасывается до 0%.

Требуется функциональность

  1. Нарисуйте двумя пальцами отдельные .pngsпод сенсорным местоположением
  2. После отрисовки рамок и курсоров отслеживайте их расположение относительно рамки, чтобы определить процентное соотношение.
  3. Если палец оторван, удерживайте курсор на пальцахв последнем известном месте, но другой палец может перемещать курсор.Также, если палец опустить вниз, он сможет снова переместить курсор.
  4. Если оба пальца оторваны от экрана, спрячьте все и сбросьте проценты до 0%

Полученные функциональные возможности

  • Я могу рисовать рамки и курсоры на мультитач
  • Позиции и проценты работают нормально
  • Курсоры перемещаются правильно

Что не работает

  • Я не уверен, должен ли я иметь один пользовательский класс, который обрабатывает оба события касания, или если у меня должно быть 2 экземпляракаждый пользовательский класс обрабатывает свои собственные сенсорные события (я пробовал оба, единственный способ получить «реальную» функциональность - это 1 пользовательский класс, обрабатывающий оба сенсорных события, другой способ не работает должным образом)
  • Когда у меня есть только 1 пользовательский класс, он отлично работает, но он у меня «скрывает» все, если оба пальца не на экране, и иногда Android регистрирует, что я убрал палец с экрана, и это вызываетЕсть много проблем, когда кадры скрываются, а затем появляются в другом месте.
  • Когда я использую 2 пользовательских класса, я прикасаюсь, что у каждого пользовательского класса будет свое собственное событие касания, и мне не придется беспокоиться о мультитач, еслия равномерно разделил классы между экранами.Это был не тот случай, все равно нужно разобраться с мультитачем

Может кто-нибудь объяснить мне, как андроид обрабатывает свои сенсорные события.из того, что я сделал, кажется, если я положу палец 1, палец 2, первый палец зарегистрирует «ACTION_DOWN», а второй зарегистрирует «ACTION_POINTER_2_DOWN», НО, если я буду жить от моего первого пальца, моего второго пальца«понижен в должности», и теперь все события, которые регистрирует мой второй палец, не связаны с «ACTION_POINTER_2» и вместо этого будут «ACTION_DOWN, ACTION_UP и т. д.».Это правильно?

TouchUI.java

    package com.robota.android;

    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.widget.ImageView;

public class TouchUI extends ImageView {

public static final String LEFT_TOUCHUI = "com.robota.android:id/leftTouchUI";
public static final String RIGHT_TOUCHUI = "com.robota.android:id/rightTouchUI";
private String whoAmI = new String();
private MyPoints framePts = new MyPoints();
private MyPoints cursorPts = new MyPoints();
private Bitmap frame;
private Bitmap cursor;
private int frameWidth;
private int frameHeight;
private int cursorHeight;
private boolean pointerDown = false;
private int dy;

public TouchUI(final Context context, final AttributeSet as){
    super(context, as);
    Log.d("TouchUI", getResources().getResourceName(this.getId()));
    whoAmI = new String(getResources().getResourceName(this.getId()));
    if(whoAmI.equals(LEFT_TOUCHUI)){
        frame = BitmapFactory.decodeResource(getResources(), R.drawable.tank_left);
    }else if(whoAmI.equals(RIGHT_TOUCHUI)){
        frame = BitmapFactory.decodeResource(getResources(), R.drawable.tank_right);
    }
    cursor = BitmapFactory.decodeResource(getResources(), R.drawable.cursor);
    frameWidth = frame.getWidth();
    frameHeight = frame.getHeight();
    cursorHeight = cursor.getHeight();
}

public void determinePointers(int x, int y){
        framePts.setOrigin(x-frameWidth/2, y-frameHeight/2);
        cursorPts.setOrigin(x-frameWidth/2, y-frameHeight/2);
}

@Override
public boolean onTouchEvent(MotionEvent e){
    int x = 0;
    int y = 0;
    Log.d("TouchUI", ">>>>> " + whoAmI);
    if(e.getAction() == MotionEvent.ACTION_DOWN){
        determinePointers(x,y);
        pointerDown = true;
    }else if(e.getAction() == MotionEvent.ACTION_UP){
        pointerDown = false;
    }else if(e.getAction() == MotionEvent.ACTION_MOVE){
        dy = (int)e.getY()-framePts.getY();
        if(dy <= 0){
            dy=0;
        }else if(dy+cursorHeight/2 >= frameHeight){
            dy=frameHeight;
        }
        sendMotorSpeed(dy);
    }
    return true;
}

public void sendMotorSpeed(int dy){
    float motor = dy;
    motor-=frameHeight;
    motor*=-1;

    motor = (motor/frameHeight)*255;

    PacketController.updateMotorSpeeds(whoAmI, (int)motor);
}

public void onDraw(Canvas canvas){
    if(pointerDown){//twoDown){
        canvas.drawBitmap(frame, framePts.getX(), framePts.getY(), null);
        canvas.drawBitmap(cursor, cursorPts.getX(), (cursorPts.getY()+dy), null);
    }
    invalidate();
}

private class MyPoints{

    private int x = -100;
    private int y = -100;
    private int deltaY = 0;;

    public MyPoints(){
        this.x = 0;
        this.y = 0;
    }

    public int getX(){
        return this.x;
    }

    public int getY(){
        return this.y;
    }

    public void setOrigin(int x, int y){
        this.x = x;
        this.y = y;
    }

    public int getDeltaY(){
        return deltaY;
    }

    public void setDeltaY(int newY){
        deltaY = (newY-y);
        Log.d("TouchUI", "DY: " + deltaY);
    }
}
}

Main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/parentLayout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<LinearLayout android:orientation="horizontal"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <com.robota.android.TouchUI xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/leftTouchUI"
        android:background="#0000"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:layout_weight="1">
    </com.robota.android.TouchUI>
    <com.robota.android.TouchUI xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/rightTouchUI"
        android:background="#0000"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:layout_weight="1">
    </com.robota.android.TouchUI>
</LinearLayout>

RobotController.java (основной класс активности)

    package com.robota.android;

    import android.app.Activity;
    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothDevice;
    import android.content.ActivityNotFoundException;
    import android.content.Intent;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.util.Log;
    import android.view.Menu;
    import android.view.MenuInflater;
    import android.view.MenuItem;
    import android.view.View;
    import android.view.Window;
    import android.widget.ScrollView;
    import android.widget.TextView;
    import android.widget.Toast;

public class RobotController extends Activity {
// Tag used to keep track of class in the Log
private static final String TAG = "robotController_new";
// Boolean to debugging
private static final boolean D = true;

// Intent request codes
private static final int DISCONNECT_DEVICE = 1;
private static final int CONNECT_DEVICE = 2;
private static final int REQUEST_ENABLE_BT = 3;

// Handler Codes
public static final int MESSAGE_READ = 1;
public static final int MESSAGE_WRITE = 2;

// Local Bluetooth Adapter
private BluetoothAdapter bluetoothAdapter = null;
// Bluetooth Discovery and Datahandler
private BluetoothComm btComm = null;

// Debug's TextView, this is where strings will be written to display
private TextView tv;
private ScrollView sv;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    if(D) Log.d(TAG, "++ON CREATE++");
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.main);

    bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

    if(bluetoothAdapter == null){
        if(D) Log.d(TAG, "NO BLUETOOTH DEVICE");
        Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_SHORT).show();
        finish();
        return;
    }

    PacketController.controller = this;
}


public void onStart(){
    super.onStart();
    if(D) Log.d(TAG, "++ON START++");

    if(!bluetoothAdapter.isEnabled()){
        Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
    }else{
        // Start BluetoothComm
        if(btComm == null){
            setupComm();
        }
    }
}

/**
 * Creates new Bluetooth Communication
 */
private void setupComm(){
    if(D) Log.d(TAG, "+++setupComm+++");
    btComm = new BluetoothComm(this, handler);
}

private void connectDevice(Intent data){
    if(D) Log.d(TAG, "+++connectDevice+++");
    String addr = data.getExtras()
        .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
    BluetoothDevice device = bluetoothAdapter.getRemoteDevice(addr);
    if(D) Log.d(TAG,"REMOTE ADDR: "+ addr);
    btComm.connect(device);
}

private void disconnectDevice(){
    if(D) Log.d(TAG, "---disconnectDevice---");
    if(btComm.getState() == btComm.STATE_CONNECTED){
        btComm.disconnect();
    }
}

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
    //super.onCreateOptionsMenu(menu);
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.menu, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    Intent serverIntent = null;
    switch(item.getItemId()){       
    case R.id.insecure_connect_scan:
        // Launch the DeviceListActivity to see devices and do scan
        serverIntent = new Intent(this, DeviceListActivity.class);
        try{
            startActivityForResult(serverIntent, CONNECT_DEVICE);
        }catch(ActivityNotFoundException activityNotFound){
            Log.e(TAG, "Could not start DeviceListActivity(Insecure)");
        }
        return true;
    }
    return false;
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
    switch(requestCode){
    case CONNECT_DEVICE:
        if(resultCode == Activity.RESULT_OK){
            connectDevice(data);
        }
        break;
    case DISCONNECT_DEVICE:
        if(resultCode == Activity.RESULT_OK){
            disconnectDevice();
        }
        break;
    }
}

public Handler getHandler(){
    return this.handler;
}

public BluetoothComm getBtComm(){
    return this.btComm;
}

// The Handler that gets information back from the BluetoothChatService
private final Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        if(D) Log.d(TAG, "check message");
        switch (msg.what) {
        case MESSAGE_READ:
            if(D) Log.d(TAG, "trying to read message");
            byte[] readBuf = (byte[]) msg.obj;
            // construct a string from the valid bytes in the buffer
            String readMessage = new String(readBuf, 0, msg.arg1);
            if(D) Log.d(TAG, "bytes: " + readBuf + " arg1: " + msg.arg1 + " Message: " + readMessage);
            tv.append(readMessage);
            break;
        case MESSAGE_WRITE:
            if(D) Log.d(TAG, "trying to send message");
            String sendMessage = new String(String.valueOf(msg.obj));
        }
    }
};
}

Любые другие классы, не указанные в списке, я не считаю нужными, но если они необходимы, пожалуйста, дайте мне знать.

Любая помощь очень ценится

1 Ответ

3 голосов
/ 31 мая 2011

Вам нужно будет сохранить pointerId для каждой точки и сравнить их с новым Id, данным для каждого MotionEvent. Это немного сложно объяснить, поэтому я укажу вам на это Сообщение АБР , которое объясняет это гораздо лучше, чем я мог. Короче? Мультитач может быть хитрым, но это не так плохо, как кажется на первый взгляд.

...