博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android学习--14-天气练习
阅读量:7081 次
发布时间:2019-06-28

本文共 21300 字,大约阅读时间需要 71 分钟。

hot3.png

听说郭大的此系列的第二本快要出版了。

首先我们要做一个app需要实现哪些功能?

  1. 省、市、县的显示
  2. 以上是可选的
  3. 查看某地区的天气
  4. 提供手动更新天气和自动更新

由于书中提供的天气地址已经过时,基本不能用。天气更新基本就失效了。 所以我找了一个 和风天气api 注册下有每天有3000次免费试用

省 市 县数据,整理了sql

包结构

  • activity 活动
  • db 操作数据的
  • model 数据模型
  • util 工具类
  • service 服务
  • receiver 接收

初始化数据

  1. Province City County 三个sql脚本放入 assets 文件夹下
  2. 创建表结构
  3. 插入表数据

怎么玩?

  1. extends SQLiteOpenHelper
  2. onCreate() 方法中执行一系列操作
public class DbHelper extends SQLiteOpenHelper{    private static final String TAG = DbHelper.class.getSimpleName();    public static final String DB_NAME = "weather.db";    private Context mContext;    public DbHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {        super(context, name, factory, version);        mContext = context;    }    /**     * Province表建表语句     */    public static final String CREATE_PROVINCE = "create table Province ("            + "id integer primary key autoincrement, "            + "province_name text, "            + "province_code text)";    /**     * City表建表语句     */    public static final String CREATE_CITY = "create table City ("            + "id integer primary key autoincrement, "            + "city_name text, "            + "city_code text, "            + "province_code text)";    /**     * County表建表语句     */    public static final String CREATE_COUNTY = "create table County ("            + "id integer primary key autoincrement, "            + "county_name text, "            + "county_code text, "            + "city_code text)";    @Override    public void onCreate(SQLiteDatabase db) {        // 创建表        db.execSQL(CREATE_PROVINCE); // 创建Province表        db.execSQL(CREATE_CITY); // 创建City表        db.execSQL(CREATE_COUNTY); // 创建County表        // 初始化数据        executeAssetsSQL(db, "Province.sql");        executeAssetsSQL(db, "City.sql");        executeAssetsSQL(db, "County.sql");        Log.d(TAG, "init city data success!!!");    }    /**     * 读取数据库文件(.sql),并执行sql语句     * */    private void executeAssetsSQL(SQLiteDatabase db, String sqlFileName) {        BufferedReader in = null;        try {            in = new BufferedReader(new InputStreamReader(mContext.getAssets().open(sqlFileName)));            String line;            while ((line = in.readLine()) != null) {                db.execSQL(line);            }        } catch (IOException e) {            Log.e(TAG, e.toString());        } finally {            try {                if (in != null)                    in.close();            } catch (IOException e) {                Log.e(TAG, e.toString());            }        }    }    @Override    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {            }}

读取sqlite数据

实体

set... get... 就省了

// 省public class Province {    private int id;    private String provinceName;    private String provinceCode;}// 市public class City {    private int id;    private String cityName;    private String cityCode;    private String provinceCode; // 对应省}// 县public class County {    private int id;    private String countyName;    private String countyCode;    private String cityCode; //对应市}

查询操作

封装到 WeatherDb

public class WeatherDb {    public static final int VERSION = 1;    private SQLiteDatabase db;    private static WeatherDb weatherDb;    // 单例    private WeatherDb(Context context) {        DbHelper dbHelper = new DbHelper(context, DbHelper.DB_NAME, null, VERSION);        db = dbHelper.getWritableDatabase();    }    /**     * 获取 实例     * @param context     * @return     */    public synchronized static WeatherDb getInstance(Context context) {        if (weatherDb == null) {            weatherDb = new WeatherDb(context);        }        return weatherDb;    }    /**     * 从数据库读取全国所有的省份信息。     */    public List
loadProvinces() { List
list = new ArrayList
(); Cursor cursor = db.query("Province", null, null, null, null, null, "province_code asc"); if (cursor.moveToFirst()) { do { Province province = new Province(); province.setId(cursor.getInt(cursor.getColumnIndex("id"))); province.setProvinceName(cursor.getString(cursor .getColumnIndex("province_name"))); province.setProvinceCode(cursor.getString(cursor.getColumnIndex("province_code"))); list.add(province); } while (cursor.moveToNext()); } cursor.close(); return list; } /** * 从数据库读取某省下所有的城市信息。 */ public List
loadCities(String provinceCode) { List
list = new ArrayList
(); Cursor cursor = db.query("City", null, "province_code = ?", new String[] { provinceCode }, null, null, null); if (cursor.moveToFirst()) { do { City city = new City(); city.setId(cursor.getInt(cursor.getColumnIndex("id"))); city.setCityName(cursor.getString(cursor .getColumnIndex("city_name"))); city.setCityCode(cursor.getString(cursor .getColumnIndex("city_code"))); city.setProvinceCode(provinceCode); list.add(city); } while (cursor.moveToNext()); } cursor.close(); return list; } /** * 从数据库读取某城市下所有的县信息。 */ public List
loadCounties(String cityCode) { List
list = new ArrayList
(); Cursor cursor = db.query("County", null, "city_code = ?", new String[] { cityCode }, null, null, null); if (cursor.moveToFirst()) { do { County county = new County(); county.setId(cursor.getInt(cursor.getColumnIndex("id"))); county.setCountyName(cursor.getString(cursor .getColumnIndex("county_name"))); county.setCountyCode(cursor.getString(cursor .getColumnIndex("county_code"))); county.setCityCode(cityCode); list.add(county); } while (cursor.moveToNext()); } cursor.close(); return list; }}

HttpUtil

用 URL 也好 , HttpClient 也行。

选择地区

布局

choose_area.xml

  1. 显示标题 选择的地方 TextView
  2. 显示列表 ListView

地区活动

为了识别 在哪一层级,是 省呢,还是市呢,还是县呢? 定义三个常量:

public static final int LEVEL_PROVINCE = 0;public static final int LEVEL_CITY = 1;public static final int LEVEL_COUNTY = 2;

然后得知道当前到底是哪个被选择了呢?

/**     * 当前选中的级别     */ private int currentLevel; /**     * 当前列表数据     */private List
dataList = new ArrayList
();

主要代码 ChooseAreaActivity

... 省略变量 @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        requestWindowFeature(Window.FEATURE_NO_TITLE);        setContentView(R.layout.choose_area);        titleView = (TextView) findViewById(R.id.title_text);        listView = (ListView) findViewById(R.id.list_view);        adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, dataList);        listView.setAdapter(adapter);        // 获取实例 ,初始化数据        weatherDb = WeatherDb.getInstance(this);                // 加载省级数据        queryProvinces();    }    /**     * 查询全部省,同时设置当前级别和列表数据     */    private void queryProvinces() {        provinceList = weatherDb.loadProvinces();        if (provinceList != null && provinceList.size() > 0 ) {            dataList.clear();            for (Province province : provinceList) {                dataList.add(province.getProvinceName());            }            adapter.notifyDataSetChanged();            listView.setSelection(0);            titleView.setText("中国");            currentLevel = LEVEL_PROVINCE;        }    }

这样省的列表就展示出来了

点击切换省市县数据

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {            @Override            public void onItemClick(AdapterView
parent, View view, int position, long id) { // 省 if (currentLevel == LEVEL_PROVINCE) { selectedProvince = provinceList.get(position); queryCities(); // 市 }else if (currentLevel == LEVEL_CITY){ selectedCity = cityList.get(position); queryCounties(); // 县 }else if (currentLevel == LEVEL_COUNTY) { String countyCode = countyList.get(position).getCountyCode(); // ...具体待下一步操作 } } });// 根据省code加载市数据,同时设置当前级别和列表数据private void queryCities() { cityList = weatherDb.loadCities(selectedProvince.getProvinceCode()); if (cityList != null && cityList.size() > 0 ) { dataList.clear(); for (City city : cityList) { dataList.add(city.getCityName()); } adapter.notifyDataSetChanged(); listView.setSelection(0); titleView.setText(selectedProvince.getProvinceName()); currentLevel = LEVEL_CITY; } }// 根据市code加载县数据,同时设置当前级别和列表数据 private void queryCounties() { countyList = weatherDb.loadCounties(selectedCity.getCityCode()); if (countyList != null && countyList.size() > 0) { dataList.clear(); for (County county : countyList) { dataList.add(county.getCountyName()); } adapter.notifyDataSetChanged(); listView.setSelection(0); titleView.setText(selectedCity.getCityName()); currentLevel = LEVEL_COUNTY; } }

别忘了配置AndroidManifest

写到这里基本 切换省市县的效果就出来了

到县就可以根据县的code去请求api 查询相关天气的数据,然后展示出来。

显示天气信息

封装http请求的天气数据

public class WeatherInfoUtil {    /**     * 解析服务器返回的JSON数据,并将解析出的数据存储到本地。     */    public static void handleWeatherResponse(Context context, String response) {        try {            JSONObject jsonObject = new JSONObject(response);            JSONArray jsonArray = jsonObject.getJSONArray("HeWeather data service 3.0");            JSONObject weatherInfo = jsonArray.getJSONObject(0);            String status = weatherInfo.getString("status");            if ("ok".equals(status)) {                // 基础信息                JSONObject basic = weatherInfo.getJSONObject("basic");                // 现在天气                JSONObject now = weatherInfo.getJSONObject("now");                // 天气预报, 1 -7 天                JSONArray daily_forecast = weatherInfo.getJSONArray("daily_forecast");                // 小时预报 3小时                JSONArray hourly_forecast = weatherInfo.getJSONArray("hourly_forecast");                // 空气质量 有可能没有                JSONObject aqi = weatherInfo.optJSONObject("aqi");                // 提醒 有可能没有                JSONObject suggestion = weatherInfo.optJSONObject("suggestion");                // =================基础信息=================                // 城市                String cityName = basic.getString("city");                // ID                String weatherCode = basic.getString("id");                JSONObject update = basic.getJSONObject("update");                // 更新时间                String publishTime = update.getString("loc");                // =================现在天气===============                // 当前温度                String temp = now.getString("tmp");                // 当前天气描述                JSONObject cond = now.getJSONObject("cond");                String weatherDesp = cond.getString("txt");                JSONObject wind = now.getJSONObject("wind");                // 风向                String dir = wind.getString("dir");                // 风力                String sc = wind.getString("sc");                // =================天气预报===============                for (int i = 0; i < daily_forecast.length(); i++) {                    JSONObject daily = daily_forecast.getJSONObject(i);                    String date = daily.getString("date");                    JSONObject condDaily = daily.getJSONObject("cond");                    String txt_d = condDaily.getString("txt_d");                    String txt_n = condDaily.getString("txt_n");                    JSONObject tmpDaily = daily.getJSONObject("tmp");                    String minTemp = tmpDaily.getString("min");                    String maxTemp = tmpDaily.getString("max");                    JSONObject windDaily = daily.getJSONObject("wind");                    String dirDaily = windDaily.getString("dir");                    String scDaily = windDaily.getString("sc");                }                saveWeatherInfo(context, cityName, weatherCode, temp, weatherDesp, publishTime, dir, sc);            }            if ("unknown city".equals(status)) {                saveWeatherInfo(context, null, null, null, "未知城市", null, null, null);            }            if ("no more requests".equals(status)) {                saveWeatherInfo(context, null, null, null, "超过访问次数", null , null, null);            }        } catch (JSONException e) {            e.printStackTrace();        }    }    /**     * 将服务器返回的所有天气信息存储到SharedPreferences文件中。     */    private static void saveWeatherInfo(Context context, String cityName, String weatherCode, String temp, String weatherDesp, String publishTime,    String dir, String sc) {        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年M月d日", Locale.CHINA);        SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(context).edit();        editor.putBoolean("city_selected", true);        editor.putString("city_name", cityName);        editor.putString("weather_code", weatherCode);        editor.putString("temp", temp + "℃");        editor.putString("weather_desp", weatherDesp);        editor.putString("publish_time", publishTime);        editor.putString("current_date", sdf.format(new Date()));        editor.putString("wind_dir", dir);        editor.putString("wind_sc", sc + "级");        editor.commit();    }}

以上就是解析json 数据,然后存到SharedPreferences 中,key-value 形式。

天气布局

来想想我们要什么?

  1. 标题
    1. 县 居中
    2. 返回重新选择
    3. 刷新数据按钮
  2. 数据时间
  3. 天气信息

天气活动 WeatherActivity

首先 从 选择地区活动 到 天气活动 并把 code传递过来

ChooseAreaActivity

// }else if (currentLevel == LEVEL_COUNTY) {                    String countyCode = countyList.get(position).getCountyCode();                    Intent intent = new Intent(ChooseAreaActivity.this, WeatherActivity.class);                    intent.putExtra("county_code", countyCode);                    startActivity(intent);                    finish();                }

但是如果之前已经选中,那就不要再加载了,直接跳到天气。 这个可能一开始不太理解,可以后面再加。

isFromWeatherActivity = getIntent().getBooleanExtra("from_weather_activity", false);// 是否选中天气 , 选择就不用再加载了        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);        if (preferences.getBoolean("city_selected", false) && !isFromWeatherActivity) {            Intent intent = new Intent(this, WeatherActivity.class);            startActivity(intent);            finish();            return;        }

WeatherActivity

@Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        requestWindowFeature(Window.FEATURE_NO_TITLE);        setContentView(R.layout.weather_layout);        // 初始化各控件        weatherInfoLayout = (LinearLayout) findViewById(R.id.weather_info_layout);        cityNameText = (TextView) findViewById(R.id.city_name);        publishText = (TextView) findViewById(R.id.publish_text);        weatherDespText = (TextView) findViewById(R.id.weather_desp);        tempText = (TextView) findViewById(R.id.temp);        currentDateText = (TextView) findViewById(R.id.current_date);        switchCity = (Button) findViewById(R.id.switch_city);        refreshWeather = (Button) findViewById(R.id.refresh_weather);        windDir = (TextView) findViewById(R.id.wind_dir);        windSc = (TextView) findViewById(R.id.wind_sc);        switchCity.setOnClickListener(this);        refreshWeather.setOnClickListener(this);        // ChooseAreaActivity  传过来的值        String countyCode = getIntent().getStringExtra("county_code");        // 有代号显示 选择城市的天气,没有就本地已存储的天气        if (!TextUtils.isEmpty(countyCode)) {            publishText.setText("同步中...");            // 隐藏            weatherInfoLayout.setVisibility(View.INVISIBLE);            cityNameText.setVisibility(View.VISIBLE);           // 查天气            queryWeatherInfo(countyCode);        } else {            showWeather();        }    }/**     * 显示天气,直接从 SharedPreferences 去取,取不到就存。     */    private void showWeather() {        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);        cityNameText.setText( prefs.getString("city_name", ""));        tempText.setText(prefs.getString("temp", ""));        weatherDespText.setText(prefs.getString("weather_desp", ""));        String publish_time = prefs.getString("publish_time", "");        publish_time = publish_time.split(" ").length > 1 ? publish_time.split(" ")[1] : "";        publishText.setText("今天" + publish_time + "发布");        windDir.setText(prefs.getString("wind_dir", ""));        windSc.setText(prefs.getString("wind_sc", ""));        currentDateText.setText(prefs.getString("current_date", ""));        // 显示        weatherInfoLayout.setVisibility(View.VISIBLE);        cityNameText.setVisibility(View.VISIBLE);    }// 查天气信息private void queryWeatherInfo(String weatherCode) {        String address = String.format("https://api.heweather.com/x3/weather?cityid=CN%s&key=%s", weatherCode, "填写api key");        Log.d(TAG, address);        queryFromServer(address);    }    /**     * 从服务器 获取 最新的天气数据     * @param address     */    private void queryFromServer(String address) {        HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {            @Override            public void onFinish(String response) {                WeatherInfoUtil.handleWeatherResponse(WeatherActivity.this, response);                runOnUiThread(new Runnable() {                    @Override                    public void run() {                        showWeather();                    }                });            }            @Override            public void onError(final Exception e) {                runOnUiThread(new Runnable() {                    @Override                    public void run() {                        e.printStackTrace();                        publishText.setText("同步失败");                    }                });            }        });    }// 点击返回 和 刷新效果@Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.switch_city:                // 选择城市,返回上一层                Intent intent = new Intent(this, ChooseAreaActivity.class);                intent.putExtra("from_weather_activity", true);                startActivity(intent);                finish();                break;            case R.id.refresh_weather:                // 刷新天气                publishText.setText("同步中...");                SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);                String weatherCode = preferences.getString("weather_code", "");                // 获取id 重新去访问天气信息                if (!TextUtils.isEmpty(weatherCode)) {                    queryWeatherInfo(weatherCode);                }                break;            default:                break;        }    }

加入权限和注册活动

后台定时更新活动

这部分功能不加也行,后续扩展吧。

public class AutoUpdateService extends Service{    @Nullable    @Override    public IBinder onBind(Intent intent) {        return null;    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        new Thread(new Runnable() {            @Override            public void run() {                updateWeather();            }        }).start();        AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);        // 8h        int anHour = 8 * 60 * 60 * 1000;        long triggerAtTime = SystemClock.elapsedRealtime() + anHour;        Intent i = new Intent(this, AutoUpdateReceiver.class);        PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);        alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);        return super.onStartCommand(intent, flags, startId);    }    private void updateWeather() {        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);        String weatherCode = preferences.getString("weather_code", "");        String address = String.format("https://api.heweather.com/x3/weather?cityid=CN%s&key=%s", weatherCode, "填写app key");        HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {            @Override            public void onFinish(String s) {                WeatherInfoUtil.handleWeatherResponse(AutoUpdateService.this, s);            }            @Override            public void onError(Exception e) {                e.printStackTrace();            }        });    }}

来个定时接收

public class AutoUpdateReceiver extends BroadcastReceiver{    @Override    public void onReceive(Context context, Intent intent) {        Intent i = new Intent(context, AutoUpdateService.class);        context.startActivity(i);    }}

别忘注册

在哪里启动这个服务呢?

成功载入天气数据的时候: showWeather()方法内

// 启动服务Intent intent = new Intent(this, AutoUpdateService.class);startService(intent);

差不多就完了。

其实这个很简陋,只显示今天的,可以搞个折线图,把7天的天气预报也显示出来。

还有一些天气的图标,也可以获取到,这个api还是很全的。

转载于:https://my.oschina.net/u/2385255/blog/777299

你可能感兴趣的文章
Locale java
查看>>
【转】Android 环境变量 和 AVD 环境变量 配置
查看>>
使用Weka进行数据挖掘
查看>>
关于计算机信息系统集成项目经理资质申报的补充通知
查看>>
飞机大战小游戏
查看>>
中国物联网的随笔
查看>>
VMware虚拟机linux系统时间同步的解决办法
查看>>
fsck修复受损的文件系统
查看>>
迅雷Bolt的ClipSubBindBitmap函数特别说明
查看>>
『Island 基环树直径』
查看>>
『点分治及其简单运用』
查看>>
ios app分享的url在微信中打开app分析
查看>>
HPUX cstm无法使用
查看>>
我的友情链接
查看>>
Linux网络管理工具
查看>>
Linux磁盘管理7
查看>>
【学神】 1-3xmanager远程工具的使用和vim编辑器的使用
查看>>
IBM 把 Informix 数据库给“卖了”
查看>>
maven在windows和Linux下的安装
查看>>
如何解锁Oracle数据库中账号
查看>>