Drehen eines ImageViews wie ein Kompass (mit dem "Nordpol" anderswo)

Ich bin stumped darüber, wie man einen "persönlichen Kompass" implementiert, dh einen Kompass, der auf ein bestimmtes Lager anstatt auf den Standard "Nordpol" hinweist … leider hat sich mein aktueller Versuch falsch herausgestellt (zeigt nicht auf die Gegebenes Lager). Es ist auch mit dem Beschleuniger verbunden, um sich dynamisch anpassen zu können, je nachdem, wie sich der Benutzer dreht.

Hier ist mein aktueller Versuch (die onSensorChanged() -Methode, die den Pfeil aktualisiert):

  • Ändern der Größe von AlertDialog auf dem Tastaturdisplay
  • Android Textauswahl in Webview
  • Google Play Game Services - kann sich nicht anmelden
  • Android Studio stecken bei erfrischendem Gradle-Projekt
  • Android Multidex nicht mit Proguard unterstützen
  • Wie kann ich während der Testausführung auf Dateien aus dem Assets-Ordner zugreifen?
  •  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("?"); } 

    Und hier ist die Methode, die die ImageView ( rotateImageView() ) dreht:

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

    Jede Hilfe wäre sehr geschätzt, da ich nicht ganz weiß, wie ich vorgehen soll. Die "Lesungen", die ich beim Ausprobieren bekomme, ist etwas ungenau und zeigt in die falsche Richtung. Bin ich etwas wirklich aus, oder habe ich gerade einen wirklich schlechten Testlauf?

  • DialogFragment in PreferenceActivity
  • Wie bekomme ich den aktuellen Geschmack in gradle?
  • Wie benutzt man die neue SD-Kartenzugriffs-API für Android 5.0 (Lollipop)?
  • Android 4.2 brach meine verschlüsselte / entschlüsselte Code und die bereitgestellten Lösungen funktionieren nicht
  • Wie man mehrere Marker auf Google Maps mit demselben Ort verarbeitet?
  • Actionbar Sherlock: Einstellen und Ausblenden der Titelleiste
  • 3 Solutions collect form web for “Drehen eines ImageViews wie ein Kompass (mit dem "Nordpol" anderswo)”

    Ihre rotateImageView-Funktion sollte gut funktionieren, aber es gibt einige Dinge, die in Ihren Rotationsberechnungen geändert werden müssen.

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

    Das Problem ist, dass bearingTo wird Ihnen eine Reichweite von -180 bis 180, die die Dinge ein wenig verwirren wird. Wir müssen diesen Wert in einen Bereich von 0 bis 360 umwandeln, um die richtige Drehung zu erhalten.

    Dies ist eine Tabelle von dem, was wir wirklich wollen, im Vergleich zu dem, was uns das gibt

     + ----------- + -------------- +
     |  Lagerung  Echtes Lager |
     + ----------- + -------------- +
     |  0 |  0 |
     + ----------- + -------------- +
     |  90 |  90 |
     + ----------- + -------------- +
     |  180 |  180 |
     + ----------- + -------------- +
     |  -90 |  270 |
     + ----------- + -------------- +
     |  -135 |  225 |
     + ----------- + -------------- +
     |  -180 |  180 |
     + ----------- + -------------- +
    

    Obwohl das Lager im Bereich von -180 bis 180 liegt, ist 0 immer noch der Norden, der uns diese Berechnung überlassen wird:

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

    Wenn wir einige Dummy-Werte hinzufügen, um unsere neue Formel zu testen:

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

    Wir haben nun den Lager aussortiert, lasst uns auf den Azimut!

    Du musst die Deklination anfassen, anstatt sie hinzuzufügen, da wir den Azimut auf 0 stellen wollen, wenn wir das Telefon direkt in den Norden zeigen, anstatt die Deklination dem Azimut hinzuzufügen, was uns dann die doppelte Deklination gibt, wenn wir das Telefon zeigen Zum wahren Norden. Korrigieren Sie dies, indem Sie die Deklination subtrahieren, anstatt sie hinzuzufügen.

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

    Wenn wir das Telefon zum echten Norden drehen, wird der Azimut dann gleich 0 sein

    Ihr Code zur Korrektur des Azimuts ist nicht mehr nötig.

     // Remove / uncomment this line azimuth = azimuth % 360; 

    Wir werden nun weiter an dem Punkt, wo wir die reale Drehung berechnen. Aber zuerst werde ich zusammenfassen, welche Art von Werten wir jetzt haben und erklären, was sie wirklich sind:

    BearTo = Der Winkel vom wahren Norden zum Zielort von dem Punkt, an dem wir gerade stehen.

    Azimut = Der Winkel, den du dein Telefon aus echtem Norden gedreht hast.

    Wenn du das sagst, wenn du dein Telefon direkt an den wahren Norden zeigst, wünscht man wirklich, dass der Pfeil den Winkel dreht, den der Bär ist. Wenn Sie Ihr Telefon 45 Grad aus dem wahren Norden zeigen, wollen wir, dass der Pfeil 45 Grad weniger dreht als das, was bearTo ist. Dies führt uns zu folgenden Berechnungen:

     float direction = bearTo - azimuth; 

    Allerdings, wenn wir irgendwelche Dummy-Werte setzen: bearTo = 45; Azimut = 180;

     direction = 45 - 180 = -135; 

    Dies bedeutet, dass der Pfeil um 135 Grad gegen den Uhrzeigersinn drehen soll. Wir müssen in eine ähnliche, wenn-Bedingung wie wir mit dem Bären to!

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

    Ihr Traffic Text, die N, E, S und W ist aus, also habe ich sie in der letzten Methode unten korrigiert.

    Ihre onSensorChanged-Methode sollte so aussehen:

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

    Sie sollten in der Lage sein, die Matrix auf die ImageView zu setzen, ohne die Bitmap jedes Mal neu zu erstellen, und er .. 'normalisieren' (ist das das Wort?) Die Lesungen.

     float b = mLoc.getBearing(); if(b < 0) b = 360 + b; float h = item.mHeading; if(h < 0) h = 360 + h; float r = (h - b) - 360; matrix.reset(); matrix.postRotate(r, width/2, height/2); 

    Im obigen Beispiel ist mLoc ein Standort, der von einem GPS-Provider zurückgegeben wird, und getBearing gibt die Anzahl der Grade östlich des Nordens der aktuellen Fahrtrichtung zurück. Item.mHeading wurde mit der Location.bearingTo () – Funktion mit mLoc und dem Standort des Elements berechnet. Breite und Höhe sind die Abmessungen der Bildansicht.

    Also, stellen Sie sicher, dass Ihre Variablen in Grad und nicht Radiant sind, und versuchen Sie "normalisieren" (immer Überschriften in den Bereich von 0-360 und nicht -180-180). Auch wenn die Ergebnisse um 180 Grad ausgeschaltet sind, stellen Sie sicher, dass Sie das Lager für Ihr Ziel erhalten, anstatt die Grade von Ihrem Ziel zu Ihnen.

    Die obige Matrix kann dann in einem ImageView gesetzt werden, das eine ScaleType.Matrix hat

     imageView.setMatrix(matrix); imageview.setScaleType(ScaleType.Matrix); 

    Da du dich um den Mittelpunkt des ImageViews drehst (die Breite / 2, Höhe / 2 im PostRotate), soll dein Drawable nach oben gerichtet sein und zum Zeitpunkt der Zeichnung gedreht werden, anstatt jedes Mal eine neue Bitmap neu zu erstellen .

    Ich habe etwa 40 Stunden ein Wochenende damit verbracht, dies zu tun.

    Schmerzen im Hintern, hoffentlich kann ich dir diesen Schmerz ersparen.

    Ok, ich warne dich, das ist ein hässlicher Code. Ich war in einer Prise, um es zu beenden, es hat keine Namensgebung, aber ich habe versucht, es so gut wie möglich zu kommentieren.

    Es wurde verwendet, um große Stapel von Nüssen zu finden, die in den Bereichen für die Lagerung

    Mit dem Telefone aktuellen Breiten- und Längengrad, dem Lat / lon des Ziels, dem Kompass-Sensor und einer Algebra konnte ich die Richtung zum Ziel berechnen.

    Lat / lon und Sensorablesungen werden aus der MainApplication-Klasse gezogen

    Dies ist ein Teil des Codes für arrow.class, mit dem ich einen Pfeil auf einer Leinwand in Richtung einer Richtung zeichnete.

      //The location you want to go to// //"Given North" double lat=0; double lon=0; ////////////////////////////////// protected void onDraw(Canvas canvas) { //Sensor values from another class managing Sensor float[] v = MainApplication.getValues(); //The current location of the device, retrieved from another class managing GPS double ourlat= MainApplication.getLatitudeD(); double ourlon= MainApplication.getLongitudeD(); //Manually calculate the direction of the pile from the device double a= Math.abs((lon-ourlon)); double b= Math.abs((lat-ourlat)); //archtangent of a/b is equal to the angle of the device from 0-degrees in the first quadrant. (Think of a unit circle) double thetaprime= Math.atan(a/b); double theta= 0; //Determine the 'quadrant' that the desired location is in //ASTC (All, Sin, Tan, Cos) Determines which value is positive //Gotta love Highschool algebra if((lat<ourlat)&&(lon>ourlon)){//-+ //theta is 180-thetaprime because it is in the 2nd quadrant theta= ((Math.PI)-thetaprime); //subtract theta from the compass value retrieved from the sensor to get our final direction theta=theta - Math.toRadians(v[0]); }else if((lat<ourlat)&&(lon<ourlon)){//-- //Add 180 degrees because it is in the third quadrant theta= ((Math.PI)+thetaprime); //subtract theta from the compass value retreived from the sensor to get our final direction theta=theta - Math.toRadians(v[0]); }else if((lat>ourlat)&&(lon>ourlon)){ //++ //No change is needed in the first quadrant theta= thetaprime; //subtract theta from the compass value retreived from the sensor to get our final direction theta=theta - Math.toRadians(v[0]); }else if((lat>ourlat)&&(lon<ourlon)){ //+- //Subtract thetaprime from 360 in the fourth quadrant theta= ((Math.PI*2)-thetaprime); //subtract theta from the compass value retreived from the sensor to get our final direction theta=theta - Math.toRadians(v[0]); } canvas.drawBitmap(_bitmap, 0, 0, paint); float[] results = {0}; //Store data Location.distanceBetween(ourlat, ourlon, lat, lon, results); try{ //Note, pileboundary is a value retreived from a database //This changes the color of the canvas based upon how close you are to the destination //Green < 100 (or database value), Yellow < (100)*2, Otherwise red if((results[0])<(pileboundary==0?100:pileboundary)){ _canvas.drawColor(Color.GREEN); }else if((results[0])<(pileboundary==0?100:pileboundary)*2){ _canvas.drawColor(Color.YELLOW); }else{ _canvas.drawColor(Color.rgb(0xff, 113, 116)); //RED-ish } //Draw the distance(in feet) from the destination canvas.drawText("Distance: "+Integer.toString((int) (results[0]*3.2808399))+ " Feet", 3, height-3, textpaint); }catch(IllegalArgumentException ex){ //im a sloppy coder } int w = canvas.getWidth(); int h = height; int x = w / 2; //put arrow in center int y = h / 2; canvas.translate(x, y); if (v != null) { // Finally, we rotate the canvas to the desired direction canvas.rotate((float)Math.toDegrees(theta)); } //Draw the arrow! canvas.drawPath(thearrow, paint); } //Some of my declarations, once again sorry :P GeomagneticField gf; Bitmap _bitmap; Canvas _canvas; int _height; int _width; Bitmap b; @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //Get the current GeomagneticField (Should be valid until 2016, according to android docs) gf = new GeomagneticField((float)lat,(float)lon,(float)MainApplication.getAltitude(),System.currentTimeMillis()); _height = View.MeasureSpec.getSize(heightMeasureSpec); _width = View.MeasureSpec.getSize(widthMeasureSpec); setMeasuredDimension(_width, _height); _bitmap = Bitmap.createBitmap(_width, _height, Bitmap.Config.ARGB_8888); _canvas = new Canvas(_bitmap); b=Bitmap.createBitmap(_bitmap); drawBoard(); invalidate(); } //Here is the code to draw the arrow thearrow.moveTo(0, -50); thearrow.lineTo(-20, 50); thearrow.lineTo(0, 50); thearrow.lineTo(20, 50); thearrow.close(); thearrow.setFillType(FillType.EVEN_ODD); 

    Hoffentlich kannst du meinen Code lesen … Wenn ich Zeit bekomme, werde ich es ein bisschen hübscher machen.

    Wenn du irgendwelche Erklärungen brauchst, lass es mich wissen.

    -MrZander

    Das Android ist ein Google Android Fan-Website, Alles ├╝ber Android Phones, Android Wear, Android Dev und Android Spiele Apps und so weiter.