http://www.iteye.com/topic/1121633
這是我的程序界面
首先建Activity,來監聽GPS狀態變化
- public class GpsViewActivity extends Activity {
- private int minTime = 1000;
- private int minDistance = 0;
- private static final String TAG = "GpsView";
- private LocationManager locationManager;
- private SatellitesView satellitesView;
- private TextView lonlatText;
- private TextView gpsStatusText;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.gps_view_activity);
- gpsStatusText = (TextView) findViewById(R.id.gps_status_text);
- lonlatText = (TextView) findViewById(R.id.lonlat_text);
- satellitesView = (SatellitesView) findViewById(R.id.satellitesView);
- registerListener();
- }
- /**
- * 註冊監聽
- */
- private void registerListener() {
- if (locationManager == null) {
- locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
- }
- //偵聽位置信息(經緯度變化)
- locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
- minTime, minDistance, locationListener);
- // 偵聽GPS狀態,主要是捕獲到的各個衛星的狀態
- locationManager.addGpsStatusListener(gpsStatusListener);
- //TODO:考慮增加監聽傳感器中的方位數據,以使羅盤的北能自動指向真實的北向
- }
- /**
- * 移除監聽
- */
- private void unregisterListener() {
- if (locationManager != null) {
- locationManager.removeGpsStatusListener(gpsStatusListener);
- locationManager.removeUpdates(locationListener);
- }
- }
- /**
- * 座標位置監聽
- */
- private LocationListener locationListener = new LocationListener() {
- @Override
- public void onLocationChanged(Location location) {
- StringBuffer sb = new StringBuffer();
- int fmt = Location.FORMAT_DEGREES;
- sb.append(Location.convert(location.getLongitude(), fmt));
- sb.append(" ");
- sb.append(Location.convert(location.getLatitude(), fmt));
- lonlatText.setText(sb.toString());
- }
- @Override
- public void onStatusChanged(String provider, int status, Bundle extras) {
- gpsStatusText.setText("onStatusChanged");
- }
- @Override
- public void onProviderEnabled(String provider) {
- gpsStatusText.setText("onProviderEnabled");
- }
- @Override
- public void onProviderDisabled(String provider) {
- gpsStatusText.setText("onProviderDisabled");
- }
- };
- /**
- * Gps狀態監聽
- */
- private GpsStatus.Listener gpsStatusListener = new GpsStatus.Listener() {
- public void onGpsStatusChanged(int event) {
- GpsStatus gpsStatus = locationManager.getGpsStatus(null);
- switch (event) {
- case GpsStatus.GPS_EVENT_FIRST_FIX: {
- gpsStatusText.setText("GPS_EVENT_FIRST_FIX");
- // 第一次定位時間UTC gps可用
- // Log.v(TAG,"GPS is usable");
- int i = gpsStatus.getTimeToFirstFix();
- break;
- }
- case GpsStatus.GPS_EVENT_SATELLITE_STATUS: {// 週期的報告衛星狀態
- // 得到所有收到的衛星的信息,包括 衛星的高度角、方位角、信噪比、和僞隨機號(及衛星編號)
- Iterable<GpsSatellite> satellites = gpsStatus.getSatellites();
- List<GpsSatellite> satelliteList = new ArrayList<GpsSatellite>();
- for (GpsSatellite satellite : satellites) {
- // 包括 衛星的高度角、方位角、信噪比、和僞隨機號(及衛星編號)
- /*
- * satellite.getElevation(); //衛星仰角
- * satellite.getAzimuth(); //衛星方位角
- * satellite.getSnr(); //信噪比
- * satellite.getPrn(); //僞隨機數,可以認爲他就是衛星的編號
- * satellite.hasAlmanac(); //衛星曆書
- * satellite.hasEphemeris();
- * satellite.usedInFix();
- */
- satelliteList.add(satellite);
- }
- satellitesView.repaintSatellites(satelliteList);
- gpsStatusText.setText("GPS_EVENT_SATELLITE_STATUS:"
- + satelliteList.size());
- break;
- }
- case GpsStatus.GPS_EVENT_STARTED: {
- gpsStatusText.setText("GPS_EVENT_STARTED");
- break;
- }
- case GpsStatus.GPS_EVENT_STOPPED: {
- gpsStatusText.setText("GPS_EVENT_STOPPED");
- break;
- }
- default:
- gpsStatusText.setText("GPS_EVENT:" + event);
- break;
- }
- }
- };
- @Override
- protected void onResume() {
- super.onResume();
- registerListener();
- }
- @Override
- protected void onDestroy() {
- unregisterListener();
- super.onDestroy();
- }
- }
然後,是創建一個類來用於繪製GPS衛星的分佈,這裏我是用繼承的SurfaceView的類來實現的,我這裏模仿了SDK中提供的範例中的LunarLander遊戲的繪製方法,把繪製工作交給一個線程完成,如下,各個繪製函數註釋已經寫得很清楚了,我不再重複了:
- package ylybbs.study.mygpstest;
- import java.util.List;
- import java.util.concurrent.LinkedBlockingQueue;
- import android.content.Context;
- import android.content.res.Resources;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.graphics.PaintFlagsDrawFilter;
- import android.graphics.Paint.Align;
- import android.location.GpsSatellite;
- import android.view.SurfaceHolder;
- public class DrawSatellitesThread extends Thread {
- // 衛星圖
- private Bitmap satelliteBitmap;
- private Bitmap compassBitmap;
- private Paint paint;
- /** Handle to the surface manager object we interact with */
- private SurfaceHolder surfaceHolder;
- /** Indicate whether the surface has been created & is ready to draw */
- private boolean isRunning = false;
- private int cx=0;
- private int cy=0;
- private int compassRadius = 434 / 2;
- PaintFlagsDrawFilter pfd = new PaintFlagsDrawFilter(0,
- Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
- public static final LinkedBlockingQueue<List<GpsSatellite>> queue =
- new LinkedBlockingQueue<List<GpsSatellite>>(60);
- public DrawSatellitesThread(SurfaceHolder surfaceHolder, Context context) {
- this.surfaceHolder = surfaceHolder;
- Resources res = context.getResources();
- // cache handles to our key sprites & other drawables
- compassBitmap = BitmapFactory.decodeResource(res, R.drawable.compass);
- compassRadius = compassBitmap.getWidth() / 2;
- satelliteBitmap = BitmapFactory.decodeResource(res,
- R.drawable.satellite_mark);
- paint = new Paint();
- paint.setSubpixelText(true);
- paint.setAntiAlias(true);
- paint.setFilterBitmap(true);
- paint.setColor(Color.RED);
- paint.setTextSize(24);
- paint.setTextAlign(Align.CENTER);
- }
- /* Callback invoked when the surface dimensions change. */
- public void setSurfaceSize(int width, int height) {
- synchronized (surfaceHolder) {
- cx = width / 2;
- cy = height / 2;
- }
- }
- @Override
- public void run() {
- List<GpsSatellite> list=null;
- Canvas c = null;
- try {
- c = surfaceHolder.lockCanvas(null);
- //初始化畫板的中心座標
- cx = c.getWidth() / 2;
- cy = c.getWidth() / 2;
- synchronized (surfaceHolder) {
- doDraw(c,null);
- }
- } finally {
- if (c != null) {
- surfaceHolder.unlockCanvasAndPost(c);
- }
- }
- while (isRunning) {
- try{
- list = queue.take();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- try {
- c = surfaceHolder.lockCanvas(null);
- synchronized (surfaceHolder) {
- doDraw(c,list);
- }
- } finally {
- if (c != null) {
- surfaceHolder.unlockCanvasAndPost(c);
- }
- }
- }
- }
- public void setRunning(boolean b) {
- isRunning = b;
- }
- public void repaintSatellites(List<GpsSatellite> list) {
- synchronized (surfaceHolder) {
- try {
- queue.offer(list);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- /**
- * 繪製背景羅盤
- * @param canvas
- * @param cx 羅盤中心點位於畫布上的X座標
- * @param cy 羅盤中心點位於畫布上的Y座標
- * @param r 羅盤的半徑
- */
- private void drawBackground(Canvas canvas, int cx, int cy, int r) {
- int x = cx - r;
- int y = cy - r;
- canvas.drawBitmap(compassBitmap, x, y, paint);
- }
- /**
- * 將角度轉換爲弧度,以用於三角函數的運算
- *
- * @param degree
- * @return
- */
- private double degreeToRadian(double degree) {
- return (degree * Math.PI) / 180.0d;
- }
- /*
- * 將SNR的值,轉化爲通用的信號強度級別,主要用於在繪製衛星時,通過顏色來表明它的信號強度,暫時沒用到
- * SNR is mapped to signal strength [0,1,4-9] COMMENT SNR: >500 >100 >50 >10
- * >5 >0 bad n/a COMMENT sig: 9 8 7 6 5 4 1 0 COMMENT
- */
- private int snrToSignalLevel(float snr) {
- int level = 0;
- if (snr >= 0 && snr < 5) {
- level = 4;
- } else if (snr >= 5 && snr < 10) {
- level = 5;
- } else if (snr >= 10 && snr < 50) {
- level = 6;
- } else if (snr >= 50 && snr < 100) {
- level = 7;
- } else if (snr >= 100 && snr < 500) {
- level = 8;
- } else if (snr >= 500) {
- level = 9;
- }
- return level;
- }
- /**
- * 在背景羅盤上繪製衛星
- * @param canvas
- * @param satellite
- * @param cx 中心圓點的X座標
- * @param cy 中心圓點的Y座標
- * @param r 羅盤背景的半徑
- */
- private void drawSatellite(Canvas canvas,GpsSatellite satellite, int cx, int cy, int r) {
- /**
- * GPS衛星導航儀通常選用仰角大於5º,小於85º。 因爲當衛星仰角大於85º時,L1波段的電離層折射誤差較大,故規定仰角大於85º時,
- * 定位無效,不進行數據更新。而衛星仰角越小,則對流層折射誤差越大,故一般選用仰角大於5º的衛星來定位。
- */
- //得到仰角
- float elevation = satellite.getElevation();
- //通過仰角,計算出這個衛星應該繪製到離圓心多遠的位置,這裏用的是角度的比值
- double r2 = r * ((90.0f - elevation) / 90.0f);
- /*得到方位角(與正北向也就是Y軸順時針方向的夾角,注意我們通常幾何上的角度
- * 是與X軸正向的逆時針方向的夾角),在計算X,Y座標的三角函數時,要做轉換
- */
- double azimuth = satellite.getAzimuth();
- /*
- * 轉換成XY座標系中的夾角,方位角是與正北向也就是Y軸順時針方向的夾角,
- * 注意我們通常幾何上的角度是與X軸正向的逆時針方向的夾角),
- * 在計算X,Y座標的三角函數時,要做轉換
- */
- double radian = degreeToRadian(360-azimuth + 90);
- double x = cx + Math.cos(radian) * r2;
- double y = cy + Math.sin(radian) * r2;
- //得到衛星圖標的半徑
- int sr = satelliteBitmap.getWidth() / 2;
- //以x,y爲中心繪製衛星圖標
- canvas.drawBitmap(satelliteBitmap, (float) (x - sr), (float) (y - sr),paint);
- //在衛星圖標的位置上繪出文字(衛星編號及信號強度)
- int snr=(int)satellite.getSnr();
- int signLevel=snrToSignalLevel(snr); //暫時不用
- String info = String.format("#%s_%s", satellite.getPrn(), snr);
- canvas.drawText(info, (float) (x), (float) (y), paint);
- }
- private void doDraw(Canvas canvas, List<GpsSatellite> satellites) {
- if (canvas != null) {
- // 繪製背景羅盤
- drawBackground(canvas, cx, cy, compassRadius);
- //繪製衛星分佈
- if (satellites != null) {
- for (GpsSatellite satellite : satellites) {
- drawSatellite(canvas,satellite, cx, cy, compassRadius);
- }
- }
- }
- }
- }
此代碼編譯發佈到設備後,搜到星後,能運行十幾秒鐘正常後,就會掛掉,原因暫時沒分析出來,看繪出的界面應該正確,可能是繪圖過程中狀態的過程可能會有死鎖之類的,關鍵是解決不好狀態更新與繪製過程之間的同步問題,有可能沒繪完上一個畫面,這時GPS狀態又有了新的更新,如果此是繪圖那邊加了鎖,我不知道這邊的listener調用會出現什麼情況。其實GPS的狀態更新,其中的衛星狀態中的衛星數目或是信號強弱的更新這種狀態變化是可以漏掉中間的某些同類事件,因爲用戶只關注衛星最新的分佈結果。有可手能幫我分析一下什麼問題嗎?或是有更好的解決方案?
附上我的項目源碼: