10

I have read articles/tutorial about accessing the phone's accelerometer (acceleration and orientation) values. I am trying to build a simple app where I can move a ball image using the these values. Here is my code:

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
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.TextView;

public class Accelerometer extends Activity implements SensorEventListener {
    /** Called when the activity is first created. */
     CustomDrawableView mCustomDrawableView = null; 
     ShapeDrawable mDrawable = new ShapeDrawable(); 
      int x ; 
       int y ;

    private SensorManager sensorManager = null;

       /** Called when the activity is first created. */
       @Override
       public void onCreate(Bundle savedInstanceState) {

           super.onCreate(savedInstanceState);
           // Get a reference to a SensorManager
           sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
           mCustomDrawableView = new CustomDrawableView(this); 
           setContentView(mCustomDrawableView); 
         //  setContentView(R.layout.main);

       }

       // This method will update the UI on new sensor events
       public void onSensorChanged(SensorEvent sensorEvent) {
         {
         if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {

         int someNumber = 100;
         float xChange = someNumber * sensorEvent.values[1];
         //values[2] can be -90 to 90
         float yChange = someNumber * 2 * sensorEvent.values[2];       
             x = x + (int)xChange;
             y = y + (int)yChange;

         }


         if (sensorEvent.sensor.getType() == Sensor.TYPE_ORIENTATION) {

         }
        }
       }

       // I've chosen to not implement this method
       public void onAccuracyChanged(Sensor arg0, int arg1) {
     // TODO Auto-generated method stub

    }

       @Override
       protected void onResume() {
        super.onResume();
        // Register this class as a listener for the accelerometer sensor
        sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
        // ...and the orientation sensor
        sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), SensorManager.SENSOR_DELAY_NORMAL);
       }

       @Override
       protected void onStop() {
        // Unregister the listener
        sensorManager.unregisterListener(this);
        super.onStop();
       } 
       public  class CustomDrawableView extends View { 

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

               int width = 50; 
               int height = 50; 
               mDrawable = new ShapeDrawable(new OvalShape()); 
               mDrawable.getPaint().setColor(0xff74AC23); 
               mDrawable.setBounds(x, y, x + width, y + height); 
           } 
           protected void onDraw(Canvas canvas) { 
               mDrawable.draw(canvas); 
               invalidate(); 
           } 
       }
}

I am getting an oval shape displayed on the screen but nothing happens after that.

thanks

demongolem
  • 9,474
  • 36
  • 90
  • 105
sankara rao bhatta
  • 649
  • 5
  • 9
  • 13
  • In your onDraw you aren't doing anything to move your shape. You should add code in there that sets the location of your drawable (from the X,Y values you are storing) – dymmeh Jun 23 '11 at 17:01
  • how do i do it.. can you give me some sample code – sankara rao bhatta Jun 24 '11 at 05:19
  • 1
    Try putting this in your onDraw method "RectF oval = new RectF(Accelerometer.x, Accelerometer.y, Accelerometer.x + width, Accelerometer.y + height); //set bounds of rectangle Paint p = new Paint(); //set some paint options canvas.drawOval(oval, p);" You'll need to make your X and Y variables public members and your width / height will need to be static values in your CustomDrawableView class. – dymmeh Jun 24 '11 at 14:46

6 Answers6

26

Use this code. You were never setting the location of the drawable after you intialized that class. You'll have to do some calculations to set the balls location properly. The way you were doing it was getting values over 10000 which was drawing the oval off screen.

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.View;

public class Accelerometer extends Activity implements SensorEventListener
{
    /** Called when the activity is first created. */
    CustomDrawableView mCustomDrawableView = null;
    ShapeDrawable mDrawable = new ShapeDrawable();
    public static int x;
    public static int y;

    private SensorManager sensorManager = null;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {

        super.onCreate(savedInstanceState);
        // Get a reference to a SensorManager
        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        mCustomDrawableView = new CustomDrawableView(this);
        setContentView(mCustomDrawableView);
        // setContentView(R.layout.main);

    }

    // This method will update the UI on new sensor events
    public void onSensorChanged(SensorEvent sensorEvent)
    {
        {
            if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
                // the values you were calculating originally here were over 10000!
                x = (int) Math.pow(sensorEvent.values[1], 2); 
                y = (int) Math.pow(sensorEvent.values[2], 2);

            }

            if (sensorEvent.sensor.getType() == Sensor.TYPE_ORIENTATION) {

            }
        }
    }

    // I've chosen to not implement this method
    public void onAccuracyChanged(Sensor arg0, int arg1)
    {
        // TODO Auto-generated method stub

    }

    @Override
    protected void onResume()
    {
        super.onResume();
        // Register this class as a listener for the accelerometer sensor
        sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
                SensorManager.SENSOR_DELAY_NORMAL);
        // ...and the orientation sensor
        sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
                SensorManager.SENSOR_DELAY_NORMAL);
    }

    @Override
    protected void onStop()
    {
        // Unregister the listener
        sensorManager.unregisterListener(this);
        super.onStop();
    }

    public class CustomDrawableView extends View
    {
        static final int width = 50;
        static final int height = 50;

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

            mDrawable = new ShapeDrawable(new OvalShape());
            mDrawable.getPaint().setColor(0xff74AC23);
            mDrawable.setBounds(x, y, x + width, y + height);
        }

        protected void onDraw(Canvas canvas)
        {
            RectF oval = new RectF(Accelerometer.x, Accelerometer.y, Accelerometer.x + width, Accelerometer.y
                    + height); // set bounds of rectangle
            Paint p = new Paint(); // set some paint options
            p.setColor(Color.BLUE);
            canvas.drawOval(oval, p);
            invalidate();
        }
    }
}
dymmeh
  • 22,247
  • 5
  • 53
  • 60
  • thank you..my image now moves..have not gone through the code yet..just copy pasted and deployed onto my mobile. thanks again – sankara rao bhatta Jun 24 '11 at 18:26
  • if this is what worked for you make sure to mark it as the answer. – dymmeh Jun 24 '11 at 18:46
  • does this code works on Android 2.3, I dont think so.. what if i want to use this in android 2.3 – Sahil Mahajan Mj Sep 05 '12 at 06:19
  • @SahilMahajanMj - at the time (June 2011) only 2.3 was available for mobile and I tested this on my own device before I posted the code. It should be working fine. Can you tell me what problems you're having with it? – dymmeh Sep 05 '12 at 12:28
  • @SahilMahajanMj - I just tested this on a Samsung Nexus S running 2.3.7 and it works fine – dymmeh Sep 05 '12 at 12:30
  • Failed to find an AVD compatible with target 'Android 4.0.3' – Sahil Mahajan Mj Sep 05 '12 at 12:33
  • @SahilMahajanMj - That's eclipse telling you it can't find a device or emulator that is running the same API (4.0.3) as the code you compiled. If you set the code to compile using target 2.3 it will work on any device running 2.3 or higher. In eclipse right click the project, go to properties, click "Android", and set the Project build target to 2.3. (You need to have the 2.3 API installed to do so). If you don't do this you'll need to have a device running 4.0.3 or higher. Give it a try and let me know! :) – dymmeh Sep 05 '12 at 12:38
  • Its working. but the oval is not moving, its stationary at top centre. – Sahil Mahajan Mj Sep 05 '12 at 12:42
  • Can you tell me which device you are using to run this? – dymmeh Sep 05 '12 at 12:44
  • I am using AVD for this.. AVD level is 2.2 and the project build target is also st to android 2.2. also added this line to manifest... – Sahil Mahajan Mj Sep 05 '12 at 12:47
  • Using an emulator you cannot simulate Accelerometer data. This is why the ball won't move. You need an external application to modify these values. I've never tried that before since I've always used an actual device. I found http://code.google.com/p/openintents/wiki/SensorSimulator by searching but I don't know how useful it will be – dymmeh Sep 05 '12 at 15:55
  • its very useful for me.But how can i change the background image based on assolerometer readings like 3d live wallpaper. – Harsha Oct 04 '13 at 07:59
  • @dymmeh Yo, nice answer, could you take a look at this [one](http://stackoverflow.com/questions/34997669/android-acceleration-down-smash) please? :D – Skizo-ozᴉʞS ツ Jan 28 '16 at 16:31
13

Here is my implementation of this problem. Dymmeh's solution kept throwing problems at me, so I refactored it until I got it working.

package edu.ian495.accelerometertest;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.Menu;
import android.widget.ImageView;

public class MainActivity extends Activity implements SensorEventListener {
    private SensorManager sensorManager;
    private Sensor accelerometer;
    private long lastUpdate;

    AnimatedView animatedView = null;
    ShapeDrawable mDrawable = new ShapeDrawable();
    public static int x;
    public static int y;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // setContentView(R.layout.activity_main);

        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        accelerometer = sensorManager
                .getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        lastUpdate = System.currentTimeMillis();

        animatedView = new AnimatedView(this);
        setContentView(animatedView);
    }

    @Override
    protected void onResume() {
        super.onResume();
        sensorManager.registerListener(this, accelerometer,
                SensorManager.SENSOR_DELAY_GAME);
    }

    @Override
    protected void onPause() {
        super.onPause();
        sensorManager.unregisterListener(this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

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

    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        // TODO Auto-generated method stub
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {

            x -= (int) event.values[0];
            y += (int) event.values[1];

        }
    }

    public class AnimatedView extends ImageView {

        static final int width = 50;
        static final int height = 50;

        public AnimatedView(Context context) {
            super(context);
            // TODO Auto-generated constructor stub

            mDrawable = new ShapeDrawable(new OvalShape());
            mDrawable.getPaint().setColor(0xffffAC23);
            mDrawable.setBounds(x, y, x + width, y + height);

        }

        @Override
        protected void onDraw(Canvas canvas) {

            mDrawable.setBounds(x, y, x + width, y + height);
            mDrawable.draw(canvas);
            invalidate();
        }
    }

}
khalid13
  • 2,767
  • 2
  • 30
  • 48
  • finally got a working code but the image is going out of the screen...how to set bounds? – Rishabh Srivastava Sep 21 '13 at 10:59
  • @RishabhSrivastava I would throw in a conditional in the onSensorChanged function - eg. x= (x-(int) event.values[0] > someValue) ? someValue : x-(int) event.values[0] – khalid13 Sep 21 '13 at 22:41
3

I have done some changes in onSensorChange code to move the ball in the screen. With the example in my case the ball don´t move correctly and for that I did the changes. This example Works fine for my.

public void onSensorChanged(SensorEvent sensorEvent)
{
    //Try synchronize the events
    synchronized(this){
    //For each sensor
    switch (sensorEvent.sensor.getType()) {
    case Sensor.TYPE_MAGNETIC_FIELD: //Magnetic sensor to know when the screen is landscape or portrait
        //Save values to calculate the orientation
        mMagneticValues = sensorEvent.values.clone();
        break;
    case Sensor.TYPE_ACCELEROMETER://Accelerometer to move the ball
        if (bOrientacion==true){//Landscape
            //Positive values to move on x
            if (sensorEvent.values[1]>0){
                //In margenMax I save the margin of the screen this value depends of the screen where we run the application. With this the ball not disapears of the screen
                if (x<=margenMaxX){
                    //We plus in x to move the ball
                    x = x + (int) Math.pow(sensorEvent.values[1], 2);
                }
            }
            else{
                //Move the ball to the other side
                if (x>=margenMinX){
                    x = x - (int) Math.pow(sensorEvent.values[1], 2);
                }
            }
            //Same in y
            if (sensorEvent.values[0]>0){
                if (y<=margenMaxY){
                    y = y + (int) Math.pow(sensorEvent.values[0], 2);
                }
            }
            else{
                if (y>=margenMinY){
                    y = y - (int) Math.pow(sensorEvent.values[0], 2);
                }
            }
        }
        else{//Portrait
            //Eje X
            if (sensorEvent.values[0]<0){
                if (x<=margenMaxX){
                    x = x + (int) Math.pow(sensorEvent.values[0], 2);
                }
            }
            else{
                if (x>=margenMinX){
                    x = x - (int) Math.pow(sensorEvent.values[0], 2);
                }
            }
            //Eje Y
            if (sensorEvent.values[1]>0){
                if (y<=margenMaxY){
                    y = y + (int) Math.pow(sensorEvent.values[1], 2);
                }
            }
            else{
                if (y>=margenMinY){
                    y = y - (int) Math.pow(sensorEvent.values[1], 2);
                }
            }

        }
        //Save the values to calculate the orientation
        mAccelerometerValues = sensorEvent.values.clone();
        break;  
    case Sensor.TYPE_ROTATION_VECTOR:  //Rotation sensor
        //With this value I do the ball bigger or smaller
        if (sensorEvent.values[1]>0){
            z=z+ (int) Math.pow(sensorEvent.values[1]+1, 2);
        }
        else{
            z=z- (int) Math.pow(sensorEvent.values[1]+1, 2);                    
        }

    default:
        break;
    }
    //Screen Orientation
    if (mMagneticValues != null && mAccelerometerValues != null) {
        float[] R = new float[16];
        SensorManager.getRotationMatrix(R, null, mAccelerometerValues, mMagneticValues);
        float[] orientation = new float[3];
        SensorManager.getOrientation(R, orientation);
        //if x have positives values the screen orientation is landscape in other case is portrait
        if (orientation[0]>0){//LandScape
            //Here I change the margins of the screen for the ball not disapear
            bOrientacion=true;
            margenMaxX=1200;
            margenMinX=0;
            margenMaxY=500;
            margenMinY=0;
        }
        else{//Portrait
            bOrientacion=false;
            margenMaxX=600;
            margenMinX=0;
            margenMaxY=1000;
            margenMinY=0;
        }

    }
    }
}

The view class where I draw the ball

public class CustomDrawableView extends View
{
    static final int width = 50;
    static final int height = 50;
    //Constructor de la figura
    public CustomDrawableView(Context context)
    {
        super(context);

        mDrawable = new ShapeDrawable(new OvalShape());
        mDrawable.getPaint().setColor(0xff74AC23);
        mDrawable.setBounds(x, y, x + width, y + height);
    }
    //Dibujamos la figura
    protected void onDraw(Canvas canvas)
    {
        //Actividad_Principal x,y,z are variables from the main activity where I have the onSensorChange
        RectF oval = new RectF(Actividad_Principal.x+Actividad_Principal.z, Actividad_Principal.y+Actividad_Principal.z, Actividad_Principal.x + width, Actividad_Principal.y + height);             
        Paint p = new Paint(); 
        p.setColor(Color.BLUE);
        canvas.drawOval(oval, p);
        invalidate();
    }
}

}

That is all, I hope help us.

Pedro
  • 31
  • 4
1

use the following library instead scrolling motion

add this line in top XML view

xmlns:parallax="http://schemas.android.com/apk/res-auto"

for Layout

 <com.nvanbenschoten.motion.ParallaxImageView
            android:id="@+id/parallex"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/image_hd"
            parallax:motionTiltSensitivity="2.5" />

for code in your onCreate() method

ParallaxImageView mBackground = (ParallaxImageView) findViewById(R.id.parallex);

in your onResume() method

if(mBackground!=null)
mBackground.registerSensorManager();

in your onDestroy() method

// Unregister SensorManager when exiting
mBackground.unregisterSensorManager();
Ali Imran
  • 8,927
  • 3
  • 39
  • 50
1

Try using sensorEvent.values[0] for your xChange and sensorEvents.values[1] for your yChange if you want to use the acceleration sensor, if not use the same values and move it into the (sensorEvent.sensor.getType() == Sensor.TYPE_ORIENTATION) if statement, this will give you the tilt of the handset rather than how quickly its moving along an axis.

You also need to call invalidate(); on the View when you set or change a sensor.

The Sensor.TYPE_ACCELEROMETER returns:

values[0]: Acceleration minus Gx on the x-axis
values[1]: Acceleration minus Gy on the y-axis
values[2]: Acceleration minus Gz on the z-axis

The Sensor.TYPE_ORIENTATION returns:

values[0]: Azimuth, angle between the magnetic north direction and the y-axis, around the z-axis (0 to 359). 0=North, 90=East, 180=South, 270=West

values[1]: Pitch, rotation around x-axis (-180 to 180), with positive values when the z-axis moves toward the y-axis.

values[2]: Roll, rotation around y-axis (-90 to 90), with positive values when the x-axis moves toward the z-axis.

Kai
  • 38,985
  • 14
  • 88
  • 103
Kenny
  • 5,522
  • 2
  • 18
  • 29
0

I think you need to invalidate your view in the onSensorChanged() method or at a specific fps rate that you have to implement.

BrainCrash
  • 12,992
  • 3
  • 32
  • 38