Create a custom view, MyCompassView, extends View. It display the drawing of our compass.
MyCompassView.java
package com.exercise.AndroidCompass;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class MyCompassView extends View {
private float direction = 0;
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private boolean firstDraw;
public MyCompassView(Context context) {
super(context);
// TODO Auto-generated constructor stub
init();
}
public MyCompassView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
init();
}
public MyCompassView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
init();
}
private void init(){
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(3);
paint.setColor(Color.WHITE);
paint.setTextSize(30);
firstDraw = true;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
int cxCompass = getMeasuredWidth()/2;
int cyCompass = getMeasuredHeight()/2;
float radiusCompass;
if(cxCompass > cyCompass){
radiusCompass = (float) (cyCompass * 0.9);
}
else{
radiusCompass = (float) (cxCompass * 0.9);
}
canvas.drawCircle(cxCompass, cyCompass, radiusCompass, paint);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
if(!firstDraw){
canvas.drawLine(cxCompass, cyCompass,
(float)(cxCompass + radiusCompass * Math.sin((double)(-direction) * 3.14/180)),
(float)(cyCompass - radiusCompass * Math.cos((double)(-direction) * 3.14/180)),
paint);
canvas.drawText(String.valueOf(direction), cxCompass, cyCompass, paint);
}
}
public void updateDirection(float dir)
{
firstDraw = false;
direction = dir;
invalidate();
}
}
Modify the layout file, main.xml, to add MyCompassView.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<view
class="com.exercise.AndroidCompass.MyCompassView"
android:id="@+id/mycompassview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
Modify the main code, AndroidCompass.java, to handle SensorManager and SensorEventListener.
package com.exercise.AndroidCompass;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.widget.Toast;
public class AndroidCompass extends Activity {
private static SensorManager mySensorManager;
private boolean sersorrunning;
private MyCompassView myCompassView;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myCompassView = (MyCompassView)findViewById(R.id.mycompassview);
mySensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
List<Sensor> mySensors = mySensorManager.getSensorList(Sensor.TYPE_ORIENTATION);
if(mySensors.size() > 0){
mySensorManager.registerListener(mySensorEventListener, mySensors.get(0), SensorManager.SENSOR_DELAY_NORMAL);
sersorrunning = true;
Toast.makeText(this, "Start ORIENTATION Sensor", Toast.LENGTH_LONG).show();
}
else{
Toast.makeText(this, "No ORIENTATION Sensor", Toast.LENGTH_LONG).show();
sersorrunning = false;
finish();
}
}
private SensorEventListener mySensorEventListener = new SensorEventListener(){
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
@Override
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
myCompassView.updateDirection((float)event.values[0]);
}
};
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
if(sersorrunning){
mySensorManager.unregisterListener(mySensorEventListener);
}
}
}
Modify AndroidManifest.xml to disable the auto-rotate feature, otherwise it will point to wrong direction.
Download the files.
Related Article:
Implement a Simple Horizontal Indicator using SensorManager and SensorEventListener
Thank you for this example
ReplyDeletegreat tutorial!
ReplyDeleteIs there anyway to tweek the direction to something I want to use?
I want my direction instead of being north, to be defined by this formula :
(float) Math.toDegrees(Math.atan2(mLongitude - picLongitude, mLatitude - picLatitude));
But im not able to change it, and keep the values working.. picLongitude and picLatitude are static, the others would depend on the user position.
anu ideia for this?
ReplyDeleteSorry PedroTeixeira,
ReplyDeleteI have no idea right now.
This example is fantastic- I need the compass to work only on Landscape view though- any idea on how to make that happen?
ReplyDeleteHello Nick,
ReplyDeleteRefer here: How to disable the auto-rotate feature?
thank you very much for this sample
ReplyDeletethis is the great tutorial^^
After looking thru your code, I'm thinking it's incorrect.
ReplyDeleteThere is this suspiciously simple method:
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
myCompassView.updateDirection((float)event.values[0]);
}
Basically, it seems like you are using the x component of the force vector as if it were an angle. Can you look at this and let me know what you think?
Never mind my last post.
ReplyDeleteI was using Sensor.TYPE_MAGNETIC_FIELD.