使用安卓加速计移动图像

10

我已经阅读了有关访问手机加速度计(加速度和方向)值的文章/教程。我正在尝试构建一个简单的应用程序,可以使用这些值移动球形图像。以下是我的代码:

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(); 
           } 
       }
}

屏幕上显示一个椭圆形状,但之后什么都没发生。

谢谢


在你的onDraw方法中,你没有做任何移动形状的操作。你应该在那里加入代码来设置你的可绘制对象的位置(使用你所存储的X,Y值)。 - dymmeh
我该怎么做?你能给我一些示例代码吗? - sankara rao bhatta
1
尝试将以下代码放入您的onDraw方法中: "RectF oval = new RectF(Accelerometer.x, Accelerometer.y, Accelerometer.x + width, Accelerometer.y + height); //设置矩形边界 Paint p = new Paint(); //设置一些画笔选项 canvas.drawOval(oval, p);" 您需要将X和Y变量设置为公共成员,并且您的宽度/高度需要是CustomDrawableView类中的静态值。 - dymmeh
6个回答

26

使用这段代码。在初始化该类之后,您从未设置可绘制对象的位置。您需要进行一些计算才能正确设置球的位置。您之前的方法获取了超出屏幕范围的值(大于10000),导致椭圆形绘制在屏幕外。

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();
        }
    }
}

谢谢,我的图像现在可以移动了。我还没有仔细查看代码,只是将其复制粘贴并部署到我的手机上。再次感谢。 - sankara rao bhatta
如果这个方法适用于您,请确保将其标记为答案。 - dymmeh
它正在工作。但是椭圆不动,停留在顶部中心。 - Sahil Mahajan Mj
这非常有用对我来说。但是我如何根据加速度计读数更改背景图像,就像3D动态壁纸一样? Translated: 这对我非常有用。但是我该如何根据加速度计的读数更改背景图片,就像3D动态壁纸一样? - Harsha
@dymmeh 嘿,回答得不错。能不能请你看一下这个问题?:D - Skizo-ozᴉʞS ツ
显示剩余8条评论

13

这是我对这个问题的实现。Dymmeh的解决方案给了我很多问题,因此我进行了重构,直到我让它正常工作。

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();
        }
    }

}

终于得到了一个可用的代码,但是图像超出了屏幕...如何设置边界? - Rishabh Srivastava
@RishabhSrivastava 我会在 onSensorChanged 函数中加入一个条件语句 - 例如 x= (x-(int) event.values[0] > someValue) ? someValue : x-(int) event.values[0] - khalid13

3

我已经在onSensorChange代码中进行了一些更改,以便将球移动到屏幕上。在我的情况下,使用这个示例时球不会正确移动,因此我进行了更改。这个例子对我来说很有效。

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;
        }

    }
    }
}

我绘制球的视图类

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();
    }
}

这就是全部内容了,我希望能对我们有所帮助。


1

如果您想使用加速度传感器,则尝试将sensorEvent.values [0]用作xChange,并将sensorEvents.values [1]用作yChange。如果不使用加速度传感器,请使用相同的值并将其移动到 (sensorEvent.sensor.getType() == Sensor.TYPE_ORIENTATION) if 语句中,这将给出手机的倾斜而不是沿某个轴线快速移动的情况。

当您设置或更改传感器时,还需要对 View 调用 invalidate();

Sensor.TYPE_ACCELEROMETER 返回:

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

Sensor.TYPE_ORIENTATION 返回:

values[0]:方位角,磁北方向与y轴之间的角度,绕z轴旋转(0到359)。0=北,90=东,180=南,270=西。

values[1]:俯仰角,绕x轴旋转(-180到180),当z轴朝向y轴移动时,正值为正。

values[2]:横滚角,绕y轴旋转(-90到90),当x轴朝向z轴移动时,正值为正。


1

请使用以下库代替滚动运动

在顶部XML视图中添加此行

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

用于布局

 <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" />

在你的onCreate()方法中循环执行代码

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

在您的onResume()方法中。
if(mBackground!=null)
mBackground.registerSensorManager();

在您的onDestroy()方法中。
// Unregister SensorManager when exiting
mBackground.unregisterSensorManager();

0

我认为你需要在onSensorChanged()方法中或者实现一个特定的fps速率来使你的视图失效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接