10天學安卓-第六天

通過前幾天的學習,咱們的天氣預報程序已經能夠把天氣正常的呈現出來了,正如以前說的,如今的APP只能顯示固定地區的天氣,那麼咱們要怎樣才能顯示咱們自己所在地的天氣呢?java

Android定位

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();
    }

 

打完收工。

 

運行吧,看看是怎樣的效果,反正個人是這樣:

device-2015-01-22-161410

 

是否是你所在的城市呢?

 

既然看到效果了,那麼稍微解釋一下上面那些代碼。

首先在界面加載的時候,會調用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

相關文章
相關標籤/搜索