“移动应用开发”实验报告
报告书
姓名
指导教师
学号
日    期
实验名称
天气预报系统
实验内容即题目内容
开发一个天气预报系统,具备以下功能:
1、可以罗列全国所有省市县
2、可以查看全国任意城市天气信息
3、可以自由切换城市,去查看其它城市的天气
4、提供手动更新以及后台自动更新天气的功能
小组成员和任务分工
姓名
学号
任务分工
A
界面布局设计和主活动代码编写
B
数据库建立和封装
C
网络和服务器的数据请求以及xml解析
D
查看某地区天气的活动代码编写和添加权限与注册的相关代码
数据代码即运行结果截图
一、整体架构、
本次实验我分配的任务是查看某地区天气的活动代码编写和添加权限与注册的相关代码,具体的过程在下面:
1、活动(Activity)整体结构
2、设计界面(layout)整体结构
二、数据代码
本次作业(天气预报)的设计界面效果图如下
接着需要编写遍历省市县的活动MainActivity。代码如下:
package ample.chen.weatherforecast;
import android.app.ProgressDialog;
import t.Intent;
import t.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import TextUtils;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import DB.CoolWeatherDB;
import model.City;
import model.County;
import model.Province;
import unity.HttpCallbackLinster;
import unity.HttpUtil;
import util.Utility;
public class MainActivity extends AppCompatActivity {
    public  static  final  int LEVEL_PROVINCE = 0; //省级
    public  static  final  int LEVEL_CITY = 1; //市级
    public  static  final  int LEVEL_COUNTY = 2; //县级
    private ProgressDialog progressDialog;
    private ListView listView; //显示省市县的数据
    private TextView title_text; //标题内容
    private ArrayAdapter<String> adapter//listView 的适配器
    private List<String> datalist = new ArrayList<String>();
    private List<Province> provinceList; //省列表
    private List<City> cityList; //市列表
    private List<County> countyList; //县列表
    private CoolWeatherDB coolWeatherDB;
    private Province selectProvince; //选中的省
    private City selectCity; //选中的市
    private  int currentLevel; //选中的县
    private Boolean isFromWeatherActivity;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        isFromWeatherActivity = getIntent().getBooleanExtra("from_weather_activity",false);
        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
        if(Boolean("city_cheeked",false)&& !isFromWeatherActivity ) {
            Intent intent = new Intent(this,LookWeather.class);
            startActivity(intent);
            finish();
        } listView = (ListView)findViewById(R.id.list_view);
        title_text = (TextView)findViewById(R.id.title_text);
        adapter = new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_list_item_1,datalist);
        listView.setAdapter(adapter);
        coolWeatherDB = CoolWeatherDB.getInstance(this);
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if(currentLevel == LEVEL_PROVINCE) {
                    selectProvince = provinceList.get(position);
                    querCity(); //选中省,则查该省的市
        } else if(currentLevel == LEVEL_CITY) {
                    selectCity = cityList.get(position);
                    querCounty(); //选中市,则查该市的县
        } else if(currentLevel == LEVEL_COUNTY) //选中县,则查该县所对应的天气
        {
            String countyCode = countyList.get(position).getCountycode();
            Intent intent = new Intent(MainActivity.this,LookWeather.class);
            intent.putExtra("county_code",countyCode);
            startActivity(intent);
            finish();
        } } });
        querProvince();
    } //查省,如果在数据库中没有到,则在服务器上
    private  void querProvince() {
        provinceList = coolWeatherDB.loadProvince();
        if(provinceList.size() > 0) {
            datalist.clear();
            for(Province province : provinceList) {
                datalist.ProvinceName());
            }
            adapter.notifyDataSetChanged();
            listView.setSelection(0);
            title_text.setText("中国");
            currentLevel = LEVEL_PROVINCE;
        } else
        { //在服务器上查
            querfromServer(null, "province");
        } } //查市,如果在数据库中没有到,则在服务器上
    private  void  querCity() {
        cityList = coolWeatherDB.loadCity(selectProvince.getId());
        if(cityList.size() > 0) {
            datalist.clear();
            for(City city : cityList) {
                datalist.CityName());
            }
            adapter.notifyDataSetChanged();
            listView.setSelection(0);
            title_text.setText(selectProvince.getProvinceName());
            currentLevel = LEVEL_CITY;
        } else
        { querfromServer(selectProvince.getProvinceCode(),"city");
        } } //查县,如果在数据库中没有到,则在服务器上
    private  void  querCounty() {
        countyList = coolWeatherDB.loadCounty(selectCity.getId());
        if(countyList.size() > 0) {
            datalist.clear();
            for(County county : countyList) {
                datalist.CountyName());
            } adapter.notifyDataSetChanged();
            listView.setSelection(0);
            title_text.setText(selectCity.getCityName());
            currentLevel = LEVEL_COUNTY;
        } else
        { querfromServer(selectCity.getCityCode(),"county");
        } } //根据传入的代号和类型在服务器上查数据
    private void  querfromServer(final String code,final  String type) { String address;
        if(!TextUtils.isEmpty(code)) { address = "www.weather/data/list3/city" + code +".xml";
        } else
        { address = "www.weather/data/l";
        } showProgressDialog();
        HttpUtil.sendRequstWithURLConnection(address, new HttpCallbackLinster() {
            @Override
        public void onFinish(String response) { boolean result = false;
            if ("province".equals(type)) { result = Utility.handleProvince(coolWeatherDB, response);
            } else if ("city".equals(type)) { result = Utility.handleCity(coolWeatherDB, response, selectProvince.getId());
            } else if ("county".equals(type)) { result = Utility.handleCounty(coolWeatherDB, response, selectCity.getId());
            } if (result) { runOnUiThread(new Runnable() { @Override
            public void run() { closeProgressDialog();
                if ("province".equals(type)) { querProvince();
                } else if ("city".equals(type)) { querCity();
                } else if ("county".equals(type)) { querCounty();
                } } });
            } } public void  onError(Exception e) { runOnUiThread(new Runnable() { @Override
        public void run() { closeProgressDialog();
            Toast.makeText(MainActivity.this,"加载失败",Toast.LENGTH_SHORT).show();
        } });
        } });
    } private void  showProgressDialog() { if(progressDialog == null) { progressDialog = new ProgressDialog(MainActivity.this);
        progressDialog.setMessage("正在加载...");
        progressDialog.setCanceledOnTouchOutside(false);
    } progressDialog.show();
    } private  void  closeProgressDialog() {
        if(progressDialog != null) {
            progressDialog.dismiss();
    } } //捕获返回键,来判断此时应直接返回省,市列表还是返回到显示天气的页面
    public  void onBackPressed() {
        if(currentLevel == LEVEL_COUNTY) {
            querCity();
    }else if(currentLevel == LEVEL_CITY) {
            querProvince();
    }else { if (isFromWeatherActivity) {
            Intent intent = new Intent(MainActivity.this, LookWeather.class);
            startActivity(intent);
    } finish();
    } } }
还要加上权限声明和注册ActivityAndroidManifest.xml的代码:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="schemas.android/apk/res/android"
    package="ample.chen.weatherforecast">
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".LookWeather"> </activity>
    </application>
</manifest>
2获取省县的天气数据
在Utility类中添加解析JSON数据与处理。(代码请见前面Utility类的代码)。
接着在Layout写LookWeatherActivity的布局。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="schemas.android/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="#0099ff">
        <Button
            android:id="@+id/switch_bt"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_marginStart="10dp"
        android:layout_centerVertical="true"
        />
        <TextView
            android:id="@+id/weather_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="24sp"
        android:textColor="#FFFFFF"
        android:layout_centerInParent="true"
        />
        <Button
            android:id="@+id/refresh"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_alignParentEnd="true"
        android:layout_marginRight="10dp"
        android:layout_centerInParent="true"/>
    </RelativeLayout>
    <RelativeLayout
        android:background="#99cccc"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:id="@+id/punish_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentEnd="true"
        android:textSize="28sp"
        android:layout_marginTop="20dp"
        android:layout_marginEnd="20dp"
        />
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:orientation="vertical">
            <TextView
                android:id="@+id/data"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textSize="25sp"
            />
            <TextView
                android:id="@+id/weather_detail"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textSize="35sp"
            />
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
                <TextView
                    android:id="@+id/low_temp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:textSize="30sp"
                />
                <TextView
                    android:id="@+id/temp_line"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:textSize="30sp"
                android:text="~"/>
                <TextView
                    android:id="@+id/high_temp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:textSize="30sp"
                />
            </LinearLayout>
        </LinearLayout>
    </RelativeLayout>
</LinearLayout>
布局效果图如下中间部分显示天气情况左上角的按钮功能是回退右上角的按钮功能是刷新(同步最新天气信息)。
接着写LookWeather的Activity。
package ample.chen.weatherforecast;
import t.Intent;
import t.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import TextUtils;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
import unity.HttpCallbackLinster;
import unity.HttpUtil;
import util.Utility;
/**
* Created by Administrator on 2018/12/29.
*/
public class LookWeather extends AppCompatActivity implements View.OnClickListener{
    private TextView title;
    private TextView punish_time;
    private TextView data;
    private TextView detail;
    private  TextView low_temp;
    private TextView high_temp;
    private Button switch_bt;
    private Button refresh;
    @Override
    protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        //setContentView(R.layout.cheeked_weather);
        setContentView(R.layout.look_weather_activity);
        findviews();
        String countyCode = getIntent().getStringExtra("county_code");
        if(!TextUtils.isEmpty(countyCode)) {
            queryWeatherCode(countyCode);
        } else
        { showWeather();
        } switch_bt.setOnClickListener(this);
        refresh.setOnClickListener(this);
    } private void queryWeatherCode(String countyCode) { String address = "www.weather/data/list3/city" + countyCode +".xml";
        queryFromService(address, "countyCode");
    } private void queryInfo(String weatherCode) { String address = "www.weather/data/cityinfo/" + weatherCode +".html";
        queryFromService(address,"weatherCode");
    } private void queryFromService(final String address, final String type) {
        HttpUtil.sendRequstWithURLConnection(address, new HttpCallbackLinster() {
            @Override
    public void onFinish(String response) { if("countyCode".equals(type)) {
                if(!TextUtils.isEmpty(response)) { String[] array = response.split("\\|"android layout布局);
        if(array != null && array.length == 2) { String weatherCode = array[1];
            queryInfo(weatherCode);
        } } }else if("weatherCode".equals(type)) {
        Utility.handleWeatherResorce(LookWeather.this, response);
        runOnUiThread(new Runnable() { @Override
        public void run() { showWeather();
        } });
    } } @Override
    public void onError(Exception e) { runOnUiThread(new Runnable() { @Override
    public void run() { punish_time.setText("更新失败");
    } });
    } });
    } private void showWeather() {
        SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(LookWeather.this);
        title.String("city_name",""));
        punish_time.setText("今天"+String("punish_time","")+"发布");
        data.String("data",""));
        detail.String("weather",""));
        low_temp.String("low_temp",""));
        high_temp.String("high_temp",""));
    } private void findviews() { title = (TextView)findViewById(R.id.weather_title);
        punish_time = (TextView)findViewById(R.id.punish_time);
        data = (TextView)findViewById(R.id.data);
        detail = (TextView)findViewById(R.id.weather_detail);
        low_temp = (TextView)findViewById(R.id.low_temp);
        high_temp = (TextView)findViewById(R.id.high_temp);
        switch_bt = (Button)findViewById(R.id.switch_bt);
        refresh = (Button)findViewById(R.id.refresh);
    } @Override
    public void onClick(View v) { switch (v.getId()) {
        case R.id.switch_bt:
        Intent intent = new Intent(LookWeather.this,MainActivity.class);
        intent.putExtra("from_weather_activity", true);
        startActivity(intent);
        finish();
        break;
        case R.id.refresh: title.setText("正在同步中...");
            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
            String weatherCode = sp.getString("city_code","");
            if(! TextUtils.isEmpty(weatherCode)) { queryInfo(weatherCode);
            } break;
        default: break;
    } } }
这个Activity同样需要注册,注册代码请见上文的l。
三、运行结果截图
天气预报APP的界面如图所示:
以查看宁波的天气为例,先在最上级目录中到浙江省,点击进入下一级目录:
点击宁波市,进入选择县(区)的目录:
选择第一个(宁波市区),点击查看宁波的天气:
若想查看其它城市的天气,则后退选择其他省份的其他城市即可。例如想查看杭州的天气:
错误总结
  这个活动中的代码比较长,在onCreate()方法中是去获取一些控件的实例,然后会尝试从本地缓存中读取天气数据。第一次是没有缓存的,因此会从Intent中取出天气id,并调用requestWeather()方法来从服务器请求天气数据。出现空数据的界面看上去会很奇怪
   在这次作业的过程当中缓存好,出现下一次再进入WeatherActivity时,再次发起网络请求了。
Android studio软件的错误提示是最多的,一次性没完成就保存后工程目录下的文件,下次打开出现一大堆红错误提示,由于英语水平有限,有些问题真的不知道怎么解决。
个人心得体会
通过这次实验掌握了Android应用程序的基本设计方法和思路,掌握了使用多种组件进行Android程序开发的方法。能够根据实际项目的需求,准确的分析出Android应用程序开发所可能涉及到的知识点,通过分析软件的需求,设计出用户界面和模块结构,并最终完成应用程序的开发和调试。
这次作业最大的收获是对Android Studio的错误处理技能的提升。一次又一次出现的系统错误都是英文的,通过查阅相关资料学会了相关错误的解决方法。刚开始安装软件到现在的问题都是一种对软件的使用技能的巩固。