10天学安卓-第八天

昨天郑州雨夹雪,还有冰雹,结果小区就断电了,真是悲剧。第八天的学习就挪到今天了。

经过前几天的学习,我们了解了一些Android的基础知识,并且做出了一个也算实用的天气预报APP,对Android也算得上是入门了,那么今天我们继续改进我们的APP。

这个APP现在只能查看所在城市的天气,那么万一妹子不跟我们一个城市,我们就不能关注到妹子所在城市的天气了,那还怎么嘘寒问暖呢,这个问题是一定要解决的。

?

如何解决?

那就是需要在一个界面上可以选择城市了,这就用到了数据库了。

我整理了一份所有城市的名单:

北京市,天津市,上海市,重庆市,邯郸市,石家庄市,保定市,张家口市,承德市,唐山市,廊坊市,沧州市,衡水市,邢台市,秦皇岛市,朔州市,忻州市,太原市,大同市,阳泉市,晋中市,长治市,晋城市,临汾市,吕梁市,运城市,沈阳市,铁岭市,大连市,鞍山市,抚顺市,本溪市,丹东市,锦州市,营口市,阜新市,辽阳市,朝阳市,盘锦市,葫芦岛市,长春市,吉林市,延边朝鲜族自治州,四平市,通化市,白城市,辽源市,*市,白山市,哈尔滨市,齐齐哈尔市,鸡西市,牡丹江市,七台河市,佳木斯市,鹤岗市,双鸭山市,绥化市,黑河市,大兴安岭地区,伊春市,大庆市,南京市,无锡市,镇江市,苏州市,南通市,扬州市,盐城市,徐州市,淮安市,连云港市,常州市,泰州市,宿迁市,舟山市,衢州市,杭州市,湖州市,嘉兴市,宁波市,绍兴市,温州市,丽水市,金华市,台州市,合肥市,芜湖市,蚌埠市,淮南市,马鞍山市,淮北市,铜陵市,安庆市,黄山市,滁州市,阜阳市,宿州市,巢湖市,六安市,亳州市,池州市,宣城市,福州市,厦门市,宁德市,莆田市,泉州市,漳州市,龙岩市,三明市,南平市,鹰潭市,新余市,南昌市,九江市,上饶市,抚州市,宜春市,吉安市,赣州市,景德镇市,萍乡市,菏泽市,济南市,青岛市,淄博市,德州市,烟台市,潍坊市,济宁市,泰安市,临沂市,滨州市,东营市,威海市,枣庄市,日照市,莱芜市,聊城市,商丘市,郑州市,安阳市,新乡市,许昌市,平顶山市,信阳市,南阳市,开封市,洛阳市,济源市,焦作市,鹤壁市,濮阳市,周口市,漯河市,驻马店市,三门峡市,武汉市,襄樊市,鄂州市,孝感市,黄冈市,黄石市,咸宁市,荆州市,宜昌市,恩施土家族苗族自治州,神农架林区,十堰市,随州市,荆门市,仙桃市,天门市,潜江市,岳阳市,长沙市,湘潭市,株洲市,衡阳市,郴州市,常德市,益阳市,娄底市,邵阳市,湘西土家族苗族自治州,张家界市,怀化市,永州市,广州市,汕尾市,阳江市,揭阳市,茂名市,惠州市,江门市,韶关市,梅州市,汕头市,深圳市,珠海市,佛山市,肇庆市,湛江市,中山市,河源市,清远市,云浮市,潮州市,东莞市,兰州市,金昌市,白银市,天水市,嘉峪关市,武威市,张掖市,平凉市,酒泉市,庆阳市,定西市,陇南市,临夏回族自治州,甘南藏族自治州,成都市,攀枝花市,自贡市,绵阳市,南充市,达州市,遂宁市,广安市,巴中市,泸州市,宜宾市,资阳市,内江市,乐山市,眉山市,凉山彝族自治州,雅安市,甘孜藏族自治州,阿坝藏族羌族自治州,德阳市,广元市,贵阳市,遵义市,安顺市,黔南布依族苗族自治州,黔东南苗族侗族自治州,铜仁地区,毕节地区,六盘水市,黔西南布依族苗族自治州,海口市,三亚市,五指山市,琼海市,儋州市,文昌市,万宁市,东方市,澄迈县,定安县,屯昌县,临高县,白沙黎族自治县,昌江黎族自治县,乐东黎族自治县,陵水黎族自治县,保亭黎族苗族自治县,琼中黎族苗族自治县,西双版纳傣族自治州,德宏傣族景颇族自治州,昭通市,昆明市,大理白族自治州,红河哈尼族彝族自治州,曲靖市,保山市,文山壮族苗族自治州,玉溪市,楚雄彝族自治州,普洱市,临沧市,怒江傈傈族自治州,迪庆藏族自治州,丽江市,海北藏族自治州,西宁市,海东地区,黄南藏族自治州,海南藏族自治州,果洛藏族自治州,玉树藏族自治州,海西蒙古族藏族自治州,西安市,咸阳市,延安市,榆林市,渭南市,商洛市,安康市,汉中市,宝鸡市,铜川市,防城港市,南宁市,崇左市,来宾市,柳州市,桂林市,梧州市,贺州市,贵港市,玉林市,百色市,钦州市,河池市,北海市,拉萨市,日喀则地区,山南地区,林芝地区,昌都地区,那曲地区,阿里地区,银川市,石嘴山市,吴忠市,固原市,中卫市,塔城地区,哈密地区,和田地区,阿勒泰地区,克孜勒苏柯尔克孜自治州,博尔塔拉蒙古自治州,克拉玛依市,乌鲁木齐市,石河子市,昌吉回族自治州,五家渠市,吐鲁番地区,巴音郭楞蒙古自治州,阿克苏地区,阿拉尔市,喀什地区,图木舒克市,伊犁哈萨克自治州,呼伦贝尔市,呼和浩特市,包头市,乌海市,乌兰察布市,通辽市,赤峰市,鄂尔多斯市,巴彦淖尔市,锡林郭勒盟,兴安盟,阿拉善盟,台北市,*市,基隆市,台中市,台南市,新竹市,嘉义市,澳门特别行政区,香港特别行政区

?

这里基本囊括了国内大部分城市,我们做一个界面从这里选择就好了。

开工。

?

新建一个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 );
}</pre>

?

第一个方法的作用是在右上角添加了一个名为“添加城市”的按钮,第二个方法的作用就是点击了“添加城市”的按钮后,会跳转到我们新建的那个Activity。

?

ActionBar

我们这里用到了ActionBar,一个完整的ActionBar是这样子的:

10天学安卓-第八天

一共分为四部分,

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的基础知识后,我们继续之前的学习。

现在的界面应该是这个样子的:

10天学安卓-第八天

?

点击“添加城市”后,就跳转到了一个空白页面。

?

跳转+传值

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 );
}</pre>

?

回传

假如这个时候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" ) );
}</pre>

?

了解了这些内容,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” >

&lt;EditText
    android:id="@+id/choose_key"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:ems="10" &gt;
&lt;/EditText&gt;

&lt;ListView
    android:id="@+id/choose_list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" &gt;
&lt;/ListView&gt;

</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_PRI 大专栏  10天学安卓-第八天VATE );
    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被卸载之后会被清空。

10天学安卓-第八天

?

稍微说明一下这几个目录,

cache:在这里可以存放临时缓存,

databases:就是数据库,我们的数据库文件就在这个目录

files:一般存放一些长时间保存的数据

lib:APP用到库文件

shared_prefs:这个就是SharedPreferences这个类保存的数据

?

跟我们这个APP相关的就只要两个目录:databases和shared_prefs,打开它们:

10天学安卓-第八天 10天学安卓-第八天

很清楚,一个数据库文件,另外一个是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;
}</pre>

?

?

关于如何把数据显示到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” >

&lt;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" /&gt;

</RelativeLayout>
?

?

然后新建一个Adapter,名为CityAdapter:

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

public CityAdapter( Context context, List&lt;CityBean&gt; 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&lt;CityBean&gt; 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 )
    {
    }
}

}
?

来运行一下吧,点击“添加城市”,在输入框中输入文字后,列表是不是有了内容了?

我的是这样的:

10天学安卓-第八天

?

到这里,我们的工作就完成了一多半了,剩下的就是把选择的城市回传给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();
}</pre>

?

这个方法是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 );
    }
}</pre>

在这里先判断了一下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&lt;WeatherDataBean&gt; 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&lt;WeatherDataBean&gt;();
    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&lt;String&gt;()
    {
        @Override
        public void onSuccess( ResponseInfo&lt;String&gt; 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 &amp;&amp; city.length() &gt; 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 &amp;&amp; city.length() &gt; 0 )
    {
        getWeather();
    }
}</pre>

如果城市为空,才获取我们在Preference中保存的城市,这是因为onActivityResult执行在前,已经把city变量赋值了,然后在 执行onStart的时候确保这个值不被覆盖。

?

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

?

就这样,天气就变成了我们选择的城市的了。

?

等等,说好的“添加城市”呢?

我想同事查看两个城市,而不是一个一个看。

别慌,怎么同时查看两个城市,我们明天继续。

?

附件是本次的工程文件,点击下载 http://pan.baidu.com/s/1bntoAGR

此系列文章系本人原创,如需转载,请注明出处 www.liuzhibang.cn

10天学安卓-第八天

上一篇:微信端浏览器title值的修改


下一篇:微信公众号调试