安卓天气查询预报APP软件的制作

最近因为在上安卓课,做了一个简单的安卓天气查询APP,正好刚上交该软件的作业报告,顺带写一篇博文将其记录一下。该APP的功能是可以查看目前我国三万多个地区的当天以及未来三天的天气状况,而查询的输入是城市ID、城市中文名称或者城市拼音三者都可以。废话不多说,开始贴代码了

1 布局代码说明:

1.1 activity_main布局代码

这个App自己加入了启动的动画显示,即点击应用图标,即显示静态图片1500毫秒之后再进入天气获取界面。因此在主布局
activity_main中只放置一张图片。将这张照片设置成为背景即可。代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.apress.gerber.jsonapp.MainActivity"
    android:background="@drawable/timg">
1.2acyivity_choose布局代码

这个布局用于显示查询城市的天气,布局较为复杂。代码如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/buju"
    android:background="@drawable/mainbackground">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:layout_alignParentTop="true"
        android:layout_alignParentStart="true">
        <TextView
            android:id="@+id/showcity"
            android:layout_width="match_parent"
            android:layout_height="74dp"
            android:text="城市"
            android:textSize="50dp"
            android:textColor="#dd413939"
            android:gravity="center"/>
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="420dp">
            <ImageView
                android:id="@+id/weatherpicture"
                android:layout_width="80dp"
                android:layout_height="80dp"
                android:layout_marginStart="52dp"
                android:layout_alignParentTop="true"
                android:layout_alignParentStart="true"
                android:layout_marginTop="44dp" />
            <TextView
                android:id="@+id/weathercondition"
                android:layout_width="140dp"
                android:layout_height="40dp"
                android:textSize="16dp"
                android:layout_below="@+id/weatherpicture"
                android:layout_alignStart="@+id/weatherpicture"
                android:layout_marginTop="15dp" />
            <TextView
                android:id="@+id/weathertempter"
                android:layout_width="150dp"
                android:layout_height="120dp"
                android:textSize="16dp"
                android:layout_alignTop="@+id/weatherpicture"
                android:layout_alignParentEnd="true"
                android:layout_toEndOf="@+id/weathercondition" />
            <TextView
                android:id="@+id/otherday"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="18dp"
                android:textSize="16dp"
                android:layout_below="@+id/weathercondition"
                android:layout_alignParentStart="true" />
        </RelativeLayout>
    </LinearLayout>
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal">
    <Button
        android:id="@+id/beijing"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="2dp"
        android:layout_weight="1"
        android:text="北京"
        android:background="@drawable/button_grey"
        />
    <EditText
        android:id="@+id/insert1"
        android:layout_width="1dp"
        android:layout_height="wrap_content"
        android:background="#aba8a8"
        android:focusable="false"/>
    <Button
            android:id="@+id/xuzhou"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/beijing"
            android:layout_weight="1"
            android:text="徐州"
        android:background="@drawable/button_grey"/>
        <EditText
            android:id="@+id/insert2"
            android:layout_width="1dp"
            android:layout_height="wrap_content"
            android:background="#aba8a8"
            android:focusable="false"/>
        <Button
            android:id="@+id/shanghai"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/insert2"
            android:layout_weight="1"
            android:text="上海"
            android:background="@drawable/button_grey"/>
        <EditText
            android:id="@+id/insert3"
            android:layout_width="1dp"
            android:layout_height="wrap_content"
            android:background="#aba8a8"
            android:focusable="false"/>
        <Button
            android:id="@+id/othercity"
            android:layout_width="105dp"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/insert3"
            android:text="其他城市...."
            android:background="@drawable/button_grey"/>
    </LinearLayout>
</RelativeLayout>

总体为相对布局,在相对布局中分为了三大块,顶部,中部以及底部。顶部用于显示用户查询的城市的名称,中部主要是
ImageView和各类TextView控件,ImageView用于显示当天该城市的天气情况,而TextView用于显示返回的Json天气数据经过
解析之后的结果。底部用于显示四个按钮,分别是显示西安、徐州、深圳以及其他城市。点击前三个城市可以在顶部和中部实现
显示西安、徐州、深圳以及其他城市。点击前三个城市可以在顶部和中部实现显示该城市的当天以及未来三天的天气状况。而点
击其他
按钮则会提示用户输入一个需要查询的城市,确认之后同样可以显示查询城市的天气状况。

2 Activity代码说明

2.1 MainActivity代码说明:

MainActivity用于显示启动的动画,因此在onCreate()中只开了一个线程用于延时1.5秒操作,延时完成即跳转到ChooseActivity
中查看城市天气。代码如下所示:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
Intent mainIntent=new Intent (MainActivity.this,ChooseActivity.class);
                MainActivity.this.startActivity(mainIntent);
                finish();
            }
        },1500);
}
2.2 ChooseActivity 代码说明

这个活动中主要实现的就是根据用户输入的城市名称、拼音或者城市ID来查询当地当天以及未来三天的天气状况。
按照程序运行的顺序,首先点击按钮来获取天气:

@Override
    public void onClick(View view) {
        switch (view.getId()){
            case  R.id.beijing:
                City_ID = "101010100";
                City_Name = "北京";
                City_Provience = "北京";
                sendRequestWithHttpClient();
                break;
            case  R.id.xuzhou:
                City_ID = "101190801";
                City_Name = "徐州";
                City_Provience = "江苏";
                sendRequestWithHttpClient();
                break;
            case  R.id.shanghai:
                City_ID = "101020100";
                City_Name = "上海";
                City_Provience = "上海";
                sendRequestWithHttpClient();
                break;
            case  R.id.othercity:
                create_dialog();
                break;
            default:
                City_Name = "徐州";
                sendRequestWithHttpClient();
                break;

        }
}

onClick()函数来判断点击了哪个按钮,之后将City_Name赋值为该城市的名称。若点击的是前三个城市按钮,则接下来调用

sendRequestWithHttpClient()进行网络请求。


public void sendRequestWithHttpClient(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                int code;
                try {
                    String path = "http://api.map.baidu.com/telematics/v3/weather?location="+City_Name+"&output=json&ak=GuZriL3rkm1MUnyTyfsNGvTC";
                    URL url = new URL(path);
                    /**
                     * 这里网络请求使用的是类HttpURLConnection,另外一种可以选择使用类HttpClient。
                     */
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.setRequestMethod("GET");//使用GET方法获取
                    conn.setConnectTimeout(5000);
                    code = conn.getResponseCode();
                    if (code == 200) {
                        /**
                         * 如果获取的code为200,则证明数据获取是正确的。
                         */
                        InputStream is = conn.getInputStream();
                        String result = HttpUtils.readMyInputStream(is);
                        /**
                         * 子线程发送消息到主线程,并将获取的结果带到主线程,让主线程来更新UI。
                         */
                        Message msg = new Message();
                        msg.obj = result;
                        msg.what = SUCCESS;
                        handler.sendMessage(msg);
                    }else{

                        Message msg = new Message();
                        msg.what = ERRORCODE;
                        handler.sendMessage(msg);
                    }
                }catch (Exception e){
                    Toast.makeText(ChooseActivity.this,"网络请求失败",Toast.LENGTH_SHORT).show();
                }
            }
        }).start();
    }
sendRequestWithHttpClient()函数中,根据输入的网址信息path,利用网络请求类HttpURLConnection与服务器建立连接,设置请
求方式为“GET”,连接超时为5000,之后根据返回码code是否为200来判断获取数据是否成功。不成功则提示错误,成功则获取字节
流,再用自定义的HttpUtils将获取的字节流转换成字符串再通过hangler将其发送到主线程中的Handler中去。

private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SUCCESS:
                    /**
                     * 获取信息成功后,对该信息进行JSON解析,得到所需要的信息,然后在textView上展示出来。
                     */
                    JSONAnalysis(msg.obj.toString());
                    Toast.makeText(ChooseActivity.this, "获取数据成功", Toast.LENGTH_SHORT)
                            .show();
                    break;

                case FAILURE:
                    Toast.makeText(ChooseActivity.this, "获取数据失败", Toast.LENGTH_SHORT)
                            .show();
                    break;
                case PICTURE:
                    weatherpiture.setImageDrawable((Drawable) msg.obj);
                    break;
                case ERRORCODE:
                    Toast.makeText(ChooseActivity.this, "获取的CODE码不为200!",
                            Toast.LENGTH_SHORT).show();
                    break;
                default:
                    break;
            }
        }
    };
Msg的状态码有四个,分别是成功获取数据,获取数据失败,获取照片(在请求照片中会用到),网络请求失败。
在成功获取数据时,调用JSONAnalysis对获取到的字符串进行Json解析。

在解析之前,先来看看这个网址:

http://api.map.baidu.com/telematics/v3/weather?location=徐州&output=json&ak=GuZriL3rkm1MUnyTyfsNGvTC

只要把徐州换成任何一个地区就行,接着看看它返回的数据:

{"error":0,"status":"success","date":"2017-06-30","results":[{"currentCity":"徐州","pm25":"105","index":[{"title":"穿衣","zs":"炎热","tipt":"穿衣指数","des":"

天气炎热,建议着短衫、短裙、短裤、薄型T恤衫等清凉夏季服装。"},{"title":"洗车","zs":"不宜","tipt":"洗车指数","des":"不宜洗车,未来24小时内有雨,

如果在此期间洗车,雨水和路上的泥水可能会再次弄脏您的爱车。"},{"title":"感冒","zs":"少发","tipt":"感冒指数","des":"各项气象条件适宜,发生感冒机率

低。但请避免长期处于空调房间中,以防感冒。"},{"title":"运动","zs":"较不宜","tipt":"运动指数","des":"有降水,推荐您在室内进行低强度运动;若坚

持户外运动,须注意选择避雨防滑并携带雨具。"},{"title":"紫外线强度","zs":"弱","tipt":"紫外线强度指数","des":"紫外线强度较弱,建议出门前涂擦SPF

在12-15之间、PA+的防晒护肤品。"}],"weather_data":[{"date":"周五 06月30日 (实时:31℃)","dayPictureUrl":"http://api.map.baidu.com/images/

weather/day/leizhenyu.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/zhongyu.png","weather":"雷阵雨转中雨","wind":"南风

微风","temperature":"33 ~ 23℃"},{"date":"周六","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/zhongyu.png","nightPictureUrl":"http:

//api.map.baidu.com/images/weather/night/yin.png","weather":"中雨转阴","wind":"南风微风","temperature":"29 ~ 23℃"},{"date":"周日","dayPictureUrl"

:"http://api.map.baidu.com/images/weather/day/xiaoyu.png","nightPictureUrl":"http://api.map.baidu.com/images/weather/night/yin.png","weather":"小

雨转阴","wind":"西南风微风","temperature":"32 ~ 24℃"},{"date":"周一","dayPictureUrl":"http://api.map.baidu.com/images/weather/day/xiaoyu.png",

"nightPictureUrl":"http://api.map.baidu.com/images/weather/night/xiaoyu.png","weather":"小雨","wind":"南风微风","temperature":"31 ~ 23℃"}]}]}

以上就是服务器返回的JSON数据,接下来就是解析就可以了:

protected void JSONAnalysis(String string) {
        JSONObject o;
        try {
            o = new JSONObject(string);
            int returnResult = o.getInt("error");
            if (returnResult == 0) {   //returnResult表示成功返回JSON数据
                String date = o.getString("date");  //日期
                JSONArray posts = o.getJSONArray(("results"));// 只有一个result
                JSONObject tempObj = (JSONObject) posts.get(0);// 第0个对象,第0个对象是当天的天气,先只显示当天的天气
                cityname.setText(tempObj.getString("currentCity")); //设置城市的名字

                JSONArray tempArr = tempObj.getJSONArray("weather_data");
                JSONObject tempWeatherData = (JSONObject) tempArr.get(0);// 当天天气
                //时间、温度以及PM2.5
                String PM = tempObj.getString("pm25");
                if (PM.length()==0)PM="无数据";
                temptureanddate.setText("时间:"+tempWeatherData.getString("date")+"\n"+tempWeatherData.getString("temperature")+"\n"+"PM2.5:"+PM);
                //获取天气图片
                image_url = tempWeatherData.getString("dayPictureUrl").toString();
                //获取天气状况图片
                getBitmap(image_url);
                weatherpiture.setImageDrawable(drawable);
                //天气状况以及风力
                weatherandwind.setText(tempWeatherData.getString("weather")+"    "+tempWeatherData.getString("wind"));

                /*获取后三天的数据
                * */
                String threedays = null;
                JSONObject day1 = (JSONObject) tempArr.get(1);// 第1个对象,第1对象是后一天的数据
                JSONObject day2 = (JSONObject) tempArr.get(2);//第2个对象,第2对象是后两天的数据
                JSONObject day3 = (JSONObject) tempArr.get(3);//第3个对象,第3对象是后三天的数据
                threedays = "\n"+tempObj.getString("currentCity")+"接下来三天的天气状况"+"\n"+"\n"+day1.getString("date")+":"+day1.getString("weather")+"  
 温度:"+day1.getString("temperature")+"   风力:"+day1.getString("wind")+"\n"+"\n"+
                day2.getString("date")+":"+day2.getString("weather")+"   温度:"+day2.getString("temperature")+"   风力:"+day2.getString("wind")+"\n"+"\n"+
                        day3.getString("date")+":"+day3.getString("weather")+"   温度:"+day3.getString("temperature")+"   风力:"+day3.getString("wind");
                otherdays.setText(threedays);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        /**
         * 在获取的string这个JSON对象中,提取所需要的信息。先测试一下
         */
        System.out.println(string);
        Log.i("JSON",string);
    }

上述即为Json解析的函数,解析过程比较简单,解析之前先创建一个JSONObject,之后根据key值来获取数据,其中会涉及到JSON数组,需要用到

JSONArry,但是总体解析并不复杂,之后将解析的数据显示在TextView中即可,这样可以显示该地区当天以及未来三天的天气状况。
但其中有个问题,显示当天天气的图片返回的是一个网址,因此还需要通过网络编程来获取图片。代码如下所示:

public void getBitmap(final String path) throws IOException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    URL url = new URL(path);
                    HttpURLConnection conn = null;
                    conn = (HttpURLConnection)url.openConnection();
                    conn.setConnectTimeout(5000);
                    conn.setRequestMethod("GET");
                    if(conn.getResponseCode() == 200){
                        InputStream inputStream = conn.getInputStream();
                        Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                        drawable = new BitmapDrawable(bitmap);
                        Log.i("Bitmap",bitmap.toString());
                        Log.i("Drawable",drawable.toString());
                        Message msg = new Message();
                        msg.obj = drawable;
                        msg.what = PICTURE;
                        handler.sendMessage(msg);
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }).start();
    }

GetBitmap()获取照片的方法和之前网络请求JSON数据思路一模一样,请求完之后,将msg的状态码置为 PICTURE(获取照片),调用Handle在主线程中更新

ImageView即可。至此即完成了天气的获取。

接下来是第四个按钮,点击之后生成的是一个可以输入文字的AlertDialog提示框,点击之后用户可以用三种方式输入:城市的中文名称、城市的拼音、城市的ID,

输入其中任何一项都可以查询到当地的天气。代码如下所示:

public void create_dialog(){
        final EditText et = new EditText(this);
        et.setMaxLines(1);
        AlertDialog.Builder builder = new AlertDialog.Builder(ChooseActivity.this);
        builder.setTitle("查询的城市名称,格式例如:徐州 OR (xuzhou),拼音不区分大小写");
        builder.setIcon(android.R.drawable.btn_star);
        builder.setView(et);
        builder.setPositiveButton("确认", new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
               City_Name = et.getText().toString();
                if (City_Name == null) {
                    Toast.makeText(ChooseActivity.this, "请不要输入错误的数据", Toast.LENGTH_SHORT).show();
                }
                if (City_Name.length()==0)
                    City_Name="徐州";
                sendRequestWithHttpClient();
            }
        });
        builder.setNegativeButton("取消",null);
        builder.show();
    }
其中调用的请求JSON以及解析JSON数据的函数在之前已经说明。这样就可以实现对我国目前三万多个不同地区天气的查询,功能非常强大。
最后的一部分关键代码是自己写的将网络请求得到的字节流转换成字符串的HttpUtils类。代码如下:

public class HttpUtils {
	public static String readMyInputStream(InputStream is) {
		byte[] result;
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			int len;
			while ((len = is.read(buffer))!=-1) {
				baos.write(buffer,0,len);
			}
			is.close();
			baos.close();
			result = baos.toByteArray();
			
		} catch (IOException e) {
			e.printStackTrace();
			String errorStr = "获取数据失败。";
			return errorStr;
		}
		return new String(result);
	}
(最后需要注明一项,目前百度天气查询不再提供天气查询API的获取,此次的链接:http://api.map.baidu.com/telematics/v3/weather?location=徐州&output=json&ak=GuZriL3rkm1MUnyTyfsNGvTC 来自CSDN博主:u012606241,网址链接:http://blog.csdn.net/u012606241/article/details/25656207)

二、显示截图



最后该项目的资源链接如下:

http://download.csdn.net/detail/jian_yun_rui/9889477



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章