10天學安卓-第八天

昨天鄭州雨夾雪,還有冰雹,結果小區就斷電了,真是悲劇。第八天的學習就挪到今天了。android

通過前幾天的學習,咱們瞭解了一些Android的基礎知識,而且作出了一個也算實用的天氣預報APP,對Android也算得上是入門了,那麼今天咱們繼續改進咱們的APP。數據庫

這個APP如今只能查看所在城市的天氣,那麼萬一妹子不跟咱們一個城市,咱們就不能關注到妹子所在城市的天氣了,那還怎麼噓寒問暖呢,這個問題是必定要解決的。json

 

如何解決?api

那就是須要在一個界面上能夠選擇城市了,這就用到了數據庫了。緩存

我整理了一份全部城市的名單:app

北京市,天津市,上海市,重慶市,邯鄲市,石家莊市,保定市,張家口市,承德市,唐山市,廊坊市,滄州市,衡水市,邢臺市,秦皇島市,朔州市,忻州市,太原市,大同市,陽泉市,晉中市,長治市,晉城市,臨汾市,呂梁市,運城市,瀋陽市,鐵嶺市,大連市,鞍山市,撫順市,本溪市,丹東市,錦州市,營口市,阜新市,遼陽市,朝陽市,盤錦市,葫蘆島市,長春市,吉林市,延邊朝鮮族自治州,四平市,通化市,白城市,遼源市,松原市,白山市,哈爾濱市,齊齊哈爾市,雞西市,牡丹江市,七臺河市,佳木斯市,鶴崗市,雙鴨山市,綏化市,黑河市,大興安嶺地區,伊春市,大慶市,南京市,無錫市,鎮江市,蘇州市,南通市,揚州市,鹽城市,徐州市,淮安市,連雲港市,常州市,泰州市,宿遷市,舟山市,衢州市,杭州市,湖州市,嘉興市,寧波市,紹興市,溫州市,麗水市,金華市,台州市,合肥市,蕪湖市,蚌埠市,淮南市,馬鞍山市,淮北市,銅陵市,安慶市,黃山市,滁州市,阜陽市,宿州市,巢湖市,六安市,亳州市,池州市,宣城市,福州市,廈門市,寧德市,莆田市,泉州市,漳州市,龍巖市,三明市,南平市,鷹潭市,新餘市,南昌市,九江市,上饒市,撫州市,宜春市,吉安市,贛州市,景德鎮市,萍鄉市,菏澤市,濟南市,青島市,淄博市,德州市,煙臺市,濰坊市,濟寧市,泰安市,臨沂市,濱州市,東營市,威海市,棗莊市,日照市,萊蕪市,聊城市,商丘市,鄭州市,安陽市,新鄉市,許昌市,平頂山市,信陽市,南陽市,開封市,洛陽市,濟源市,焦做市,鶴壁市,濮陽市,周口市,漯河市,駐馬店市,三門峽市,武漢市,襄樊市,鄂州市,孝感市,黃岡市,黃石市,咸寧市,荊州市,宜昌市,恩施土家族苗族自治州,神農架林區,十堰市,隨州市,荊門市,仙桃市,天門市,潛江市,岳陽市,長沙市,湘潭市,株洲市,衡陽市,郴州市,常德市,益陽市,婁底市,邵陽市,湘西土家族苗族自治州,張家界市,懷化市,永州市,廣州市,汕尾市,陽江市,揭陽市,茂名市,惠州市,江門市,韶關市,梅州市,汕頭市,深圳市,珠海市,佛山市,肇慶市,湛江市,中山市,河源市,清遠市,雲浮市,潮州市,東莞市,蘭州市,金昌市,白銀市,天水市,嘉峪關市,武威市,張掖市,平涼市,酒泉市,慶陽市,定西市,隴南市,臨夏回族自治州,甘南藏族自治州,成都市,攀枝花市,自貢市,綿陽市,南充市,達州市,遂寧市,廣安市,巴中市,瀘州市,宜賓市,資陽市,內江市,樂山市,眉山市,涼山彝族自治州,雅安市,甘孜藏族自治州,阿壩藏族羌族自治州,德陽市,廣元市,貴陽市,遵義市,安順市,黔南布依族苗族自治州,黔東南苗族侗族自治州,銅仁地區,畢節地區,六盤水市,黔西南布依族苗族自治州,海口市,三亞市,五指山市,瓊海市,儋州市,文昌市,萬寧市,東方市,澄邁縣,定安縣,屯昌縣,臨高縣,白沙黎族自治縣,昌江黎族自治縣,樂東黎族自治縣,陵水黎族自治縣,保亭黎族苗族自治縣,瓊中黎族苗族自治縣,西雙版納傣族自治州,德宏傣族景頗族自治州,昭通市,昆明市,大理白族自治州,紅河哈尼族彝族自治州,曲靖市,保山市,文山壯族苗族自治州,玉溪市,楚雄彝族自治州,普洱市,臨滄市,怒江傈傈族自治州,迪慶藏族自治州,麗江市,海北藏族自治州,西寧市,海東地區,黃南藏族自治州,海南藏族自治州,果洛藏族自治州,玉樹藏族自治州,海西蒙古族藏族自治州,西安市,咸陽市,延安市,榆林市,渭南市,商洛市,安康市,漢中市,寶雞市,銅川市,防城港市,南寧市,崇左市,來賓市,柳州市,桂林市,梧州市,賀州市,貴港市,玉林市,百色市,欽州市,河池市,北海市,拉薩市,日喀則地區,山南地區,林芝地區,昌都地區,那曲地區,阿里地區,銀川市,石嘴山市,吳忠市,固原市,中衛市,塔城地區,哈密地區,和田地區,阿勒泰地區,克孜勒蘇柯爾克孜自治州,博爾塔拉蒙古自治州,克拉瑪依市,烏魯木齊市,石河子市,昌吉回族自治州,五家渠市,吐魯番地區,巴音郭楞蒙古自治州,阿克蘇地區,阿拉爾市,喀什地區,圖木舒克市,伊犁哈薩克自治州,呼倫貝爾市,呼和浩特市,包頭市,烏海市,烏蘭察布市,通遼市,赤峯市,鄂爾多斯市,巴彥淖爾市,錫林郭勒盟,興安盟,阿拉善盟,臺北市,高雄市,基隆市,臺中市,臺南市,新竹市,嘉義市,澳門特別行政區,香港特別行政區框架

 

這裏基本囊括了國內大部分城市,咱們作一個界面從這裏選擇就行了。ide

開工。工具

 

新建一個Activity,取名爲ChooseCityActivity,而且修改AndroidManifest.xml,在application節點添加一個activity,以下:學習

<activity android:name="com.demo.weather.ChooseCityActivity"></activity>

 

這樣就添加了一個新的Activity到APP中,不過如今仍是空的,沒有內容,不着急,咱們過會兒會添加進去。

 

而後修改MainActivity,添加兩個方法:

    @Override
    public boolean onCreateOptionsMenu( Menu menu )
    {
        super.onCreateOptionsMenu( menu );
        menu.add( Menu.NONE, Menu.FIRST + 1, 0, "添加城市" ).setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS );
        return true;
    }

    @Override
    public boolean onOptionsItemSelected( MenuItem item )
    {
        if( item.getItemId() == Menu.FIRST + 1 )
        {
            startActivityForResult( new Intent( getApplicationContext(), ChooseCityActivity.class ), 99 );
        }

        return super.onOptionsItemSelected( item );
    }

 

第一個方法的做用是在右上角添加了一個名爲「添加城市」的按鈕,第二個方法的做用就是點擊了「添加城市」的按鈕後,會跳轉到咱們新建的那個Activity。

 

ActionBar

咱們這裏用到了ActionBar,一個完整的ActionBar是這樣子的:

2224215266067739940

一共分爲四部分,

1. App Icon,也可替換爲任意的圖標

2. View Controller,這部分爲下拉菜單或者Tab選項卡,或者是App Title

3. Action Item,這部分就是咱們上面那兩個方法進行設置的,每個Action Item都會包含文字、圖標

4. Action Overflow,若是第三部分的Action Item比較多顯示不全的時候,就會在這裏以列表的形式展現

每個Action Item均可以經過調用setShowAsAction來設置它的展現方式,在前面咱們添加了一個Action Item,而且設置爲SHOW_AS_ACTION_ALWAYS,這就要求這個Item是必須顯示在第三部分的。

 

瞭解了一些ActionBar的基礎知識後,咱們繼續以前的學習。

如今的界面應該是這個樣子的:

device-2015-01-25-143330

 

點擊「添加城市」後,就跳轉到了一個空白頁面。

 

跳轉+傳值

Android中頁面跳轉提供了兩種方法,startActivityForResult和startActivity,這兩個方法區別在於可否將結果回傳。

 

傳值

假如MainActivity須要向ChooseCityActivity傳值,能夠這麼作:

            Intent intent = new Intent( getApplicationContext(), ChooseCityActivity.class );
            intent.putExtra( "key", "value" );
            startActivityForResult( intent, 99 );

而且在ChooseCityActivity的onCreate方法中進行接收:

    protected void onCreate( Bundle savedInstanceState )
    {
        super.onCreate( savedInstanceState );

        String value = getIntent().getStringExtra( "key" );
        Log.v( "ChooseCityActivity", value );
    }

 

回傳

假如這個時候ChooseCityActivity須要把一些數據回傳,能夠這麼作:

        Intent intent = new Intent();
        intent.putExtra( "key2", "value2" );
        setResult( RESULT_OK, intent );
        super.finish();

而且在MainActivity添加onActivityResult方法:

    protected void onActivityResult( int requestCode, int resultCode, Intent data )
    {
        super.onActivityResult( requestCode, resultCode, data );

        Log.v( "onActivityResult", data.getStringExtra( "key2" ) );
    }

 

瞭解了這些內容,Activity間的傳值就沒有問題了,能夠傳不少類型的數據,包括基本類型以及實現了Serializable或者Parcelable接口的類型,Serializable是Java標準的序列化接口,Parcelable是Google定義的效率更高的序列化接口,若是是複雜類型,推薦你們使用Parcelable接口。

 

但願你們掌握這些知識點,這些內容會在實際的應用中頻繁使用。

咱們繼續以前的學習,此次的目標是在新的界面實現選擇城市而且傳回MainActivity,爲了作到這一點,這個界面須要有一個能夠輸入關鍵字的文本框已經一個城市列表,知道要作什麼了,就動起手來吧。

還記得怎麼新建一個Layout嗎?若是忘記了,請看這裏:http://liuzhibang.cn/?p=380

此次咱們新建的Layout能夠起名爲:activity_choose_city,而且咱們此次嘗試使用LinearLayout,

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <EditText
        android:id="@+id/choose_key"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10" >
    </EditText>

    <ListView
        android:id="@+id/choose_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </ListView>

</LinearLayout>

 

這個界面有一個文本框和一個列表,而且是垂直排列,佔滿了整個屏幕。

界面已經好了,接下來咱們須要把數據以某種方式存儲起來,而且在文本框輸入了關鍵字後進行篩選,把篩選的城市顯示到列表中。

 

怎樣把那麼些個城市數據存儲起來?何時進行這個操做呢?

 

Application

爲何咱們的手機應用都叫APP呢?

那是由於咱們所作出來的每個應用都是一個Android Application,而APP就是Application的縮寫,可見Application的重要性了。

實際上,Application是Android框架的系統組件,當應用啓動的時候就會建立一個且只有一個Application對象,用來存儲APP的一些信息,全部說Application是一個單例模式。既然是單例對象,那麼咱們就能夠在這裏保存一些全局的數據,例如咱們的城市數據;或者進行一些必要的初始化操做,好比把咱們的城市數據存儲起來。

要作到這些,只須要繼承Application這個類,實現咱們本身的Application就能夠了。

新建一個類WeatherApplication,讓它繼承Application,而且實現它的onCreate方法:

public class WeatherApplication extends Application
{
    @Override
    public void onCreate()
    {
        super.onCreate();
    }
}

這個onCreate就是實際意義的整個APP的入口,每次啓動都會執行這個方法,咱們在這裏把城市數據存儲到數據庫中。

 

而後修改AndroidManifest.xml,在application節點添加:

android:name="com.demo.weather.WeatherApplication"

這就指定了這個應用的Application就是WeatherApplication,而後修改WeatherApplication,進行咱們的初始化數據的操做。

public class WeatherApplication extends Application
{
    private String citys = "北京市,天津市,上海市,重慶市,邯鄲市,石家莊市,保定市,張家口市,承德市,唐山市,廊坊市,滄州市,衡水市,邢臺市,秦皇島市,朔州市,忻州市,太原市,大同市,陽泉市,晉中市,長治市,晉城市,臨汾市,呂梁市,運城市,瀋陽市,鐵嶺市,大連市,鞍山市,撫順市,本溪市,丹東市,錦州市,營口市,阜新市,遼陽市,朝陽市,盤錦市,葫蘆島市,長春市,吉林市,延邊朝鮮族自治州,四平市,通化市,白城市,遼源市,松原市,白山市,哈爾濱市,齊齊哈爾市,雞西市,牡丹江市,七臺河市,佳木斯市,鶴崗市,雙鴨山市,綏化市,黑河市,大興安嶺地區,伊春市,大慶市,南京市,無錫市,鎮江市,蘇州市,南通市,揚州市,鹽城市,徐州市,淮安市,連雲港市,常州市,泰州市,宿遷市,舟山市,衢州市,杭州市,湖州市,嘉興市,寧波市,紹興市,溫州市,麗水市,金華市,台州市,合肥市,蕪湖市,蚌埠市,淮南市,馬鞍山市,淮北市,銅陵市,安慶市,黃山市,滁州市,阜陽市,宿州市,巢湖市,六安市,亳州市,池州市,宣城市,福州市,廈門市,寧德市,莆田市,泉州市,漳州市,龍巖市,三明市,南平市,鷹潭市,新餘市,南昌市,九江市,上饒市,撫州市,宜春市,吉安市,贛州市,景德鎮市,萍鄉市,菏澤市,濟南市,青島市,淄博市,德州市,煙臺市,濰坊市,濟寧市,泰安市,臨沂市,濱州市,東營市,威海市,棗莊市,日照市,萊蕪市,聊城市,商丘市,鄭州市,安陽市,新鄉市,許昌市,平頂山市,信陽市,南陽市,開封市,洛陽市,濟源市,焦做市,鶴壁市,濮陽市,周口市,漯河市,駐馬店市,三門峽市,武漢市,襄樊市,鄂州市,孝感市,黃岡市,黃石市,咸寧市,荊州市,宜昌市,恩施土家族苗族自治州,神農架林區,十堰市,隨州市,荊門市,仙桃市,天門市,潛江市,岳陽市,長沙市,湘潭市,株洲市,衡陽市,郴州市,常德市,益陽市,婁底市,邵陽市,湘西土家族苗族自治州,張家界市,懷化市,永州市,廣州市,汕尾市,陽江市,揭陽市,茂名市,惠州市,江門市,韶關市,梅州市,汕頭市,深圳市,珠海市,佛山市,肇慶市,湛江市,中山市,河源市,清遠市,雲浮市,潮州市,東莞市,蘭州市,金昌市,白銀市,天水市,嘉峪關市,武威市,張掖市,平涼市,酒泉市,慶陽市,定西市,隴南市,臨夏回族自治州,甘南藏族自治州,成都市,攀枝花市,自貢市,綿陽市,南充市,達州市,遂寧市,廣安市,巴中市,瀘州市,宜賓市,資陽市,內江市,樂山市,眉山市,涼山彝族自治州,雅安市,甘孜藏族自治州,阿壩藏族羌族自治州,德陽市,廣元市,貴陽市,遵義市,安順市,黔南布依族苗族自治州,黔東南苗族侗族自治州,銅仁地區,畢節地區,六盤水市,黔西南布依族苗族自治州,海口市,三亞市,五指山市,瓊海市,儋州市,文昌市,萬寧市,東方市,澄邁縣,定安縣,屯昌縣,臨高縣,白沙黎族自治縣,昌江黎族自治縣,樂東黎族自治縣,陵水黎族自治縣,保亭黎族苗族自治縣,瓊中黎族苗族自治縣,西雙版納傣族自治州,德宏傣族景頗族自治州,昭通市,昆明市,大理白族自治州,紅河哈尼族彝族自治州,曲靖市,保山市,文山壯族苗族自治州,玉溪市,楚雄彝族自治州,普洱市,臨滄市,怒江傈傈族自治州,迪慶藏族自治州,麗江市,海北藏族自治州,西寧市,海東地區,黃南藏族自治州,海南藏族自治州,果洛藏族自治州,玉樹藏族自治州,海西蒙古族藏族自治州,西安市,咸陽市,延安市,榆林市,渭南市,商洛市,安康市,漢中市,寶雞市,銅川市,防城港市,南寧市,崇左市,來賓市,柳州市,桂林市,梧州市,賀州市,貴港市,玉林市,百色市,欽州市,河池市,北海市,拉薩市,日喀則地區,山南地區,林芝地區,昌都地區,那曲地區,阿里地區,銀川市,石嘴山市,吳忠市,固原市,中衛市,塔城地區,哈密地區,和田地區,阿勒泰地區,克孜勒蘇柯爾克孜自治州,博爾塔拉蒙古自治州,克拉瑪依市,烏魯木齊市,石河子市,昌吉回族自治州,五家渠市,吐魯番地區,巴音郭楞蒙古自治州,阿克蘇地區,阿拉爾市,喀什地區,圖木舒克市,伊犁哈薩克自治州,呼倫貝爾市,呼和浩特市,包頭市,烏海市,烏蘭察布市,通遼市,赤峯市,鄂爾多斯市,巴彥淖爾市,錫林郭勒盟,興安盟,阿拉善盟,臺北市,高雄市,基隆市,臺中市,臺南市,新竹市,嘉義市,澳門特別行政區,香港特別行政區";
    private DbUtils dbUtils;

    private static WeatherApplication instance;

    public static WeatherApplication getInstance()
    {
        return instance;
    }

    @Override
    public void onCreate()
    {
        super.onCreate();
        instance = this;
        dbUtils = DbUtils.create( this );

        if( !readInit() )
        {
            initCity();
        }
    }

    private void initCity()
    {
        boolean isSuccess = true;
        String[] cityArray = citys.split( "," );
        for( String city : cityArray )
        {
            CityBean cityBean = new CityBean();
            cityBean.setCityName( city );
            try
            {
                dbUtils.save( cityBean );
            }
            catch( DbException e )
            {
                isSuccess = false;
            }
        }

        saveInit( isSuccess );
    }

    private void saveInit( boolean isInited )
    {
        SharedPreferences sharedPreferences = getSharedPreferences( "weather", Context.MODE_PRIVATE );
        Editor editor = sharedPreferences.edit();
        editor.putBoolean( "isInited", isInited );
        editor.commit();
    }

    private boolean readInit()
    {
        SharedPreferences sharedPreferences = getSharedPreferences( "weather", Context.MODE_PRIVATE );
        return sharedPreferences.getBoolean( "isInited", false );
    }
}

 

這裏用到了CityBean這個類,

public class CityBean
{
    private int id;
    private String cityName;

    public int getId()
    {
        return id;
    }

    public void setId( int id )
    {
        this.id = id;
    }

    public String getCityName()
    {
        return cityName;
    }

    public void setCityName( String cityName )
    {
        this.cityName = cityName;
    }
}

 

這個類是進行數據庫操做的類,而咱們的數據庫操做使用了第三方類庫,根據類庫的要求,咱們這裏有一個名爲id的字段。

 

作好了這些後,運行程序。

有沒有發現特別慢?那是由於咱們在啓動的時候進行了數據庫操做,而且插入了300多條城市數據,這個過程是比較耗時的。

等一切現實正常後,咱們退出程序,再從新啓動,有沒有發現變快了?那是由於咱們只在第一次啓動的時候進行了數據存儲。

這裏面用到了一個小技巧:

在數據存儲操做後,咱們另外在Preference中寫入了一個名爲isInited的Boolean類型的數據,若是成功,咱們就再也不進行這個操做了。

 

查看數據庫文件

若是你的手機是Root過的,那麼你還能夠把數據庫導出,放到電腦上用工具查看數據是否正確。另外,有一些手機APP,例如Root Explorer也提供了在手機上直接查看數據庫的功能,可是畢竟不如在電腦上清晰直觀。

每個APP的數據庫、緩存文件都存在/data/data/[package]下面,好比咱們的APP,就存在/data/data/com.demo.weather這個目錄下面。

須要注意,這個目錄在APP被卸載以後會被清空。

device-2015-01-25-143934

 

稍微說明一下這幾個目錄,

cache:在這裏能夠存放臨時緩存,

databases:就是數據庫,咱們的數據庫文件就在這個目錄

files:通常存放一些長時間保存的數據

lib:APP用到庫文件

shared_prefs:這個就是SharedPreferences這個類保存的數據

 

跟咱們這個APP相關的就只要兩個目錄:databases和shared_prefs,打開它們:

device-2015-01-25-145619 device-2015-01-25-145633

很清楚,一個數據庫文件,另一個是xml文件。你們能夠經過各類辦法把這兩個文件導出到電腦來查看。

 

接下來的工做就是在ChooseCityActivity中選擇城市了。

首先把這個Activity和咱們以前新建的Layout聯繫起來,

public class ChooseCityActivity extends Activity
{
    @ViewInject( R.id.choose_key )
    private EditText edtKey;

    @ViewInject( R.id.choose_list )
    private ListView lstCity;

    @Override
    protected void onCreate( Bundle savedInstanceState )
    {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.activity_choose_city );

        ViewUtils.inject( this );
    }
}

 

 

再回想一下咱們的需求:輸入關鍵字後,在列表中顯示相應的城市

這須要咱們監聽文本框的輸入事件,能夠這麼作:

public class ChooseCityActivity extends Activity
{
    @ViewInject( R.id.choose_key )
    private EditText edtKey;

    @ViewInject( R.id.choose_list )
    private ListView lstCity;

    @Override
    protected void onCreate( Bundle savedInstanceState )
    {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.activity_choose_city );

        ViewUtils.inject( this );

        edtKey.addTextChangedListener( new TextFilter() );
    }

    private void search( String key )
    {
    }

    class TextFilter implements TextWatcher
    {
        @Override
        public void beforeTextChanged( CharSequence s, int start, int count, int after )
        {
        }

        @Override
        public void onTextChanged( CharSequence s, int start, int before, int count )
        {
            String key = edtKey.getText().toString();
            search( key );
        }

        @Override
        public void afterTextChanged( Editable s )
        {
        }
    }
}

 

給EditText添加一個TextWatcher事件,就能夠監聽它的文本變化,當文本變化後,會調用search方法,這個方法將會查詢數據庫,而且把查詢結果顯示到ListView中。

 

將會用到數據庫查詢操做,聯想到以前咱們已經進行過存儲操做了,咱們能夠把在Application裏用到的DbUtils公開出來,在WeatherApplication添加方法:

    public DbUtils getDbUtil()
    {
        if( dbUtils == null )
        {
            dbUtils = DbUtils.create( this );
        }

        return dbUtils;
    }

 

 

關於如何把數據顯示到ListView中,相信你們還記得以前如何把天氣顯示出來的吧。

新建一個Layout,名爲item_city:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/item_city"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_margin="15dp"
        android:textColor="#000000"
        android:textSize="16sp" />

</RelativeLayout>

 

 

而後新建一個Adapter,名爲CityAdapter:

public class CityAdapter extends BaseAdapter
{
    private List<CityBean> citys;
    private LayoutInflater inflater;

    public CityAdapter( Context context, List<CityBean> citys )
    {
        this.citys = citys;
        inflater = LayoutInflater.from( context );
    }

    @Override
    public int getCount()
    {
        return citys.size();
    }

    @Override
    public Object getItem( int position )
    {
        return citys.get( position );
    }

    @Override
    public long getItemId( int position )
    {
        return position;
    }

    @Override
    public View getView( int position, View convertView, ViewGroup parent )
    {
        convertView = inflater.inflate( R.layout.item_city, null );

        CityBean bean = (CityBean)getItem( position );

        TextView txtCity = (TextView)convertView.findViewById( R.id.item_city );
        txtCity.setText( bean.getCityName() );

        return convertView;
    }
}

 

 

最後修改ChooseCityActivity的search方法:

    private void search( String key )
    {
        DbUtils dbUtils = WeatherApplication.getInstance().getDbUtil();
        try
        {
            List<CityBean> citys = dbUtils.findAll( Selector.from( CityBean.class ).where( "cityName", "like", "%" + key + "%" ) );
            CityAdapter adapter = new CityAdapter( getApplicationContext(), citys );
            lstCity.setAdapter( adapter );
        }
        catch( DbException e )
        {
            e.printStackTrace();
        }
    }

 

這個時候的ChooseCityActivity是這樣的:

public class ChooseCityActivity extends Activity
{
    @ViewInject( R.id.choose_key )
    private EditText edtKey;

    @ViewInject( R.id.choose_list )
    private ListView lstCity;

    @Override
    protected void onCreate( Bundle savedInstanceState )
    {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.activity_choose_city );

        ViewUtils.inject( this );

        edtKey.addTextChangedListener( new TextFilter() );
    }

    private void search( String key )
    {
        DbUtils dbUtils = WeatherApplication.getInstance().getDbUtil();
        try
        {
            List<CityBean> citys = dbUtils.findAll( Selector.from( CityBean.class ).where( "cityName", "like", "%" + key + "%" ) );
            CityAdapter adapter = new CityAdapter( getApplicationContext(), citys );
            lstCity.setAdapter( adapter );
        }
        catch( DbException e )
        {
            e.printStackTrace();
        }
    }

    class TextFilter implements TextWatcher
    {
        @Override
        public void beforeTextChanged( CharSequence s, int start, int count, int after )
        {
        }

        @Override
        public void onTextChanged( CharSequence s, int start, int before, int count )
        {
            String key = edtKey.getText().toString();
            search( key );
        }

        @Override
        public void afterTextChanged( Editable s )
        {
        }
    }
}

 

來運行一下吧,點擊「添加城市」,在輸入框中輸入文字後,列表是否是有了內容了?

個人是這樣的:

device-2015-01-25-145816

 

到這裏,咱們的工做就完成了一多半了,剩下的就是把選擇的城市回傳給MainActivity,而且更新天氣數據。

 

再接再礪,咱們把最後這部分完成再休息。

 

關於如何把選擇的城市回傳,以前已經瞭解了,此次實踐一下:

    @OnItemClick( R.id.choose_list )
    public void onItemClick( AdapterView<?> parent, View view, int position, long id )
    {
        CityBean cityBean = (CityBean)parent.getAdapter().getItem( position );

        Intent intent = new Intent();
        intent.putExtra( "selectedCity", cityBean.getCityName() );
        setResult( RESULT_OK, intent );
        super.finish();
    }

 

這個方法是ListView中的Item點擊後觸發,做用就是把選中的城市從Adapter中取出來,而且回傳。

 

而後在MainActivity中修改onActivityResult方法:

    protected void onActivityResult( int requestCode, int resultCode, Intent data )
    {
        super.onActivityResult( requestCode, resultCode, data );

        if( resultCode == RESULT_OK )
        {
            String selectedCity = data.getStringExtra( "selectedCity" );
            getWeather( selectedCity );
        }
    }

在這裏先判斷了一下resultCode時候是正確返回,而後取出來城市而且調用了getWeather方法獲取天氣。

 

打完收工,運行一下吧。

 

有沒有發現這個狀況:

選擇了別的城市後,再返回回來,天氣好像是發生了變化,可是標題上仍是所在地的名字,爲何會有這個狀況呢?咱們明明在getWeather方法中設置了標題啊。

 

別急,讓咱們回想一下Activity的生命週期中的第3條:

>> 3. Activity從新得到焦點的時候,會依次執行onRestart、onStart和onResume

當返回到MainActivity的時候,這個順序有發生了一點變化,它會這樣執行:

onActivityResult -> onRestart -> onStart -> onResume

看到了吧,onActivityResult是最早執行,這個時候咱們調用getWeather會被onStart調用的getWeather給覆蓋掉,怎麼解決呢?

我這裏有兩種辦法:

1. 咱們不在onStart中獲取天氣,而在onCreate中獲取

2. 修改一下getWeather,把城市做爲類的成員變量,而不是方法參數,這樣保證城市的一致

咱們先採用第二種辦法吧。

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;

    private String city;

    @Override
    protected void onCreate( Bundle savedInstanceState )
    {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.activity_main );

        Log.v( "WeatherAPP", "onCreate" );

        ViewUtils.inject( this );

        datas = new ArrayList<WeatherDataBean>();
        adapter = new WeatherAdapter( getApplicationContext(), datas );
        lstWeather.setAdapter( adapter );

        initLocationClient();
        mLocationClient.start();
    }

    @Override
    public boolean onCreateOptionsMenu( Menu menu )
    {
        super.onCreateOptionsMenu( menu );
        menu.add( Menu.NONE, Menu.FIRST + 1, 0, "添加城市" ).setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS );
        return true;
    }

    @Override
    public boolean onOptionsItemSelected( MenuItem item )
    {
        if( item.getItemId() == Menu.FIRST + 1 )
        {
            Intent intent = new Intent( getApplicationContext(), ChooseCityActivity.class );
            intent.putExtra( "key", "value" );
            startActivityForResult( intent, 99 );
        }

        return super.onOptionsItemSelected( item );
    }

    @Override
    protected void onActivityResult( int requestCode, int resultCode, Intent data )
    {
        super.onActivityResult( requestCode, resultCode, data );

        if( resultCode == RESULT_OK )
        {
            city = data.getStringExtra( "selectedCity" );
        }
    }

    private void getWeather()
    {
        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()
    {
        Log.v( "WeatherAPP", "onStop" );
        super.onStop();
        mLocationClient.stop();
    }

    @Override
    protected void onPause()
    {
        Log.v( "WeatherAPP", "onPause" );
        super.onPause();
    }

    @Override
    protected void onRestart()
    {
        Log.v( "WeatherAPP", "onRestart" );
        super.onRestart();
    }

    @Override
    protected void onResume()
    {
        Log.v( "WeatherAPP", "onResume" );
        super.onResume();
    }

    @Override
    protected void onStart()
    {
        Log.v( "WeatherAPP", "onStart" );
        super.onStart();

        if( city == null || city.length() == 0 )
        {
            city = readCity();
        }

        if( city != null && city.length() > 0 )
        {
            getWeather();
        }
    }

    @Override
    protected void onDestroy()
    {
        Log.v( "WeatherAPP", "onDestroy" );
        super.onDestroy();
    }

    public class MyLocationListener implements BDLocationListener
    {
        @Override
        public void onReceiveLocation( BDLocation location )
        {
            city = location.getCity();

            String localCity = readCity();
            if( !localCity.equals( city ) )
            {
                saveCity( city );
                getWeather();
            }
        }
    }

    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", "" );
    }
}

 

這裏修改了onStart方法:

    protected void onStart()
    {
        Log.v( "WeatherAPP", "onStart" );
        super.onStart();

        if( city == null || city.length() == 0 )
        {
            city = readCity();
        }

        if( city != null && city.length() > 0 )
        {
            getWeather();
        }
    }

若是城市爲空,才獲取咱們在Preference中保存的城市,這是由於onActivityResult執行在前,已經把city變量賦值了,而後在 執行onStart的時候確保這個值不被覆蓋。

 

大功告成了。お疲れ様です。(辛苦了)

 

就這樣,天氣就變成了咱們選擇的城市的了。

 

等等,說好的「添加城市」呢?

我想同事查看兩個城市,而不是一個一個看。

別慌,怎麼同時查看兩個城市,咱們明天繼續。

 

附件是本次的工程文件,點擊下載 http://pan.baidu.com/s/1bntoAGR 。

此係列文章系本人原創,如需轉載,請註明出處 www.liuzhibang.cn

相關文章
相關標籤/搜索