使用百度地圖API實現駕車導航

進入應用後首先顯示藍色點為當前位置,可以輸入目的地來形成導航線路(圖1),也可以點選地圖上任意點來形成導航線路(圖2,3),選定點後,在地圖上會標註紅色定位點,點擊開始導航按鈕後便會形成最佳駕車線路。

接下來看看實現步驟:
首先是工程結構
         
其中libs下面是申請百度開發者後到地圖API下下載Android的地圖支持包並導入工程,這裡不再細說。
然後是佈局文件:
[html] 
<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:orientation="vertical" > 
 
    <LinearLayout 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" 
        android:orientation="horizontal" > 
 
        <TextView 
            android:layout_width="wrap_content" 
            android:layout_height="wrap_content" 
            android:text="目的地:" 
            android:textSize="17sp" /> 
 
        <EditText 
            android:id="@+id/et_destination" 
            android:layout_width="fill_parent" 
            android:hint="輸入目的地名稱或在地圖上點選" 
            android:textSize="14sp" 
            android:layout_height="wrap_content" /> 
    </LinearLayout> 
 
    <LinearLayout 
        android:layout_width="fill_parent" 
        android:layout_height="wrap_content" > 
 
        <Button 
            android:id="@+id/btn_navi" 
            android:layout_width="fill_parent" 
            android:layout_height="wrap_content" 
            android:layout_weight="1" 
            android:text="開始導航" /> 
 
        <Button 
            android:id="@+id/btn_clear" 
            android:layout_width="fill_parent" 
            android:layout_height="wrap_content" 
            android:layout_weight="1" 
            android:text="清除路線" /> 
    </LinearLayout> 
 
    <com.baidu.mapapi.MapView 
        android:id="@+id/bmapsView" 
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent" 
        android:clickable="true" /> 
 
</LinearLayout> 

然後是實現自定義的地圖圖層MyItemizedOverlay.java,這個類的作用是實現可點擊地圖圖層的作用,點擊地圖後,便可以顯示當前位置的經緯度,並可設置為目的地。
[java] 
/**
 * 自定義圖層
 * @author Ryan
 */ 
public class MyItemizedOverlay extends ItemizedOverlay<OverlayItem> { 
     
    private ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();     
    private Context context; 
     
    public MyItemizedOverlay(Context context,Drawable drawale) { 
        super(boundCenterBottom(drawale));       
        this.context=context; 
    } 
     
    @Override 
    protected OverlayItem createItem(int i) { 
        return mOverlays.get(i); 
    } 
     
    @Override 
    public int size() { 
        return mOverlays.size(); 
    } 
     
    // 點擊地圖標註顯示的內容 
    @Override 
    protected boolean onTap(int index) { 
        //這個方法的重寫彈出信息等 
        return true; 
    } 
     
    @Override 
    public void draw(Canvas canvas, MapView mapView, boolean shadow) { 
        super.draw(canvas, mapView, shadow); 
    } 
     
    // Define a method in order to add new OverlayItems to our ArrayList 
    public void addOverlay(OverlayItem overlay) { 
        // add OverlayItems 
        mOverlays.add(overlay); 
        populate(); 
    } 
     
    //該方法的重寫可以相應點擊圖標的區域內還是外 
    @Override 
    public boolean onTap(GeoPoint p, MapView mapView) { 
        final SharedPreferences sharedPreferences = context.getSharedPreferences("navigation_pre", Context.MODE_WORLD_WRITEABLE); 
         
        //p獲取的經緯度數據是整型變量,需要轉換為float類型 
        final float lat=p.getLatitudeE6(); 
        final float lon=p.getLongitudeE6(); 
        final MapView map = mapView; 
         
        float latitude = sharedPreferences.getFloat("lat", 0); 
        if (latitude == 0) { 
            AlertDialog.Builder builder = new AlertDialog.Builder(this.context); 
            builder.setTitle("設置目的地"); 
            builder.setMessage("設置選中的點為目的地嗎?"); 
            builder.setPositiveButton("確定",new OnClickListener() { 
                 
                @Override 
                public void onClick(DialogInterface dialog, int which) { 
                    List<Overlay> overlays = map.getOverlays(); 
                    GeoPoint gpoint = new GeoPoint((int)lat,(int)lon); 
                    OverlayItem overlayitem = new OverlayItem(gpoint, "title", "content"); 
                    Drawable drawale = context.getResources().getDrawable(R.drawable.current_mark); 
                    MyItemizedOverlay iconOverlay = new MyItemizedOverlay(context,drawale); 
                    // 添加圖層 
                    iconOverlay.addOverlay(overlayitem); 
                    overlays.add(iconOverlay); 
                    map.getController().animateTo(gpoint); 
                     
                    Editor editor = sharedPreferences.edit(); 
                    editor.putFloat("lat", lat); 
                    editor.putFloat("lon", lon); 
                    editor.commit(); 
                    Toast.makeText(context, "緯度:"+lat / 1E6+"\n經度:"+lon / 1E6, Toast.LENGTH_SHORT).show();   
                } 
            }); 
            builder.setNegativeButton("取消", null); 
            builder.create().show(); 
        }else { 
//          AlertDialog.Builder builder = new AlertDialog.Builder(this.context); 
//          builder.setTitle("設置目的地"); 
//          builder.setMessage("已經設置過線路,請先點擊清除路線按鈕清除當前路線"); 
//          builder.setNegativeButton("確定", null); 
//          builder.create().show(); 
            Toast.makeText(context, "已經設置過線路,請先點擊清除路線按鈕清除當前路線",Toast.LENGTH_SHORT).show();   
             
        } 
         
        return super.onTap(p, mapView); 
    }    

最後是主Activity實現類,註釋中有詳細說明:
[java] 
public class NavigationDemoActivity extends MapActivity { 
    //Map key 
    private String mMapKey = "你的MapKey,到百度開發者官網申請"; 
     
    private EditText destinationEditText = null; 
    private Button startNaviButton = null; 
    private Button clearButton = null; 
    private MapView mapView = null; 
    private BMapManager mMapManager = null; 
    private MyLocationOverlay myLocationOverlay = null; 
    //onResume時註冊此listener,onPause時需要Remove,註意此listener不是Android自帶的,是百度API中的 
    private LocationListener locationListener = null; 
    //搜索模塊 
    private MKSearch searchModel = null; 
    private GeoPoint pt = null; 
    private SharedPreferences sharedPreferences; 
     
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        requestWindowFeature(Window.FEATURE_NO_TITLE); 
        setContentView(R.layout.main); 
        //彈出使用說明對話框 
        showIntroduceDialog(); 
         
        sharedPreferences = this.getSharedPreferences("navigation_pre", Context.MODE_WORLD_WRITEABLE); 
        destinationEditText = (EditText) this.findViewById(R.id.et_destination); 
        startNaviButton = (Button) this.findViewById(R.id.btn_navi); 
        clearButton = (Button) this.findViewById(R.id.btn_clear); 
         
        //初始化地圖管理器 
        mMapManager = new BMapManager(getApplication()); 
        mMapManager.init(mMapKey, new MyGeneralListener()); 
        super.initMapActivity(mMapManager); 
         
        mapView = (MapView) this.findViewById(R.id.bmapsView); 
        //設置啟用內置的縮放控件 
        mapView.setBuiltInZoomControls(true);   
        //設置在縮放動畫過程中也顯示overlay,默認為不繪制 
//        mapView.setDrawOverlayWhenZooming(true); 
        //設置初始化地圖的縮放級別 
        mapView.getController().setZoom(16); 
        mapView.setClickable(true); 
         
        //獲取當前位置層 
        myLocationOverlay = new MyLocationOverlay(this, mapView); 
        //將當前位置的層添加到地圖底層中 
        mapView.getOverlays().add(myLocationOverlay); 
         
        //添加可點選地圖獲取目的地的層 
        addTapOverLay(); 
         
        //註冊定位事件 
        locationListener = new LocationListener(){ 
 
            @Override 
            public void onLocationChanged(Location location) { 
                if (location != null){ 
                    //生成GEO類型坐標並在地圖上定位到該坐標標示的地點 
                     pt = new GeoPoint((int)(location.getLatitude() * 1e6), 
                            (int)(location.getLongitude() * 1e6)); 
                     mapView.getController().animateTo(pt); 
                } 
            } 
        }; 
         
        //初始化搜索模塊 
        searchModel = new MKSearch(); 
        //設置路線策略為最短距離 
        searchModel.setDrivingPolicy(MKSearch.ECAR_DIS_FIRST); 
        searchModel.init(mMapManager, new MKSearchListener() { 
            //獲取駕車路線回調方法 
            @Override 
            public void onGetDrivingRouteResult(MKDrivingRouteResult res, int error) { 
                // 錯誤號可參考MKEvent中的定義 
                if (error != 0 || res == null) { 
                    Toast.makeText(NavigationDemoActivity.this, "抱歉,未找到結果", Toast.LENGTH_SHORT).show(); 
                    return; 
                } 
                RouteOverlay routeOverlay = new RouteOverlay(NavigationDemoActivity.this, mapView); 
                 
                // 此處僅展示一個方案作為示例 
                MKRoute route = res.getPlan(0).getRoute(0); 
                int distanceM = route.getDistance(); 
                String distanceKm = String.valueOf(distanceM / 1000) +"."+String.valueOf(distanceM % 1000); 
                System.out.println("距離:"+distanceKm+"公裡—節點數量:"+route.getNumSteps()); 
                for (int i = 0; i < route.getNumSteps(); i++) { 
                    MKStep step = route.getStep(i); 
                    System.out.println("節點信息:"+step.getContent()); 
                    System.out.println("經度:"+step.getPoint().getLongitudeE6() / 1E6 +" 緯度:"+step.getPoint().getLatitudeE6() / 1E6); 
                } 
                routeOverlay.setData(route); 
                mapView.getOverlays().clear(); 
                mapView.getOverlays().add(routeOverlay); 
                mapView.invalidate(); 
                mapView.getController().animateTo(res.getStart().pt); 
            } 
             
            //以下兩種方式和上面的駕車方案實現方法一樣 
            @Override 
            public void onGetWalkingRouteResult(MKWalkingRouteResult res, int error) { 
                //獲取步行路線 
            } 
             
            @Override 
            public void onGetTransitRouteResult(MKTransitRouteResult arg0, int arg1) { 
                //獲取公交線路 
            } 
             
            @Override 
            public void onGetBusDetailResult(MKBusLineResult arg0, int arg1) { 
            } 
            @Override 
            public void onGetAddrResult(MKAddrInfo arg0, int arg1) { 
            } 
            @Override 
            public void onGetSuggestionResult(MKSuggestionResult arg0, int arg1) { 
            } 
            @Override 
            public void onGetPoiResult(MKPoiResult arg0, int arg1, int arg2) { 
            } 
        }); 
         
        startNaviButton.setOnClickListener(new OnClickListener() { 
             
            @Override 
            public void onClick(View v) { 
                String destination = destinationEditText.getText().toString(); 
                 
                //設置起始地(當前位置) 
                MKPlanNode startNode = new MKPlanNode(); 
                startNode.pt = pt; 
                 
                //設置目的地 
                MKPlanNode endNode = new MKPlanNode();  
                 
                float lat = sharedPreferences.getFloat("lat", 0); 
                float lon = sharedPreferences.getFloat("lon", 0); 
                 
                if (lat != 0 && lon != 0) { 
                    endNode.pt = new GeoPoint((int)lat,(int)lon); 
                }else if (!destination.equals("")) { 
                    endNode.name = destination; 
                }else { 
                    Toast.makeText(NavigationDemoActivity.this, "請輸入或點選目的地",Toast.LENGTH_SHORT).show(); 
                    return; 
                } 
                 
                //展開搜索的城市 
                String city = getResources().getString(R.string.beijing); 
                searchModel.drivingSearch(city, startNode, city, endNode); 
                 
                //步行路線 
//              searchModel.walkingSearch(city, startNode, city, endNode); 
                //公交路線 
//              searchModel.transitSearch(city, startNode, endNode); 
            } 
        }); 
         
        //清除路線按鈕事件 
        clearButton.setOnClickListener(new OnClickListener() { 
             
            @Override 
            public void onClick(View v) { 
                List<Overlay> overlays = mapView.getOverlays(); 
                 
                if (sharedPreferences.getFloat("lon", 0) != 0) { 
                    //移除頂層的路線圖層 
                    overlays.remove(overlays.size() – 1); 
                    //添加用戶當前位置圖層 
                    overlays.add(myLocationOverlay); 
                    //地圖更新定位到當前位置 
                    mapView.getController().animateTo(pt); 
                     
                    //清除存儲的經緯度信息 
                    Editor editor = sharedPreferences.edit(); 
                    editor.clear(); 
                    editor.commit(); 
                     
                    //添加可點選圖層 
                    addTapOverLay(); 
                     
                }else if (!destinationEditText.getText().toString().equals("")) { 
                    overlays.remove(overlays.size() – 1); 
                    overlays.add(myLocationOverlay); 
                    mapView.getController().animateTo(pt); 
                    addTapOverLay(); 
                }else { 
                    Toast.makeText(NavigationDemoActivity.this, "沒有設置路線", Toast.LENGTH_SHORT).show(); 
                } 
            } 
        }); 
    } 
     
    public void showIntroduceDialog(){ 
        AlertDialog.Builder builder = new AlertDialog.Builder(this); 
        builder.setTitle("使用說明"); 
        StringBuilder sb = new StringBuilder(); 
        sb.append("1.地圖中藍色點為當前位置;\n\n"); 
        sb.append("2.雙指捏合或點擊縮放按鈕可以縮放地圖;\n\n"); 
        sb.append("3.在輸入框中輸入目的地或在地圖上點選目的地;\n\n"); 
        sb.append("4.選取目的地後點擊按鈕開始導航;\n\n"); 
        sb.append("5.點擊清除按鈕取消本次導航路線;"); 
        builder.setMessage(sb.toString()); 
        builder.setNegativeButton("確定", null); 
        builder.create().show(); 
    } 
     
    public void addTapOverLay(){ 
        //以一副透明圖片標示可點選圖層,這裡在地圖上不顯示出來,隻提供可點選目的地功能 
        GeoPoint gpoint = new GeoPoint((int) (39.914714 * 1E6), (int) (116.404269 * 1E6)); 
        OverlayItem overlayitem = new OverlayItem(gpoint, "title", "content"); 
        Drawable drawale = getResources().getDrawable(R.drawable.current); 
        MyItemizedOverlay iconOverlay = new MyItemizedOverlay(NavigationDemoActivity.this,drawale); 
        // 添加圖層 
        iconOverlay.addOverlay(overlayitem); 
        mapView.getOverlays().add(iconOverlay); 
    } 
     
    @Override 
    protected void onResume() { 
        mMapManager.getLocationManager().requestLocationUpdates(locationListener); 
        myLocationOverlay.enableMyLocation(); 
        myLocationOverlay.enableCompass(); // 打開指南針 
        mMapManager.start(); 
         
        super.onResume(); 
    } 
     
    @Override 
    protected void onPause() { 
        mMapManager.getLocationManager().removeUpdates(locationListener); 
        myLocationOverlay.disableMyLocation();//顯示當前位置 
        myLocationOverlay.disableCompass(); // 關閉指南針 
        mMapManager.stop(); 
        super.onPause(); 
    } 
 
    @Override 
    protected boolean isRouteDisplayed() { 
        // TODO Auto-generated method stub 
        return false; 
    } 
     
    //按物理返回鍵退出應用時清空存儲的經緯度信息 
    @Override 
    public boolean dispatchKeyEvent(KeyEvent event) { 
        if (event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_BACK) { 
            Editor editor = sharedPreferences.edit(); 
            editor.clear(); 
            editor.commit(); 
        } 
        return super.dispatchKeyEvent(event); 
    } 
     
    // 常用事件監聽,用來處理通常的網絡錯誤,授權驗證錯誤等 
    class MyGeneralListener implements MKGeneralListener { 
            @Override 
            public void onGetNetworkState(int iError) { 
                Log.d("MyGeneralListener", "onGetNetworkState error is "+ iError); 
                Toast.makeText(NavigationDemoActivity.this, "您的網絡出錯啦!", 
                        Toast.LENGTH_LONG).show(); 
            } 
 
            @Override 
            public void onGetPermissionState(int iError) { 
                Log.d("MyGeneralListener", "onGetPermissionState error is "+ iError); 
                if (iError ==  MKEvent.ERROR_PERMISSION_DENIED) { 
                    // 授權Key錯誤: 
                    Toast.makeText(NavigationDemoActivity.this,  
                            "請在BMapApiDemoApp.java文件輸入正確的授權Key!", 
                            Toast.LENGTH_LONG).show(); 
                } 
            } 
        } 

最後是配置文件:
[html]
<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    package="com.ericssonlabs" 
    android:versionCode="1" 
    android:versionName="1.0" > 
 
    <uses-sdk android:minSdkVersion="8" /> 
 
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission> 
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission> 
    <uses-permission android:name="android.permission.INTERNET"></uses-permission> 
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> 
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>   
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>  
    <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission> 
     
    <supports-screens android:largeScreens="true" 
        android:normalScreens="true" android:smallScreens="true" 
        android:resizeable="true" android:anyDensity="true"/> 
    <uses-sdk android:minSdkVersion="3"></uses-sdk> 
 
    <application 
        android:icon="@drawable/ic_launcher" 
        android:label="@string/app_name" > 
        <activity 
            android:name=".NavigationDemoActivity" 
            android:label="@string/app_name" > 
            <intent-filter> 
                <action android:name="android.intent.action.MAIN" /> 
 
                <category android:name="android.intent.category.LAUNCHER" /> 
            </intent-filter> 
        </activity> 
    </application> 
 
</manifest> 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。