Я разработал простую мультитач-систему, которая отслеживает местоположение пальца и их статус на экране. Способ разработки программы, когда два пальца помещаются вниз, им присваивается номер, основанный на порядке размещения. Если палец один поднят, палец два по-прежнему называется пальцем два.
Этот код отлично работает в течение времени. Это означает, что я буду запускать свое приложение в процессе тестирования, а иногда и мультитач работает каждый раз, а иногда с использованием нескольких пальцев приложение полностью терпит неудачу. Он нерегулярно надежен.
Мне интересно, если это аппаратная ошибка или что-то вроде того, как я обрабатываю сенсорный ввод. Источник доступен ниже.
Спасибо за любую помощь, которую вы можете предложить!
-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();
}
}
}
Проблема решена путем отключения WidgetLocker. Похоже, что это приложение испортило некоторые программы. Я заметил проблемы в других приложениях, когда я блокировал экран во время игры.
Я выплю некоторые идеи, хотя мои навыки андроида немного не хватает...
Как вы это тестируете? Наверное, на реальном устройстве? Может ли это быть протестировано на эмуляторе? Похоже, он будет подвержен аппаратным проблемам.
Если есть сбой, и экран теряет следы пальцев даже на миллисекунде, не начнется ли это с индексации пальцев? Если это так, то это будет означать, что пальцы довольно случайным образом вызывают проблемы, которые вы испытываете.
Некоторые идеи:
MotionEvent.ACTION_POINTER_UP
- это событие, которое вы должны зарегистрировать. Фактически, я бы добавил журнал для каждого события, чтобы просто увидеть, что происходит.Думаю, вы смотрели пример мультитач в блоге разработчиков Android, чтобы убедиться, что вы правильно используете API?
Надеюсь, это поможет вам.