极简天气app

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Dongfnag_HU/article/details/85061169

最近在学习Android,这里就用这个极简天气app练练手吧。

制作思路:

  1. UI布局
  2. 城市选择
  3. 对应城市天气数据获取与处理
  4. 刷新UI

先看看效果吧(前面有点迟钝):

我点击右上角的城市管理按钮后进入城市选择(里面的城市是随便写的哦),确认后返回主界面,这里只显示高温而已。

确实极简吧,但是其中的处理过程可是一点没有少的!是要少加布局上的修饰就瞬间高大上了。

先看布局文件(activity_main.xml):

@+id/activity_main_textview_city这是用于显示我们选择的城市的TextView。

@+id/activity_main_textview_content这是显示天气内容的TextView(我偷懒把所有的内容都放进去了,嘿嘿,嘿嘿)

<?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">

    <Button
        android:id="@+id/activity_main_button_chengshiguanli"
        android:layout_width="50dp"
        android:layout_height="25dp"
        android:layout_alignParentTop="true"
        android:layout_alignParentRight="true"
        android:layout_marginTop="10dp"
        android:layout_marginRight="10dp"
        android:text="@string/chengshiguanli"
        android:background="@color/colorWhite"
        android:textSize="5pt"/>

    <TextView
        android:id="@+id/activity_main_textview_city"
        android:layout_width="150dp"
        android:layout_height="50dp"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="10dp"
        android:textSize="17pt"
        android:gravity="center"
    />

    <TextView
        android:id="@+id/activity_main_textview_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/activity_main_textview_city"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:layout_marginBottom="10dp"
        />
</RelativeLayout>

下面是选择城市的布局文件(choose_city.xml):

其中我们用到了com.wx.wheelview.widget.WheelView组件,这是一个开源的滚轮组件,使用之前需要先导入相应的包,这里是其项目地址和详细的使用说明:https://github.com/venshine/WheelView

"@+id/city_btn",android:text="boom" 这个按钮是我们做测试用的。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
   >

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.wx.wheelview.widget.WheelView
            android:id="@+id/wheel_province"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>

        <com.wx.wheelview.widget.WheelView
            android:id="@+id/wheel_city"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>

        <com.wx.wheelview.widget.WheelView
            android:id="@+id/wheel_area"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>

    </LinearLayout>

    <Button
        android:id="@+id/city_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="boom"
        android:layout_gravity="center"/>

    <Button
        android:id="@+id/makesure_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="确认"
        android:layout_gravity="center"/>
</LinearLayout>

下面是我们的MainActivity.java文件:

负责为按钮的监听和刷新UI。

这里我们使用startActivityForResult(intent, requestcode)方法进行UI页面的跳转。并用onActivityResult(int requestCode, int resultCode, Intent data)方法根据对应的requestcode来接受ChooseCity.java文件处理后含天气数据的bundle对象,进而刷新UI。

package com.example.administrator.myapplication_weather;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private Button btn_glcs;
    private TextView text_city;
    private TextView text_content;

    private int requestcode;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }

    private void init() {
        btn_glcs = (Button) findViewById(R.id.activity_main_button_chengshiguanli);
        btn_glcs.setOnClickListener(new btn_glcsListener());
        text_city = (TextView) findViewById(R.id.activity_main_textview_city);
        text_content = (TextView)findViewById(R.id.activity_main_textview_content);
    }


    class btn_glcsListener implements View.OnClickListener {
        @Override
        public void onClick(View view) {
            Intent intent = new Intent(MainActivity.this, ChooseCity.class);
            requestcode = 0;
            startActivityForResult(intent, requestcode);
        }
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == 0) {
            Bundle bundle =data.getExtras();
            text_city.setText(bundle.getString("city"));
            text_content.setText(bundle.getString("high"));

        }
    }
}

下面是ChooseCity.java文件用来处理choosecity.xml布局文件对应的逻辑,其中对滚轮的配置较多,但是基本流程是一样的。

在确认按钮的监听事件中有这么一句: new GetContacts().execute();可以在下面看到这个类是继承AsyncTask类的。这是android提供的异步类。(因为android不允许将耗时的操作放在UI线程里,所以要使用异步类(当然新开线程也可以),即 

Void doInBackground(Void... voids)来在后台下载我们的天气数据。

package com.example.administrator.myapplication_weather;

import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.wx.wheelview.adapter.ArrayWheelAdapter;
import com.wx.wheelview.widget.WheelView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;


public class ChooseCity extends Activity {

    public static final String action = "jason.broadcast.action";
    private int requestcode;
    private WheelView wheel_province;
    private WheelView wheel_city;
    private WheelView wheel_area;

    private Button btn_city;
    private Button btn_makesure;

    private Bundle bundle = new Bundle();
    private static  String cityurl = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.choose_city);
        initCity();
    }

    private void initCity()
    {
        wheel_province = findViewById(R.id.wheel_province);
        wheel_city = findViewById(R.id.wheel_city);
        wheel_area = findViewById(R.id.wheel_area);
        //定义WheelView的style,比如选中文字大小和其他文字大小(这里WheelView已经封装了)
        WheelView.WheelViewStyle style = new WheelView.WheelViewStyle();
        style.selectedTextSize = 18;
        style.textSize = 14;

        //在这里设置一个WheelView的Adapter作为数据源的适配器
        wheel_province.setWheelAdapter(new ArrayWheelAdapter(this));
        //为WheelView设置一个皮肤风格(这里在WheelView中已经封装了一个Holo)
        wheel_province.setSkin(WheelView.Skin.Holo);
        //这里将数据放入WheelView中
        wheel_province.setWheelData(createProvinceDatas());
        //设置WheelView的Style(上面已经定义)
        wheel_province.setStyle(style);

        wheel_city.setWheelAdapter(new ArrayWheelAdapter(this));
        wheel_city.setSkin(WheelView.Skin.Holo);
        //这里就很奇妙了,我详细说一下
        //看下面的几个创建数据的函数,从province到city再到area,其中的返回类型中分别为List<String>,HashMap<String, List<String>>, HashMap<String, List<String>>
        //其中第一种为String列表,也就是第一个省份的列表可以直接通过String列表得到。
        //HashMap是哈希表,他里面的值都是通过key-value进行对应,所以在这个情况中就是一个省(String key)对应着一个市(String value)的列表(同理得到第二个市与区的关系)
        //HashMap.get(key)方法是用来通过key的值来得到value的值
        //WheelView.getSelection()通过看就知道是一个获取位置的方法(大神在WheelView中封装好了).
        //综上所述,其实这条东西,逆向来读就是,通过得到省的WheelView的位置来得到省的value值,而省的value值就是市的key值,所以说可以得到市的一整个value值。
        wheel_city.setWheelData(createCityDatas().get(createProvinceDatas().get(wheel_province.getSelection())));
        wheel_city.setStyle(style);

        //这里是把省的WheelView与市的WheelView连接起来(封装好的)(加入下一级的WheelView)
        wheel_province.join(wheel_city);
        //这里是把省的WheelView与市的WheelView的数据连接起来
        wheel_province.joinDatas(createCityDatas());

        wheel_area.setWheelAdapter(new ArrayWheelAdapter(this));
        wheel_area.setSkin(WheelView.Skin.Holo);
        //这个嘛,上面解释过了,但是又臭又长,简单说一下
        //其实就匹配了两次,通过得到省和市的位置来定位到他们两个的value,再通过value得到区的value值
        wheel_area.setWheelData(createAreaDatas().get(createCityDatas().get(createProvinceDatas().get(wheel_province.getSelection())).get(wheel_city.getSelection())));
        wheel_area.setStyle(style);

        wheel_city.join(wheel_area);
        wheel_city.joinDatas(createAreaDatas());



        btn_city = (Button)findViewById(R.id.city_btn);
        btn_city.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String province = wheel_province.getSelectionItem().toString();
                String city = wheel_city.getSelectionItem().toString();
                String area = wheel_area.getSelectionItem().toString();
                Toast.makeText(ChooseCity.this, province + city + area, Toast.LENGTH_SHORT).show();
            }
        });


        btn_makesure = (Button)findViewById(R.id.makesure_btn);
        btn_makesure.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                String area = wheel_province.getSelectionItem().toString();
                cityurl = "http://wthrcdn.etouch.cn/weather_mini?city="+area;
                Log.d("DATA",cityurl);

                new GetContacts().execute();

            }
        });
    }

    //这里是第一级,所以直接把他放入一个List中就可以了
    private List<String> createProvinceDatas() {
        String[] strings = {"黑龙江", "吉林", "北京"};
        //将字符数组转换为List形式
        return Arrays.asList(strings);
    }

    private HashMap<String, List<String>> createCityDatas() {
        //新建一个哈希表
        HashMap<String, List<String>> map = new HashMap<String, List<String>>();
        String[] strings = {"黑龙江", "吉林","北京"};
        String[] s1 = {"哈尔滨", "齐齐哈尔", "大庆"};
        String[] s2 = {"长春", "吉林"};
        String[] s3 = {"沈阳", "大连", "鞍山", "抚顺"};
        String[][] ss = {s1, s2, s3};
        for (int i = 0; i < strings.length; i++) {
            //在这里把key与value分别列出,然后通过HashMap.put进行配对然后写入哈希表。
            map.put(strings[i], Arrays.asList(ss[i]));
        }
        // 一个哈希表的输出检测(自学哈希表时测试一下用的,自己也可以试试)
//        Iterator iter = map.entrySet().iterator();
//        while(iter.hasNext()) {
//            Map.Entry entry = (Map.Entry) iter.next();
//            Object key = entry.getKey();
//            Object value = entry.getValue();
//            Log.v("second",key + ":" + value);
//        }
        return map;
    }

    private HashMap<String, List<String>> createAreaDatas() {
        HashMap<String, List<String>> map = new HashMap<String, List<String>>();
        String[] strings = {"哈尔滨", "齐齐哈尔", "大庆", "长春", "吉林", "沈阳", "大连", "鞍山", "抚顺"};
        String[] s1 = {"道里区", "道外区", "南岗区", "香坊区"};
        String[] s2 = {"龙沙区", "建华区", "铁锋区"};
        String[] s3 = {"红岗区", "大同区"};
        String[] s11 = {"南关区", "朝阳区"};
        String[] s12 = {"龙潭区"};
        String[] s21 = {"和平区", "皇姑区", "大东区", "铁西区"};
        String[] s22 = {"中山区", "金州区"};
        String[] s23 = {"铁东区", "铁西区"};
        String[] s24 = {"新抚区", "望花区", "顺城区"};
        String[][] ss = {s1, s2, s3, s11, s12, s21, s22, s23, s24};
        for (int i = 0; i < strings.length; i++) {
            map.put(strings[i], Arrays.asList(ss[i]));
        }
//        Iterator iter = map.entrySet().iterator();
//        while(iter.hasNext()) {
//            Map.Entry entry = (Map.Entry) iter.next();
//            Object key = entry.getKey();
//            Object value = entry.getValue();
//            Log.v("first",key + ":" + value);
//        }
        return map;
    }

        public Bundle jsonParse(String cityjsondata) throws JSONException {
            Bundle jsonbundle = new Bundle();
            JSONObject reader = new JSONObject(cityjsondata);
            JSONObject weatherdata  = reader.getJSONObject("data");
            JSONArray forecast = weatherdata.getJSONArray("forecast");
            JSONObject todayweather = (JSONObject) forecast.get(0);

            Log.d("DATA",todayweather.getString("low"));
            Log.d("DATA",todayweather.getString("fengli"));
            Log.d("DATA",todayweather.getString("fengxiang"));
            Log.d("DATA",todayweather.getString("type"));

            jsonbundle.putString("high",todayweather.getString("high"));
            jsonbundle.putString("low",todayweather.getString("low"));
            jsonbundle.putString("fengli",todayweather.getString("fengli"));
            jsonbundle.putString("fengxiang",todayweather.getString("fengxiang"));
            jsonbundle.putString("type",todayweather.getString("type"));
            return jsonbundle;
        }

    private class GetContacts extends AsyncTask<Void, Void, Void>
    {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            Log.d("DATA","Json Data is downloading");
            Toast.makeText(ChooseCity.this,"Json Data is downloading",Toast.LENGTH_LONG).show();
        }
        @Override
        protected Void doInBackground(Void... voids) {

            Log.d("DATA","Deal with data");
            GetCityJsonData getdata = new GetCityJsonData();
            String jsoncontent = getdata.GetDataContent(cityurl);
            try {
                    bundle = jsonParse(jsoncontent);
            } catch (JSONException e) {
                Log.d("DATA","ERROR in doInBackground");
                e.printStackTrace();
            }

            return null;
        }

        @Override
        protected void onPostExecute(Void result) {

            super.onPostExecute(result);

            String area = wheel_province.getSelectionItem().toString();

            bundle.putString("city",area);
            bundle.putString("joke","haha");

            Log.d("DATA","POST!");
            Log.d("DATA",bundle.getString("high"));
            Log.d("DATA","POST2");

            Intent intent = new Intent(ChooseCity.this,MainActivity.class);
            intent.putExtras(bundle);
            requestcode = 0;
            setResult(requestcode,intent);
            finish();
        }
    }

}

GetDataContent.java文件:用来根据URL来获取对应的Json文件,并以String的格式返回
:

package com.example.administrator.myapplication_weather;

import android.util.Log;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

 public class GetCityJsonData {

    private String url="";

    public GetCityJsonData()
    {

    }

    public String GetDataContent(String url)
    {
        String response = null;
        setUrl(url);
        Log.d("DATA","download the data...");

        try {
            URL Url = new URL(getUrl());
            HttpURLConnection conn = (HttpURLConnection) Url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(8000);
            conn.setReadTimeout(8000);

            InputStream in = new BufferedInputStream(conn.getInputStream());
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            StringBuffer sb = new StringBuffer();
            String str;
            while((str = reader.readLine())!=null)
            {
                sb.append(str);
            }
            response = sb.toString();
            Log.d("GetData",response);

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return response;
    }

    private String getUrl()
    {
        return url;
    }

    private void setUrl(String url)
    {
        this.url = url;
    }

}

对应的配置文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.administrator.myapplication_weather">
    <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=".ChooseCity"
            android:exported="true"/>
    </application>

</manifest>

到此我的极简天气App就结束了。

虽然简陋,但是我们学习了以下内容:

  • Android单线程模型
  • 两个Activity之间传递数据
  • 异步类AsyncTask
  • 数据结构Map
  • Json格式解析
  • 多级联动的滚轮

猜你喜欢

转载自blog.csdn.net/Dongfnag_HU/article/details/85061169