zoukankan      html  css  js  c++  java
  • 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是这样子的:

    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 );
    }</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被卸载之后会被清空。

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

    }
     

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

    我的是这样的:

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

  • 相关阅读:
    easyui combo自动高度(下拉框空白问题)
    log4net
    asp.net mvc 生成条形码
    Kubernetes 初探
    美国最顶级的投资机构在关注什么
    Ansible--01
    第一章 进入java的世界
    正则表达式
    zabbix接口调用注意事项--Python
    Docker+K8S实践
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12366136.html
Copyright © 2011-2022 走看看