昨天鄭州雨夾雪,還有冰雹,結果小區就斷電了,真是悲劇。第八天的學習就挪到今天了。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是這樣子的:
一共分爲四部分,
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的基礎知識後,咱們繼續以前的學習。
如今的界面應該是這個樣子的:
點擊「添加城市」後,就跳轉到了一個空白頁面。
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被卸載以後會被清空。
稍微說明一下這幾個目錄,
cache:在這裏能夠存放臨時緩存,
databases:就是數據庫,咱們的數據庫文件就在這個目錄
files:通常存放一些長時間保存的數據
lib:APP用到庫文件
shared_prefs:這個就是SharedPreferences這個類保存的數據
跟咱們這個APP相關的就只要兩個目錄:databases和shared_prefs,打開它們:
很清楚,一個數據庫文件,另一個是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 ) { } } }
來運行一下吧,點擊「添加城市」,在輸入框中輸入文字後,列表是否是有了內容了?
個人是這樣的:
到這裏,咱們的工做就完成了一多半了,剩下的就是把選擇的城市回傳給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