moving ball using ACCELEROMETER sensor is lagging...why?

logicallayer
logicallayer used Ask the Experts™
on
Hi,

I'm somehow new to android as I'm trying to make a very simple game that relys on moving the ball or circle based on the tilting of the cell phone, the movement works good maybe there is a room for improvement to get better accuracy but doesn't really concerns me for now

my problem is that the circle lags while it's moving, strangely I know I played alot of games before that are similar on my phone and it suppose to be able to handle such a simple game

I would guess that my problem is the way I'm listening to the sensor that is causing the lag but I don't know how to fix, can someone give some hints please?

there are some extra texts in the screen as I was doing some testing with them feel free to ignore them...
package org.example.sensortesty;

import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;



public class sensorTesty extends Activity implements SensorEventListener {
	public float x  ;
	public float y ;
	public float z;
	public int r;
	 float sensX = 0;
	 float sensY = 0;
	 float sensZ = 0;
	 
	 float x_ax = 90;
	 float y_ax = 90;

	
	/** Called when the activity is first created. */
	SensorManager sensorManager = null;

	// for accelerometer values
	TextView outputX;

	TextView outputY;
	TextView outputZ;

	// for orientation values

	TextView outputX2;
	TextView outputY2;
	TextView outputZ2;
	
	
	
		
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
		setContentView(R.layout.main);
		setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

	//	FrameLayout main = (FrameLayout) findViewById(R.id.main_view); 
		// main.addView(new Ball(this,50,50,25));
	}

		// just some for data output

		
	
	
	@Override
	protected void onResume() {
		super.onResume();
		sensorManager.registerListener(this,
				sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
				SensorManager.SENSOR_DELAY_GAME);

		sensorManager.registerListener(this,
				sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
				SensorManager.SENSOR_ORIENTATION);
	}

	@Override
	protected void onStop() {
		super.onStop();
		sensorManager.unregisterListener(this,
				sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER));
		sensorManager.unregisterListener(this,
				sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION));
	}

	
	public void onSensorChanged(SensorEvent event) {
		 synchronized (this) {
			switch (event.sensor.getType()) {
			case Sensor.TYPE_ACCELEROMETER:
				outputX = (TextView) findViewById(R.id.TextView01);	
				outputY = (TextView) findViewById(R.id.TextView02);
				outputZ = (TextView) findViewById(R.id.TextView03);

				outputX2 = (TextView) findViewById(R.id.TextView04);
				outputY2 = (TextView) findViewById(R.id.TextView05);
				outputZ2 = (TextView) findViewById(R.id.TextView06);

				 x = event.values[0];
                 y = event.values[1];
                 z = event.values[2];
                 if(z>2){
                 
                
                	 outputX.setText("Face Up");
                    
                	
                 }else if(z<-2){
                     outputX.setText("Face Down");
                	 
                 
                 }
                 if(x>1){
                     
                outputX.setText("Left Side");
                
                if(x_ax > 20)
                	 x_ax = x_ax - 10;
                 
                 }else if(x<-1){
                     outputX.setText("Right Side");
                	 if(x_ax < 300)
                     x_ax = x_ax + 10;
                 }
                 
                 if(y>1){
                	 
                     outputX.setText("Standing Up");
                     if(y_ax < 370)
                     y_ax = y_ax + 10;
        
                 }
                 else if(y<-1){
                    
                	 outputX.setText("On Head");
                	 
                	 if(y_ax > 20)
                	 y_ax = y_ax - 10;
                	 
                 }else{
                     outputX.setText("In Between");
                 }
				/*
			if( event.values[0] > 9)
			{
			sensX = event.values[0];
			x = x + 10;	
			}
			
			else if(event.values[0] < -9)
			{
			sensX = event.values[0];
			x = x - 10;
			}
			
			if(event.values[1] > 9)
			{
				sensY = event.values[1];
				y = y + 10;
			}
			
			else if( event.values[1] < -9)
			{
				sensY = event.values[1];
				y = y - 10;
			}
			

			if(sensZ > event.values[2])
			{
				sensZ = event.values[2];
				z = z + 10;
			}
			
			else if(sensZ < event.values[2])
			{
				sensZ = event.values[2];
				z = z - 10;
			}
*/
			
				
			FrameLayout main = (FrameLayout) findViewById(R.id.main_view); 
			main.removeAllViews();
			 main.addView(new Ball(this,x_ax,y_ax,25));
			
			
			
				//circle.moveTo(x, y);
			//main.addView(new Ball(this,40,40,25));
				break;

			case Sensor.TYPE_ORIENTATION:
				//x = event.values[0];
			//	y = event.values[1];
				
				//circle.moveTo(x, y);
				//outputX2.setText("x:" + Float.toString(event.values[0]));
				//outputY2.setText("y:" + Float.toString(event.values[1]));
				//outputZ2.setText("z:" + Float.toString(event.values[2]));
				break;
			}
		}
	}

	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {
		// TODO Auto-generated method stub

	}
	
}

Open in new window

package org.example.sensortesty;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;

public class Ball extends View {

	 private float x;
	    private float y;
	    private int r;
	    Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
	    
	    public Ball(Context context, float x, float y, int r) {
	        super(context);
	        mPaint.setColor(0xFFFF0000);
	        this.x = x;
	        this.y = y;
	        this.r = r;
}

		@Override
		protected void onDraw(Canvas canvas) {
		
				super.onDraw(canvas);
			   
			    canvas.drawCircle(x, y, r,mPaint);	
		}
		
}

Open in new window

Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Developer
Commented:
To get a good animation you should consider to avoid rebuilding the entire view in every sensor change event
and use some buffering strategy (writing to an Image canvas) and keeping only one Ball object and change only the position attributes.

Author

Commented:
I tried to use postinvalidate() to enforce onDraw again after the cords been changed but it doesn't work

can you take a look at the adjusted code please
package org.example.sensortesty;

import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;



public class sensorTesty extends Activity implements SensorEventListener {
	public float x  ;
	public float y ;
	public float z;
	public int r;
	 float sensX = 0;
	 float sensY = 0;
	 float sensZ = 0;
	 
	 float x_ax = 90;
	 float y_ax = 90;

	
	/** Called when the activity is first created. */
	SensorManager sensorManager = null;

	// for accelerometer values
	TextView outputX;

	TextView outputY;
	TextView outputZ;

	// for orientation values

	TextView outputX2;
	TextView outputY2;
	TextView outputZ2;
	FrameLayout main;
	
	
		
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
		setContentView(R.layout.main);
		setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

		
		main = (FrameLayout) findViewById(R.id.main_view); 
		 main.addView(new Ball(this,x_ax,y_ax,25));
	}

		// just some for data output

		
	
	
	@Override
	protected void onResume() {
		super.onResume();
		sensorManager.registerListener(this,
				sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
				SensorManager.SENSOR_DELAY_GAME);

		sensorManager.registerListener(this,
				sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
				SensorManager.SENSOR_ORIENTATION);
	}

	@Override
	protected void onStop() {
		super.onStop();
		sensorManager.unregisterListener(this,
				sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER));
		sensorManager.unregisterListener(this,
				sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION));
	}

	
	public void onSensorChanged(SensorEvent event) {
		 synchronized (this) {
			switch (event.sensor.getType()) {
			case Sensor.TYPE_ACCELEROMETER:
				outputX = (TextView) findViewById(R.id.TextView01);	
				outputY = (TextView) findViewById(R.id.TextView02);
				outputZ = (TextView) findViewById(R.id.TextView03);

				outputX2 = (TextView) findViewById(R.id.TextView04);
				outputY2 = (TextView) findViewById(R.id.TextView05);
				outputZ2 = (TextView) findViewById(R.id.TextView06);

				 x = event.values[0];
                 y = event.values[1];
                 z = event.values[2];
                 if(z>2){
                 
                
                	 outputX.setText("Face Up");
                    
                	
                 }else if(z<-2){
                     outputX.setText("Face Down");
                	 
                 
                 }
                 if(x>1){
                     
                outputX.setText("Left Side");
                
                if(x_ax > 20)
                	 x_ax = x_ax - 10;
                 
                 }else if(x<-1){
                     outputX.setText("Right Side");
                	 if(x_ax < 300)
                     x_ax = x_ax + 10;
                 }
                 
                 if(y>1){
                	 
                     outputX.setText("Standing Up");
                     if(y_ax < 370)
                     y_ax = y_ax + 10;
        
                 }
                 else if(y<-1){
                    
                	 outputX.setText("On Head");
                	 
                	 if(y_ax > 20)
                	 y_ax = y_ax - 10;
                	 
                 }else{
                     outputX.setText("In Between");
                 }
				/*
			if( event.values[0] > 9)
			{
			sensX = event.values[0];
			x = x + 10;	
			}
			
			else if(event.values[0] < -9)
			{
			sensX = event.values[0];
			x = x - 10;
			}
			
			if(event.values[1] > 9)
			{
				sensY = event.values[1];
				y = y + 10;
			}
			
			else if( event.values[1] < -9)
			{
				sensY = event.values[1];
				y = y - 10;
			}
			

			if(sensZ > event.values[2])
			{
				sensZ = event.values[2];
				z = z + 10;
			}
			
			else if(sensZ < event.values[2])
			{
				sensZ = event.values[2];
				z = z - 10;
			}
*/
			
				
                 main.postInvalidate();
			//FrameLayout main = (FrameLayout) findViewById(R.id.main_view); 
			//main.removeAllViews();
			
			 //main.addView(new Ball(this,x_ax,y_ax,25));
			
			
			
			
				//circle.moveTo(x, y);
			//main.addView(new Ball(this,40,40,25));
				break;

			case Sensor.TYPE_ORIENTATION:
				//x = event.values[0];
			//	y = event.values[1];
				
				//circle.moveTo(x, y);
				//outputX2.setText("x:" + Float.toString(event.values[0]));
				//outputY2.setText("y:" + Float.toString(event.values[1]));
				//outputZ2.setText("z:" + Float.toString(event.values[2]));
				break;
			}
		}
	}

	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {
		// TODO Auto-generated method stub

	}
	
}

Open in new window

Author

Commented:
main.postInvalidate();
Announcing the Winners!

The results are in for the 15th Annual Expert Awards! Congratulations to the winners, and thank you to everyone who participated in the nominations. We are so grateful for the valuable contributions experts make on a daily basis. Click to read more about this year’s recipients!

F IgorDeveloper
Commented:
You have disabled the "ball" creation and there is not in the view to display it.

                  //FrameLayout main = (FrameLayout) findViewById(R.id.main_view);
                  //main.removeAllViews();
                  
                   //main.addView(new Ball(this,x_ax,y_ax,25));

instead, you can create a fist "Ball" view in the "OnCreate" event:

//class variable "ball"
Ball ball;

      @Override
      public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            
            sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
            setContentView(R.layout.main);
            setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

            FrameLayout main = (FrameLayout) findViewById(R.id.main_view);
                                ball=new Ball(this,50,50,25);// set an initial position

             main.addView(ball);       
}

and only update the x,y position of the ball using some new method in Ball  

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

and call it in every sensor change as:

ball.setPosition(x_ax,y_ax); //update draw position
ball.invalidate();  //invalidate the view => call onDraw() on the Ball object.


Maybe you can try to disable the TextView updates to check if these update are causing lags.

Author

Commented:
actually I got main.invalidate() to work as I found out that im recalling onDraw only with invalidate and the variable Im passing to onDraw changes only if I recall the object so I what I did changed x_ax and y_ax to public variables..

check the code if you want,

but still the circle is lagging even more than before this time Im not creating a new object everytime and still suffering from massive lag


package org.example.sensortesty;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;

public class Ball extends View {

	 //private float x;
	   // private float y;
	    private int r;
	    Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
	    
	    public Ball(Context context, float x, float y, int r) {
	        super(context);
	        mPaint.setColor(0xFFFF0000);
	        sensorTesty.x_ax = x;
	        sensorTesty.y_ax = y;
	        this.r = r;
	 
}

		@Override
		protected void onDraw(Canvas canvas) {
		
				super.onDraw(canvas);
			   
			    canvas.drawCircle(sensorTesty.x_ax, sensorTesty.y_ax, r,mPaint);	
		}
		
}

Open in new window

package org.example.sensortesty;

import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;



public class sensorTesty extends Activity implements SensorEventListener {
	public float x  ;
	public float y ;
	public float z;
	public int r;
	 float sensX = 0;
	 float sensY = 0;
	 float sensZ = 0;
	 
	public static float x_ax = 90;
	 public static float y_ax = 90;

	
	/** Called when the activity is first created. */
	SensorManager sensorManager = null;

	// for accelerometer values
	TextView outputX;

	TextView outputY;
	TextView outputZ;

	// for orientation values

	TextView outputX2;
	TextView outputY2;
	TextView outputZ2;
	FrameLayout main;
	
	
		
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
		setContentView(R.layout.main);
		setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

		
		main = (FrameLayout) findViewById(R.id.main_view); 
		 main.addView(new Ball(this,x_ax,y_ax,25));
		 
		 x_ax = 300;
		 y_ax = 300;
		 
		 main.invalidate();
	}

		// just some for data output

		
	
	
	@Override
	protected void onResume() {
		super.onResume();
		sensorManager.registerListener(this,
				sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
				SensorManager.SENSOR_DELAY_GAME);

		sensorManager.registerListener(this,
				sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
				SensorManager.SENSOR_ORIENTATION);
	}

	@Override
	protected void onStop() {
		super.onStop();
		sensorManager.unregisterListener(this,
				sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER));
		sensorManager.unregisterListener(this,
				sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION));
	}

	
	public void onSensorChanged(SensorEvent event) {
		 synchronized (this) {
			switch (event.sensor.getType()) {
			case Sensor.TYPE_ACCELEROMETER:
				outputX = (TextView) findViewById(R.id.TextView01);	
				outputY = (TextView) findViewById(R.id.TextView02);
				outputZ = (TextView) findViewById(R.id.TextView03);

				outputX2 = (TextView) findViewById(R.id.TextView04);
				outputY2 = (TextView) findViewById(R.id.TextView05);
				outputZ2 = (TextView) findViewById(R.id.TextView06);

				 x = event.values[0];
                 y = event.values[1];
                 z = event.values[2];
                 if(z>2){
                 
                
                	 outputX.setText("Face Up");
                    
                	
                 }else if(z<-2){
                     outputX.setText("Face Down");
                	 
                 
                 }
                 if(x>1){
                     
                outputX.setText("Left Side");
                
                if(x_ax > 20)
                	 x_ax = x_ax - 10;
                 
                 }else if(x<-1){
                     outputX.setText("Right Side");
                	 if(x_ax < 300)
                     x_ax = x_ax + 10;
                 }
                 
                 if(y>1){
                	 
                     outputX.setText("Standing Up");
                     if(y_ax < 370)
                     y_ax = y_ax + 10;
        
                 }
                 else if(y<-1){
                    
                	 outputX.setText("On Head");
                	 
                	 if(y_ax > 20)
                	 y_ax = y_ax - 10;
                	 
                 }else{
                     outputX.setText("In Between");
                 }
				/*
			if( event.values[0] > 9)
			{
			sensX = event.values[0];
			x = x + 10;	
			}
			
			else if(event.values[0] < -9)
			{
			sensX = event.values[0];
			x = x - 10;
			}
			
			if(event.values[1] > 9)
			{
				sensY = event.values[1];
				y = y + 10;
			}
			
			else if( event.values[1] < -9)
			{
				sensY = event.values[1];
				y = y - 10;
			}
			

			if(sensZ > event.values[2])
			{
				sensZ = event.values[2];
				z = z + 10;
			}
			
			else if(sensZ < event.values[2])
			{
				sensZ = event.values[2];
				z = z - 10;
			}
*/
			
				
                 main.invalidate();
			//FrameLayout main = (FrameLayout) findViewById(R.id.main_view); 
			//main.removeAllViews();
			
			 //main.addView(new Ball(this,x_ax,y_ax,25));
			
			
			
			
				//circle.moveTo(x, y);
			//main.addView(new Ball(this,40,40,25));
				break;

			case Sensor.TYPE_ORIENTATION:
				//x = event.values[0];
			//	y = event.values[1];
				
				//circle.moveTo(x, y);
				//outputX2.setText("x:" + Float.toString(event.values[0]));
				//outputY2.setText("y:" + Float.toString(event.values[1]));
				//outputZ2.setText("z:" + Float.toString(event.values[2]));
				break;
			}
		}
	}

	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) {
		// TODO Auto-generated method stub

	}
	
}

Open in new window

Author

Commented:
hmm, after careful checking I think it's not really a lag as much as it's the ball stops when it faceup and it seems like a lag

so I would say I got invalidate to work perfectly and that what I had problem with before,
problem solved

I have both invalidate() and postinvalidate() working, I know the first one goes for UI thread while the 2nd goes for non-UI thread...so why both are actually working for me?

Author

Commented:
btw many thanks for both of you

Author

Commented:
many thanks to you* (sorry typo earlier)
F IgorDeveloper

Commented:
since the "onSensorChanged" event can send lots of events you probably are getting lags
when trying to invalidate in every sensor change and try to update all the TextViews in every loop.
Try to reduce the repetitive calls to invalidate and update textviews using a timer


long time=System.currentTimeMillis();


public void onSensorChanged(SensorEvent event) {
...

if (System.currentTimeMillis()-time>100){
  //invalidate view code
}
 time=System.currentTimeMillis();


...

}





 

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial