像指南针一样旋转ImageView(在其他地方设置"北极")

kar*_*ark 35 gps android rotation android-imageview

我对如何实施"个人指南针"感到困惑,即指向特定方位而不是标准"北极"的指南针...不幸的是,我目前的尝试出错了(不指向给定方位).它还与加速器连接,能够根据用户转向的方式动态调整自身.

这是我目前的尝试(onSensorChanged()更新箭头的方法):

public void onSensorChanged( SensorEvent event ) {

            // If we don't have a Location, we break out
            if ( LocationObj == null ) return;

            float azimuth = event.values[0];
                            float baseAzimuth = azimuth;

            GeomagneticField geoField = new GeomagneticField( Double
                    .valueOf( LocationObj.getLatitude() ).floatValue(), Double
                    .valueOf( LocationObj.getLongitude() ).floatValue(),
                    Double.valueOf( LocationObj.getAltitude() ).floatValue(),
                    System.currentTimeMillis() );
            azimuth += geoField.getDeclination(); // converts magnetic north into true north

            //Correct the azimuth
            azimuth = azimuth % 360;

            //This is where we choose to point it
            float direction = azimuth + LocationObj.bearingTo( destinationObj );
            rotateImageView( arrow, R.drawable.arrow, direction );

            //Set the field
            if( baseAzimuth > 0 && baseAzimuth < 45 ) fieldBearing.setText("S");
            else if( baseAzimuth >= 45 && baseAzimuth < 90 ) fieldBearing.setText("SW");
            else if( baseAzimuth > 0 && baseAzimuth < 135 ) fieldBearing.setText("W");
            else if( baseAzimuth > 0 && baseAzimuth < 180 ) fieldBearing.setText("NW");
            else if( baseAzimuth > 0 && baseAzimuth < 225 ) fieldBearing.setText("N");
            else if( baseAzimuth > 0 && baseAzimuth < 270 ) fieldBearing.setText("NE");
            else if( baseAzimuth > 0 && baseAzimuth < 315 ) fieldBearing.setText("E");
            else if( baseAzimuth > 0 && baseAzimuth < 360 ) fieldBearing.setText("SE");
            else fieldBearing.setText("?"); 

        }
Run Code Online (Sandbox Code Playgroud)

这是旋转ImageView(rotateImageView())的方法:

private void rotateImageView( ImageView imageView, int drawable, float rotate ) {

    // Decode the drawable into a bitmap
    Bitmap bitmapOrg = BitmapFactory.decodeResource( getResources(),
            drawable );

    // Get the width/height of the drawable
    DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm);
    int width = bitmapOrg.getWidth(), height = bitmapOrg.getHeight();

    // Initialize a new Matrix
    Matrix matrix = new Matrix();

    // Decide on how much to rotate
    rotate = rotate % 360;

    // Actually rotate the image
    matrix.postRotate( rotate, width, height );

    // recreate the new Bitmap via a couple conditions
    Bitmap rotatedBitmap = Bitmap.createBitmap( bitmapOrg, 0, 0, width, height, matrix, true );
    //BitmapDrawable bmd = new BitmapDrawable( rotatedBitmap );

    //imageView.setImageBitmap( rotatedBitmap );
    imageView.setImageDrawable(new BitmapDrawable(getResources(), rotatedBitmap));
    imageView.setScaleType( ScaleType.CENTER );
}
Run Code Online (Sandbox Code Playgroud)

任何帮助将不胜感激,因为我不知道如何继续.我在尝试时得到的"读数"有点不准确,指向错误的方向.我真的做了什么,或者我的测试运行真的很糟糕吗?

小智 55

您的rotateImageView函数应该可以正常工作,但是在旋转计算中有一些事情需要更改.

//This is where we choose to point it
float direction = azimuth + LocationObj.bearingTo( destinationObj );
rotateImageView( arrow, R.drawable.arrow, direction );
Run Code Online (Sandbox Code Playgroud)

问题是,轴承会给你-180到180的范围,这会让事情有点混乱.我们需要将此值转换为0到360的范围才能获得正确的旋转.

这是我们真正想要的表格,与轴承给我们的内容相比较

+-----------+--------------+
| bearingTo | Real bearing |
+-----------+--------------+
| 0         | 0            |
+-----------+--------------+
| 90        | 90           |
+-----------+--------------+
| 180       | 180          |
+-----------+--------------+
| -90       | 270          |
+-----------+--------------+
| -135      | 225          |
+-----------+--------------+
| -180      | 180          |
+-----------+--------------+

即使轴承在-180到180的范围内,0仍然是真北,这将使我们进行此计算:

// Store the bearingTo in the bearTo variable
float bearTo = LocationObj.bearingTo( destinationObj );

// If the bearTo is smaller than 0, add 360 to get the rotation clockwise.
if (bearTo < 0) {
    bearTo = bearTo + 360;
}
Run Code Online (Sandbox Code Playgroud)

如果我们添加一些虚拟值来测试我们的新公式:

float bearTo = -100;
// This will now equal to true
if (-100 < 0) {
    bearTo = -100 + 360 = 360 - 100 = 260;
}
Run Code Online (Sandbox Code Playgroud)

我们现在已经整理了轴承,让我们前往方位角!

您需要减去赤纬而不是添加它,因为当我们将手机直接指向真北而不是将偏角添加到方位角时我们希望方位角为0,这样当我们指向手机时会给我们两倍的偏角真正的北方.通过减去偏角而不是添加它来纠正这个问题.

azimuth -= geoField.getDeclination(); // converts magnetic north into true north
Run Code Online (Sandbox Code Playgroud)

当我们现在将手机转为真北时,方位角将等于0

您不再需要修改方位角的代码.

// Remove / uncomment this line
azimuth = azimuth % 360;
Run Code Online (Sandbox Code Playgroud)

我们现在将继续我们计算实际旋转的位置.但首先,我将总结我们现在拥有的价值观并解释它们的真实含义:

bearTo =从我们现在所处的位置开始,从真北到目的地的角度.

azimuth =您从正北方向旋转手机的角度.

通过这样说,如果你将手机直接指向真正的北方,我们真的希望箭头旋转bearTo设置为的角度.如果你将手机指向真北45度,我们希望箭头比熊的旋转度低45度.这让我们进行以下计算:

float direction = bearTo - azimuth;
Run Code Online (Sandbox Code Playgroud)

但是,如果我们输入一些虚拟值:bearTo = 45; 方位角= 180;

direction = 45 - 180 = -135;
Run Code Online (Sandbox Code Playgroud)

这意味着箭头应逆时针旋转135度.我们需要像熊一样处理类似的if条件!

// If the direction is smaller than 0, add 360 to get the rotation clockwise.
if (direction < 0) {
    direction = direction + 360;
}
Run Code Online (Sandbox Code Playgroud)

你的轴承文字,N,E,S和W是关闭的,所以我在下面的最后方法中对它们进行了修正.

你的onSensorChanged方法应如下所示:

public void onSensorChanged( SensorEvent event ) {

    // If we don't have a Location, we break out
    if ( LocationObj == null ) return;

    float azimuth = event.values[0];
    float baseAzimuth = azimuth;

    GeomagneticField geoField = new GeomagneticField( Double
        .valueOf( LocationObj.getLatitude() ).floatValue(), Double
        .valueOf( LocationObj.getLongitude() ).floatValue(),
        Double.valueOf( LocationObj.getAltitude() ).floatValue(),
        System.currentTimeMillis() );

    azimuth -= geoField.getDeclination(); // converts magnetic north into true north

    // Store the bearingTo in the bearTo variable
    float bearTo = LocationObj.bearingTo( destinationObj );

    // If the bearTo is smaller than 0, add 360 to get the rotation clockwise.
    if (bearTo < 0) {
        bearTo = bearTo + 360;
    }

    //This is where we choose to point it
    float direction = bearTo - azimuth;

    // If the direction is smaller than 0, add 360 to get the rotation clockwise.
    if (direction < 0) {
        direction = direction + 360;
    }

    rotateImageView( arrow, R.drawable.arrow, direction );

    //Set the field
    String bearingText = "N";

    if ( (360 >= baseAzimuth && baseAzimuth >= 337.5) || (0 <= baseAzimuth && baseAzimuth <= 22.5) ) bearingText = "N";
    else if (baseAzimuth > 22.5 && baseAzimuth < 67.5) bearingText = "NE";
    else if (baseAzimuth >= 67.5 && baseAzimuth <= 112.5) bearingText = "E";
    else if (baseAzimuth > 112.5 && baseAzimuth < 157.5) bearingText = "SE";
    else if (baseAzimuth >= 157.5 && baseAzimuth <= 202.5) bearingText = "S";
    else if (baseAzimuth > 202.5 && baseAzimuth < 247.5) bearingText = "SW";
    else if (baseAzimuth >= 247.5 && baseAzimuth <= 292.5) bearingText = "W";
    else if (baseAzimuth > 292.5 && baseAzimuth < 337.5) bearingText = "NW";
    else bearingText = "?";

    fieldBearing.setText(bearingText);

}
Run Code Online (Sandbox Code Playgroud)