Android Multitouch - отслеживание нерегулярных пальцев

1

Я разработал простую мультитач-систему, которая отслеживает местоположение пальца и их статус на экране. Способ разработки программы, когда два пальца помещаются вниз, им присваивается номер, основанный на порядке размещения. Если палец один поднят, палец два по-прежнему называется пальцем два.

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

Мне интересно, если это аппаратная ошибка или что-то вроде того, как я обрабатываю сенсорный ввод. Источник доступен ниже.

Спасибо за любую помощь, которую вы можете предложить!

-Nathan Торнквист

EDIT: Я думаю, что проблема может быть связана с тем, что eventListeners зарегистрированы и незарегистрированы. Иногда приложение будет разбиваться и работать отлично, когда я его снова открою.

EDIT2: проблема заключается в том, насколько нерегулярна программа. Иногда, когда я открываю его, он отлично отслеживает пальцы. Первый палец помещается в номер 1, второй палец остается номером 2 и т.д. Независимо от того, поднимаете ли вы палец 1 после размещения 2, номера остаются назначенными. В других случаях вы помещаете два пальца, а палец - 1, а 2 - изменения. Приложение, похоже, теряет контроль над ними, и цифры переключаются, когда вы оставляете пальцы на экране.

EDIT3: Я попробовал "ускорители сенсорного экрана", чтобы попытаться настроить экран на правильную реакцию. Это не решило проблему.

EDIT4: после большего тестирования это явно ошибка кода. Когда приложение запускается, оно всегда работает отлично. После того, как устройство заблокировано (когда действие приостановлено), а затем разблокировано (когда действие возобновлено), мультитач перестает работать, и я получаю приложение, которое хорошо работает для одного касания и смущает мультитач.

MultitouchGameFixActivity.java

package com.nathantornquist.multitouchgame;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class MultiTouchGameFixActivity extends Activity{
    /** Called when the activity is first created. */
    MainGamePanel viewPanel;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //Window state functions.
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

        //This works without declaring a viewPanel instance here.
        //The instance declaration is needed to pass the 
        //onPause and onResume commands though.
        viewPanel = new MainGamePanel(this);
        setContentView(viewPanel);
    }

    //Restarts the accelerometer after onPause
    protected void onResume() {
        super.onResume();
        viewPanel.resume(this);

    }

    //Standard Method run when the Application loses focus.
    //This runs the pause() function in the viewPanel so that
    //the accelerometer can be paused.
    protected void onPause() {
        super.onPause();   
        viewPanel.pause();

    }

    protected void onDestroy() {
        super.onDestroy();
        viewPanel.destroy();
    }
}

MainThread.java

package com.nathantornquist.multitouchgame;

import com.nathantornquist.multitouchgame.MainGamePanel;
import android.graphics.Canvas;
import android.view.SurfaceHolder;

public class MainThread extends Thread {

private SurfaceHolder surfaceHolder;
private MainGamePanel gamePanel;
private boolean running;
public boolean pleaseWait = true;
public void setRunning(boolean running) {
    this.running = running;
}

public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
    super();
    this.surfaceHolder = surfaceHolder;
    this.gamePanel = gamePanel;
}

@Override
public void run() 
{
    Canvas canvas;
    while (running) {
        if(!pleaseWait) {
            canvas = null;
            // try locking the canvas for exclusive pixel editing on the surface
            try {
                canvas = this.surfaceHolder.lockCanvas();
                synchronized (surfaceHolder) {
                    // update game state
                    this.gamePanel.update();

                    // draws the canvas on the panel
                    this.gamePanel.onDraw(canvas);
                }
            } finally {
                // in case of an exception the surface is not left in
                // an inconsistent state
                if (canvas != null) {
                    surfaceHolder.unlockCanvasAndPost(canvas);
                }
            }   // end finally            
        }
        else {
            synchronized (this) {
                try {
                    wait();
                } catch (Exception e) { }
            }
        }
    }
}
}

MainGamePanel.java

package com.nathantornquist.multitouchgame;

import android.R.string;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MainGamePanel extends SurfaceView implements SensorEventListener, SurfaceHolder.Callback 
{
    //Variable Declarations.
    private MainThread thread;

    public int screenWidth;
    public int screenHeight;

    private SensorManager mSensorManager;
    private Sensor mAccelerometer;    

    Paint paint;
    public int fingerOneDown;
    public int fingerTwoDown;
    public int fingerThreeDown;
    public int fingerFourDown;
    public float fingerOneX;
    public float fingerOneY;
    public float fingerTwoX;
    public float fingerTwoY;
    public float fingerThreeX;
    public float fingerThreeY;
    public float fingerFourX;
    public float fingerFourY;

    public MainGamePanel(Context context)
    {
        super(context);

        getHolder().addCallback(this);

        thread = new MainThread(getHolder(),this);

        paint = new Paint();
        paint.setAntiAlias(true);

        Display display = ((Activity) context).getWindowManager().getDefaultDisplay(); 
        screenWidth = display.getWidth();
        screenHeight = display.getHeight();

        mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
        mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);

        fingerOneDown = 0;
        fingerTwoDown = 0;
        fingerThreeDown = 0;
        fingerFourDown = 0;
        fingerOneX = 0;
        fingerOneY = 0;
        fingerTwoX = 0;
        fingerTwoY = 0;
        fingerThreeX = 0;
        fingerThreeY = 0;
        fingerFourX = 0;
        fingerFourY = 0;

        setFocusable(true);

        thread.setRunning(true);
        thread.start();

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        //continue the thread
        synchronized (thread) {
            thread.pleaseWait = false;
            thread.notifyAll();
        }

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        //pause the thread
        synchronized (thread) {
            thread.pleaseWait = true;
        }

    }

    @Override
    public boolean onTouchEvent(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:
            Log.d("pointer id - down",Integer.toString(pointerId));
            if (pointerId == 0)
            {
                fingerOneDown = 1;
                fingerOneX = event.getX(pointerIndex);
                fingerOneY = event.getY(pointerIndex);
            }
            if (pointerId == 1)
            {
                fingerTwoDown = 1;
                fingerTwoX = event.getX(pointerIndex);
                fingerTwoY = event.getY(pointerIndex);
            }
            if(pointerId == 2)
            {
                fingerThreeDown = 1;
                fingerThreeX = event.getX(pointerIndex);
                fingerThreeY = event.getY(pointerIndex);
            }
            if(pointerId == 3)
            {
                fingerFourDown = 1;
                fingerFourX = event.getX(pointerIndex);
                fingerFourY = event.getY(pointerIndex);
            }
            break;

        case MotionEvent.ACTION_UP:          
        case MotionEvent.ACTION_POINTER_UP:
        case MotionEvent.ACTION_CANCEL:
            Log.d("pointer id - cancel",Integer.toString(pointerId));
            if (pointerId == 0)
            {
                fingerOneDown = 0;
                fingerOneX = event.getX(pointerIndex);
                fingerOneY = event.getY(pointerIndex);
            }
            if (pointerId == 1)
            {
                fingerTwoDown = 0;
                fingerTwoX = event.getX(pointerIndex);
                fingerTwoY = event.getY(pointerIndex);
            }
            if(pointerId == 2)
            {
                fingerThreeDown = 0;
                fingerThreeX = event.getX(pointerIndex);
                fingerThreeY = event.getY(pointerIndex);
            }
            if(pointerId == 3)
            {
                fingerFourDown = 0;
                fingerFourX = event.getX(pointerIndex);
                fingerFourY = 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);
                Log.d("pointer id - move",Integer.toString(pointerId));
                if(pointerId == 0)
                {
                    fingerOneDown = 1;
                    fingerOneX = event.getX(pointerIndex);
                    fingerOneY = event.getY(pointerIndex);
                }
                if(pointerId == 1)
                {
                    fingerTwoDown = 1;
                    fingerTwoX = event.getX(pointerIndex);
                    fingerTwoY = event.getY(pointerIndex);
                }
                if(pointerId == 2)
                {
                    fingerThreeDown = 1;
                    fingerThreeX = event.getX(pointerIndex);
                    fingerThreeY = event.getY(pointerIndex);
                }
                if(pointerId == 3)
                {
                    fingerFourDown = 1;
                    fingerFourX = event.getX(pointerIndex);
                    fingerFourY = event.getY(pointerIndex);
                }
            }
            break;
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) 
    {        
        paint.setColor(Color.WHITE); 
        paint.setStyle(Style.FILL); 
        canvas.drawPaint(paint); 

        if (fingerOneDown == 1)
        {
            paint.setColor(Color.BLUE); 
            paint.setTextSize(20); 
            canvas.drawText("1", fingerOneX, fingerOneY - 30, paint); 
        }

        if (fingerTwoDown == 1)
        {
            paint.setColor(Color.RED); 
            paint.setTextSize(20); 
            canvas.drawText("2", fingerTwoX, fingerTwoY - 30, paint); 
        }
        if (fingerThreeDown == 1)
        {
            paint.setColor(Color.GREEN); 
            paint.setTextSize(20); 
            canvas.drawText("3", fingerThreeX, fingerThreeY - 30, paint); 
        }
        if (fingerFourDown == 1)
        {
            paint.setColor(Color.BLACK); 
            paint.setTextSize(20); 
            canvas.drawText("4", fingerFourX, fingerFourY - 30, paint); 
        }
    }

    public void update() {
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {        
    }

    @Override
    public void onSensorChanged(SensorEvent event) {

    }

    public void pause() {
        mSensorManager.unregisterListener(this);
    }

    public void resume(Context context) {
        mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);      
    }

    public void destroy() {
        thread.setRunning(false);

        if (thread != null)
        {
            Thread killThread = thread;
            thread = null;
            killThread.interrupt();
        }   

    }


}
  • 0
    Было бы неплохо немного рассказать о том, что вы подразумеваете под «отказ приложения». Это сбой? Если это так, что такое трассировка стека? Разве это просто не перестает функционировать должным образом? Если да, то как?
  • 0
    Я добавил больше информации выше. Смотрите EDIT2. Я надеюсь, что это добавляет ясности.
Теги:
multi-touch
android-hardware

2 ответа

0

Проблема решена путем отключения WidgetLocker. Похоже, что это приложение испортило некоторые программы. Я заметил проблемы в других приложениях, когда я блокировал экран во время игры.

0

Я выплю некоторые идеи, хотя мои навыки андроида немного не хватает...

Как вы это тестируете? Наверное, на реальном устройстве? Может ли это быть протестировано на эмуляторе? Похоже, он будет подвержен аппаратным проблемам.

Если есть сбой, и экран теряет следы пальцев даже на миллисекунде, не начнется ли это с индексации пальцев? Если это так, то это будет означать, что пальцы довольно случайным образом вызывают проблемы, которые вы испытываете.

Некоторые идеи:

  • Не отслеживайте указатель пальцев, просто держите список пальцев и их относительные позиции. Но я думаю, что часть игры требует, чтобы вы указали указатель на каждый палец.
  • Запишите, если вы потеряете соединение с пальцем, который покажет, является ли это isse. Я думаю, что MotionEvent.ACTION_POINTER_UP - это событие, которое вы должны зарегистрировать. Фактически, я бы добавил журнал для каждого события, чтобы просто увидеть, что происходит.
  • Добавьте метод, который пытается определить, какой палец был помещен на экран, т.е. Он сравнивает текущее положение пальца с известными значениями предыдущих пальцев, и если он находится в пределах порога, предположим, что тот же самый палец. Возможно, хотите добавить таймер, чтобы "забыть" и предыдущие пальцы.

Думаю, вы смотрели пример мультитач в блоге разработчиков Android, чтобы убедиться, что вы правильно используете API?

Надеюсь, это поможет вам.

  • 0
    Нет способа проверить это с помощью эмулятора из-за мультитач-природы. Спасибо за ваш ответ, хотя. Самая сложная часть тестирования это знание того, что вызывает события. Я чувствую, что это ошибка кода, но любые странные ошибки, которые я генерирую, могут быть вызваны плохим кодом на my.part вместо неисправного оборудования. Это сложный вопрос.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню