通過前幾天的學習,咱們的天氣預報程序已經能夠把天氣正常的呈現出來了,正如以前說的,如今的APP只能顯示固定地區的天氣,那麼咱們要怎樣才能顯示咱們自己所在地的天氣呢?java
Android系統自己提供了三種定位方式,分別是網絡、基站和GPS,主要利用的是LocationManager、TelephonyManager相關的類庫,可是由於一些緣由,Google的API在國內訪問常常出現問題,因此在這裏我就不對這些API作介紹了,有想了解的能夠自行查詢相關資料。android
除了Android自己提供的定位功能外,在國內也有不少供咱們使用的定位系統,好比百度地圖、高德地圖都提供了相應的功能,我這裏主要介紹一下百度地圖的使用方式。程序員
須要到 http://lbsyun.baidu.com/sdk/download?selected=location 下載定位功能的開發包,而後把開發包的內容放到libs文件夾,這樣咱們就引入了百度地圖定位功能的API。json
而後,開工吧。api
配置Manifest網絡
首先添加定位功能所須要的權限,還記得添加到哪吧。app
<!-- 這個權限用於進行網絡定位 --> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 這個權限用於訪問GPS定位 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <!-- 用於訪問wifi網絡信息,wifi信息會用於進行網絡定位 --> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 獲取運營商信息,用於支持提供運營商信息相關的接口 --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- 這個權限用於獲取wifi的獲取權限,wifi信息會用來進行網絡定位 --> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <!-- 用於讀取手機當前的狀態 --> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- 寫入擴展存儲,向擴展卡寫入數據,用於寫入離線定位數據 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- SD卡讀取權限,用戶寫入離線定位數據 --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <!-- 容許應用讀取低級別的系統日誌文件 --> <uses-permission android:name="android.permission.READ_LOGS" />
而後在application節點內,添加一個service,這個service是運行於後臺的獲取定位的服務,異步
<service android:name="com.baidu.location.f" android:enabled="true" android:process=":remote" > <intent-filter> <action android:name="com.baidu.location.service_v2.2" > </action> </intent-filter> </service>
最後,在application節點內,添加咱們申請的百度地圖API的Accesskey。ide
<meta-data android:name="com.baidu.lbsapi.API_KEY" android:value="YknGmxIoPugT7YrNrG955YLS" />
就這樣,百度地圖的引入就算是完成了。那麼如何在代碼中使用?工具
打開MainActivity.java,咱們須要簡單重構一下代碼。
你可使用Eclipse的重構工具,也能夠手動修改代碼,修改成以下:
@Override protected void onCreate( Bundle savedInstanceState ) { super.onCreate( savedInstanceState ); setContentView( R.layout.activity_main ); ViewUtils.inject( this ); datas = new ArrayList<WeatherDataBean>(); adapter = new WeatherAdapter( getApplicationContext(), datas ); lstWeather.setAdapter( adapter ); getWeather( "北京" ); } private void getWeather( String city ) { HttpUtils http = new HttpUtils(); RequestParams params = new RequestParams(); params.addQueryStringParameter( "location", city ); params.addQueryStringParameter( "output", "json" ); params.addQueryStringParameter( "ak", "YknGmxIoPugT7YrNrG955YLS" ); http.send( HttpMethod.GET, "http://api.map.baidu.com/telematics/v3/weather", params, new RequestCallBack<String>() { @Override public void onSuccess( ResponseInfo<String> responseInfo ) { String weather = responseInfo.result; Gson gson = new Gson(); data = gson.fromJson( weather, BaiduData.class ); datas.clear(); datas.addAll( data.getResults().get( 0 ).getWeather_data() ); adapter.notifyDataSetChanged(); Log.v( "onSuccess", data.toString() ); } @Override public void onFailure( HttpException arg0, String arg1 ) { Log.v( "onFailure", arg1 ); } } ); }
這裏新增長了一個以城市爲參數方法getWeather,作好了重構以後,咱們加入百度的定位功能。
聲明兩個變量:
private LocationClient mLocationClient; private BDLocationListener myListener;
添加初始化這兩個變量的方法,
private void initLocationClient() { mLocationClient = new LocationClient( getApplicationContext() ); myListener = new MyLocationListener(); LocationClientOption option = new LocationClientOption(); option.setLocationMode( LocationMode.Hight_Accuracy ); option.setIsNeedAddress( true ); mLocationClient.setLocOption( option ); mLocationClient.registerLocationListener( myListener ); }
咱們是用到了MyLocationListener,這個類須要自定義,做爲MainActivity的內部類存在,
public class MyLocationListener implements BDLocationListener { @Override public void onReceiveLocation( BDLocation location ) { String city = location.getCity(); getWeather( city ); setTitle( city + "天氣" ); } }
而後,修改onCreate方法,
protected void onCreate( Bundle savedInstanceState ) { super.onCreate( savedInstanceState ); setContentView( R.layout.activity_main ); ViewUtils.inject( this ); datas = new ArrayList<WeatherDataBean>(); adapter = new WeatherAdapter( getApplicationContext(), datas ); lstWeather.setAdapter( adapter ); initLocationClient(); mLocationClient.start(); }
最後,添加onStop方法,
@Override protected void onStop() { super.onStop(); mLocationClient.stop(); }
打完收工。
運行吧,看看是怎樣的效果,反正個人是這樣:
是否是你所在的城市呢?
既然看到效果了,那麼稍微解釋一下上面那些代碼。
首先在界面加載的時候,會調用initLocationClient方法,這個方法初始化了LocationClient和BDLocationListener,LocationClient的做用是異步獲取定位,MyLocationListener則是在LocationClient獲取定位後的回調方法,咱們在這個回調方法中調用了獲取天氣的方法getWeather,而且調用setTitle方法修改了APP頁面的標題爲「城市名+天氣」。
整個流程堪稱完美,只差一點點。
這一點點是什麼地方呢?
就在於咱們每一次獲取天氣以前都須要定位,而你是不會天天都換一個城市的,爲此咱們的程序須要優化一下。優化後的流程爲:
1. 從本地讀取城市,而且同時調用百度定位
2. 若是1中的本地城市不爲空,則獲取天氣
3. 若是1中百度定位的城市跟本地城市一致,就再也不次獲取天氣;若不一致,則覆蓋本地城市,而且從新獲取天氣
這樣作的好處有兩個:
1. 只有第一次啓動APP的時候,本地城市爲空,那麼就是獲取天氣會比較快
2. 即便更換了城市,也能準確應對
既然整理好思路了,那麼就開工吧。
Android本地存儲數據有多種方式,主要有Preference、Sqlite、File這三種,咱們今天使用的是Preference這種方式。
Preference適合存儲輕量數據,如String、Int、Boolean等類型的數據,咱們所須要保存的城市數據就是一個簡單的字符串,很是適合這種方式。
在MainActivity.java 內添加兩個方法,分別爲存儲、讀取數據的方法。
private void saveCity( String city ) { SharedPreferences sharedPreferences = getSharedPreferences( "weather", Context.MODE_PRIVATE ); Editor editor = sharedPreferences.edit(); editor.putString( "city", city ); editor.commit(); } private String readCity() { SharedPreferences sharedPreferences = getSharedPreferences( "weather", Context.MODE_PRIVATE ); return sharedPreferences.getString( "city", "" ); }
有些經驗的程序員一眼就能看明白,Preference中是以Key/Value的形式存儲數據的。
而後修改onCreate方法,
protected void onCreate( Bundle savedInstanceState ) { super.onCreate( savedInstanceState ); setContentView( R.layout.activity_main ); ViewUtils.inject( this ); datas = new ArrayList<WeatherDataBean>(); adapter = new WeatherAdapter( getApplicationContext(), datas ); lstWeather.setAdapter( adapter ); initLocationClient(); mLocationClient.start(); String city = readCity(); if( city != null && city.length() > 0 ) { getWeather( city ); } }
這裏加入了讀取本地城市,而且獲取天氣的邏輯。
接下來修改MyLocationListener,
public class MyLocationListener implements BDLocationListener { @Override public void onReceiveLocation( BDLocation location ) { String city = location.getCity(); String localCity = readCity(); if( !localCity.equals( city ) ) { saveCity( city ); getWeather( city ); } } }
這裏加入了定位的城市和本地城市判斷的邏輯,而且刪掉了對setTitle 方法的調用.
最後,修改getWeather方法,在方法第一行加入對setTitle的調用。
setTitle( city + "天氣" );
打完收工,最後MainActivity.java是這個樣子的:
public class MainActivity extends Activity { @ViewInject( R.id.weather_list ) private ListView lstWeather; private WeatherAdapter adapter; private BaiduData data; private List<WeatherDataBean> datas; private LocationClient mLocationClient; private BDLocationListener myListener; @Override protected void onCreate( Bundle savedInstanceState ) { super.onCreate( savedInstanceState ); setContentView( R.layout.activity_main ); ViewUtils.inject( this ); datas = new ArrayList<WeatherDataBean>(); adapter = new WeatherAdapter( getApplicationContext(), datas ); lstWeather.setAdapter( adapter ); initLocationClient(); mLocationClient.start(); String city = readCity(); if( city != null && city.length() > 0 ) { getWeather( city ); } } private void getWeather( String city ) { setTitle( city + "天氣" ); HttpUtils http = new HttpUtils(); RequestParams params = new RequestParams(); params.addQueryStringParameter( "location", city ); params.addQueryStringParameter( "output", "json" ); params.addQueryStringParameter( "ak", "YknGmxIoPugT7YrNrG955YLS" ); http.send( HttpMethod.GET, "http://api.map.baidu.com/telematics/v3/weather", params, new RequestCallBack<String>() { @Override public void onSuccess( ResponseInfo<String> responseInfo ) { String weather = responseInfo.result; Gson gson = new Gson(); data = gson.fromJson( weather, BaiduData.class ); datas.clear(); datas.addAll( data.getResults().get( 0 ).getWeather_data() ); adapter.notifyDataSetChanged(); Log.v( "onSuccess", data.toString() ); } @Override public void onFailure( HttpException arg0, String arg1 ) { Log.v( "onFailure", arg1 ); } } ); } private void initLocationClient() { mLocationClient = new LocationClient( getApplicationContext() ); // 聲明LocationClient類 myListener = new MyLocationListener(); LocationClientOption option = new LocationClientOption(); option.setLocationMode( LocationMode.Hight_Accuracy ); option.setIsNeedAddress( true ); mLocationClient.setLocOption( option ); mLocationClient.registerLocationListener( myListener ); } @Override protected void onStop() { super.onStop(); mLocationClient.stop(); } public class MyLocationListener implements BDLocationListener { @Override public void onReceiveLocation( BDLocation location ) { String city = location.getCity(); String localCity = readCity(); if( !localCity.equals( city ) ) { saveCity( city ); getWeather( city ); } } } private void saveCity( String city ) { SharedPreferences sharedPreferences = getSharedPreferences( "weather", Context.MODE_PRIVATE ); Editor editor = sharedPreferences.edit(); editor.putString( "city", city ); editor.commit(); } private String readCity() { SharedPreferences sharedPreferences = getSharedPreferences( "weather", Context.MODE_PRIVATE ); return sharedPreferences.getString( "city", "" ); } }
今天的主要任務就是嵌入了百度地圖,重構了代碼,優化了流程,最最重要的是咱們學習了SharedPreferences的使用,這種保存數據方式在作APP的時候是常常會用到的,但願你們能熟練掌握。
請注意,本文用到的key是我我的使用的,請勿將其用於任何商業用途。若是有商業須要,請聯繫我或者自行在百度官網申請Accesskey。
附件是本次的工程文件,點擊下載 http://pan.baidu.com/s/1jG9puYU 。
此係列文章系本人原創,如需轉載,請註明出處 www.liuzhibang.cn