10天学安卓-第六天

经过前几天的学习,我们的天气预报程序已经可以把天气正常的呈现出来了,正如之前说的,现在的APP只能显示固定地区的天气,那么我们要怎样才能显示我们本身所在地的天气呢?

Android定位

Android系统本身提供了三种定位方式,分别是网络、基站和GPS,主要利用的是LocationManager、TelephonyManager相关的类库,但是因为一些原因,Google的API在国内访问经常出现问题,所以在这里我就不对这些API做介绍了,有想了解的可以自行查询相关资料。

百度地图定位

除了Android本身提供的定位功能外,在国内也有很多供我们使用的定位系统,比如百度地图、高德地图都提供了相应的功能,我这里主要介绍一下百度地图的使用方式。

需要到 http://lbsyun.baidu.com/sdk/download?selected=location 下载定位功能的开发包,然后把开发包的内容放到libs文件夹,这样我们就引入了百度地图定位功能的API。

然后,开工吧。

配置Manifest

首先添加定位功能所需要的权限,还记得添加到哪吧。

    <!-- 这个权限用于进行网络定位 -->
    <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。

        <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

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

时间: 2024-11-04 00:42:14

10天学安卓-第六天的相关文章

10天学安卓系列

10天学安卓-第一天 摘要: 说明1:本系列教程仅针对新手入门,高手勿入!说明2:本系列教程均不考虑安卓版本低于4.0的情况.说明3:本系列教程假定您了解一些编程的基础知识,对于Java语言略懂即可.说点废话从什么时候说起呢,应该是很久以前的事情了.2008年的早春,因为工作的原因在日本工作了一个月,当我和同事们在商场闲逛的时候...阅读全文 posted @ 2015-01-17 15:15 Game_over 阅读(2755) | 评论 (9) 编辑 10天学安卓-第二天 摘要: 继续我们的

10天学安卓-第一天

原文:10天学安卓-第一天 说明1:本系列教程仅针对新手入门,高手勿入! 说明2:本系列教程均不考虑安卓版本低于4.0的情况. 说明3:本系列教程假定您了解一些编程的基础知识,对于Java语言略懂即可. 说点废话 从什么时候说起呢,应该是很久以前的事情了. 2008年的早春,因为工作的原因在日本工作了一个月,当我和同事们在商场闲逛的时候,发现了以前从未见过的电子设备,一款是大屏幕(当时可以称得上巨屏了)的手机,一款是超大容量(80G)的MP3,对了,也许聪明的你已经猜到了,一个是iPhone 1

10天学安卓-第三天

原文:10天学安卓-第三天 经过第二天的学习,我们正确的调用了百度天气API,将天气信息显示到了界面上,做到这一步,我们的工作就算是完成1%了,剩下99%的工作就需要不断的润色这个未成形的APP了. 最首要的就是,我们要把那么一大堆字符转换为普通用户可以轻松理解的界面,那么我们来学习一下Android里面的界面布局. 打开res/layout/activity_main.xml文件,切换到Layouts选项卡,可以看到里面有许多项目,GridLayout.LinearLayout.Relativ

10天学安卓-第四天

原文:10天学安卓-第四天 继续昨天的学习. 昨天我们根据取得的天气数据新建了一个视图用来显示各项内容,那么今天我们就把数据显示出来吧!!! 这里我们要把数据和视图联系起来,那么就用到了适配器-Adapter,Android给我们提供了很多Adapter,这里我们用到了BaseAdapter. BaseAdapter(1) 右键点击src/com.demo.weather,选择 New > Class,按照下图填写: 选择[Finish]后,我们就新建了一个BaseAdapter的子类,打开 W

10天学安卓-第五天

原文:10天学安卓-第五天 经过前几天的练习,相信大家已经对如何做出一个简单的界面有了初步的了解,并且已经做出来一个还不错的天气列表了. 今天大家稍事休息,要练习的内容比较少,着重学习一些理论知识,先理清几个概念. Android系统架构 Android系统本质上是Linux系统,但相对于Linux系统,主要在驱动.性能.内存管理.设备管理等一些部分做了比较多的改动,以更适用于移动设备. 从上图可以看到,Android系统架构为四层,分别是Linux内核.系统运行库.应用程序框架以及应用层,每一

10天学安卓-第二天

原文:10天学安卓-第二天 继续我们的学习. 相信我,第一天的工作是最为重要的,通过这些工作,我们把开发安卓所必须的环境.基础条件都配置好了,相信肯定遇到了很多问题,不过,根据我的经验,您会很快解决这些问题的.在第一天的最后,我们终于运行了第一个应用->“Hello, world”. 理论知识 下面我们学习一些理论知识,了解一下安卓系统的架构.组件,有了一些基础知识,才不会感觉迷惑. 先看下整个项目的目录,每个目录都有特定的作用,分别如下: src目录,存放源代码文件. gen目录,由ADT插件

10天学安卓-第八天

昨天郑州雨夹雪,还有冰雹,结果小区就断电了,真是悲剧.第八天的学习就挪到今天了. 经过前几天的学习,我们了解了一些Android的基础知识,并且做出了一个也算实用的天气预报APP,对Android也算得上是入门了,那么今天我们继续改进我们的APP. 这个APP现在只能查看所在城市的天气,那么万一妹子不跟我们一个城市,我们就不能关注到妹子所在城市的天气了,那还怎么嘘寒问暖呢,这个问题是一定要解决的. 如何解决? 那就是需要在一个界面上可以选择城市了,这就用到了数据库了. 我整理了一份所有城市的名单

10天学安卓-第十天

本次是这个教程的最后一篇了,我们的APP开发基本上已经可以宣告完成了,接下来的工作就是如何发布推广运营了. 广告平台 古人云:兵马未动,粮草先行.我们身为APP开发者就需要考虑如何从APP盈利,目前通常的做法主要有APP收费.APP免费+内购及嵌入广告三种方式,我们这个简单的APP想让用户付费是比较有难度的,那么就只能通过嵌入广告的方式来赚取一点广告费了. 我经常使用的广告平台就是Admob了,不过Admob国内被屏蔽,但是广告依然可以展示,各位程序员一定要学习科学上网,否则... 话说回来,国

10天学安卓-第七天

我们上次学习了百度定位以及SharedPreferences的使用,不知道大家有没有注意到我们新加了一个方法: protected void onStop() { super.onStop(); mLocationClient.stop(); } 这个方法的作用是在界面停止的时候,同时停止百度定位功能. 联想到我们还有onCreate,那么这两个方法是做什么用的?是什么原理呢? 这就需要我们来了解一下Activity的生命周期. Activity生命周期 Activity的整个生命周期有七个重要