通过前几天的学习,咱们的天气预报程序已经能够把天气正常的呈现出来了,正如以前说的,如今的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