首页 > 解决方案 > 无法使用 System.currentTimeMillis() 在 Android 中计算时间差

问题描述

我正在开发一个小的 Android 应用程序来计算使用手机的加速度计击中一个正方形与另一个正方形所需的时间。

我有一个自定义视图定义如下:(为了便于阅读,我删除了几个声明)

public class BallView extends View implements SensorEventListener {

    private long startTime = 0;
    private long endTime = 0;
    private long time = 0;

    public BallView( Context context) {
        super( context);
        init( context );
    }

    private void init(Context context) {
        size = 200;
        orignSize = size;
        rectBall.set(currentX,currentY,currentX+size,currentY+size);
        rectviseur.set(getWidth()/2-10,getHeight()/2-10,getWidth()/2-5,getHeight()/2-5);
        vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
        startT();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        currentX = 0;
        currentY = 0;
    }

    @Override
    protected void onDraw(Canvas canvas) {

        rectviseur.set(getWidth()/2-4,getHeight()/2-4,getWidth()/2+4,getHeight()/2+4);
        super.onDraw(canvas);
        paintViseur.setColor(Color.GREEN);
        paint.setColor(Color.RED);
        rectBall.set(currentX,currentY,currentX+size,currentY+size);
        canvas.drawRect(rectBall,paint);
        canvas.drawRect(rectviseur,paintViseur);
        paint.setTextSize(20);

        canvas.drawText(String.valueOf(time),300,300,paint);
    }

    private void moveImage( float x, float y ) {
        currentX += (int) x;
        currentY += (int) y;

        if ( currentX < 0 ) {
            currentX = 0;
        } else if ( currentX + size > getWidth() ){
            this.currentX = getWidth() - size;
        }

        if ( currentY < 0 ) {
            currentY = 0;
        } else if ( currentY + size > getHeight() ){
            currentY = getHeight() - size;
        }

        invalidate();
    }

    @Override
    public void onAccuracyChanged(Sensor arg0, int arg1) {}

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER ) {
            x = event.values[0];
            y = event.values[1];
            z = event.values[2];
        }
        if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE ) {
            xg = event.values[0];
            yg = event.values[1];
            zg = event.values[2];
        }

        this.moveImage( -x*5, -(z-(float)5.5)*5 );
        hit();
    }

    public void hit(){
        if (rectBall.contains(rectviseur)) {
            //vibrate
            if (Build.VERSION.SDK_INT >= 26) {
                vibrator.vibrate(VibrationEffect.createOneShot(200, VibrationEffect.DEFAULT_AMPLITUDE));
            } else {
                vibrator.vibrate(200);
            }


            //replace the rectangle
            size = size-20;
            currentX = random.nextInt(getWidth()-size);
            currentY = random.nextInt(getHeight()-size);
            if (size < 8) {
                size= orignSize;
            }

            stopT();
            time = time(startTime, endTime);

            startT();
        }
    }

    private void startT() {
        startTime = System.currentTimeMillis();
    }

    private void stopT() {
        endTime = System.currentTimeMillis();
    }

    private long time(long start, long end) {
       return (end - start);
    }
}

我有 3 个变量startTime来存储游戏启动(或重新启动)endTime的当前时间、存储方块被击中的当前时间以及time计算所花费的时间。

我通过分别调用和来影响startTime和。第一个做作是在方法上。我用endTimestartT()stopT()startTimeinitcanvas.drawText(String.valueOf(time),300,300,paint);

正如您所看到的,当方块碰到另一个方块时,该方法hit()被调用。在这个方法结束时,我调用stopT()存储方块被击中的时间,并调用该time方法来计算它所花费的时间。然后我记得startT()因为游戏重新开始。

所以从技术上讲,startTime它应该包含类似“1607303475300”和endTime类似“1607303477350”的东西。但我time每次都得到完全不连贯的结果。像 5、7 或 8,而时间以毫秒为单位,这是不可能的。我真的不明白为什么。我尝试了几种解决方案,但都没有奏效,所以我正在寻求您的帮助。

编辑:
这也是使用我的 BallView 的 MainActivity 类:

public class MainActivity extends Activity {

    private BallView ballView;
    private SensorManager sensorManager = null;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ballView = new BallView(this);
        ballView.setBackgroundColor(Color.BLACK);
        setContentView(ballView);

        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
    }

    @Override
    protected void onResume() {
        super.onResume();
        sensorManager.registerListener(
                ballView,
                sensorManager.getDefaultSensor( Sensor.TYPE_ACCELEROMETER ),
                sensorManager.SENSOR_DELAY_GAME );
        sensorManager.registerListener(
                ballView,
                sensorManager.getDefaultSensor( Sensor.TYPE_GYROSCOPE ),
                sensorManager.SENSOR_DELAY_GAME );
    }

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

标签: javaandroidtime

解决方案


我没有看到 OnSensorChanged 只被调用一次的限制。假设有一个加速度计事件,紧接着是一个陀螺仪事件。由于您只保留“最后一次”,因此您最终会得到两个事件之间的间隔。


另一种方法是考虑“靠近在一起”的调用是同一时间间隔的一部分。让我试着勾勒一下这种方法。我喜欢一种计时方法而不是三种。

    // This is an estimate of how much time needs to
    // elapse before we consider an 'event' to be the
    // first in a new timing interval.  3 seconds is
    // just my guess; it depends on what you want.
    final static long shortInterval = 3_000;

    // Clock time at start of new interval
    long intervalStart = 0; 

    // Elapsed time in interval since last call
    long elapsed = 0;

     // The timer.  Call on every sensor event.
    void intervalTimer() {
        long now = System.currentTimeMilliseconds();
        if (now - intervalStart > shortInterval)
             intervalStart = now;
        elapsed = now - intervalStart;
    }

你就在那里。intervalTimer从您的 init 方法和 OnSensorChanged 的​​末尾调用。只要调用之间的时间超过该shortInterval值,测量就会自动重置。的值elapsed在您需要使用时可用。


推荐阅读