Android 天气APP(五)天气预报、生活指数的数据请求与渲染

上一篇:Android 天气APP(四)搭建MVP框架与使用

新版-------------------

  在上一篇文章中,我们通过网络框架请求和风的搜索城市API接口拿到了当前所定位的城市的城市ID,那么通过这个城市ID,我们可以去获取城市的天气状况了。

一、增加天气接口地址

首先我们看一下实时天气的接口。

在这里插入图片描述

  这里我们看到请求的地址和之前的搜索城市不同,所以我们需要在请求网络的时候修改不同的API地址头,那么首先我们在library包下的ApiType枚举类中增加一个API类型,代码如下:

public enum ApiType {
    SEARCH,     //和风 城市搜索
    WEATHER,    //和风 天气接口
}

这里增加了一个天气接口,下面我们修改一下NetworkApi类中的getBaseUrl(),代码如下:

	private static void getBaseUrl(ApiType apiType) {
        switch (apiType) {
            case SEARCH:
                mBaseUrl = "https://geoapi.qweather.com";//和风天气搜索城市
                break;
            case WEATHER:
                mBaseUrl = "https://devapi.qweather.com";//和风天气API
            default:
                break;
        }
    }

  这里我增加了一个case,用于判断接口传进来的Api类型,从而设置不同的地址头,因为每一个接口都需要有成功和失败的请求回调,那么我们修改一下BaseViewModel中的failed变量的作用域为public,代码如下:

public class BaseViewModel extends ViewModel {
    public MutableLiveData<String> failed = new MutableLiveData<>();
}

之前是protected。

二、增加API接口

  下面我们需要增加一个实时天气的API接口,那么在增加接口之前首先要知道这个接口请求之后返回什么数据,可以看文档中的这里。

在这里插入图片描述

根据这里返回的示例JSON数据,我们可以手写一个数据实体类,在app模块的bean包下新建NowResponse类,代码如下所示:

public class NowResponse {

    private String code;
    private String updateTime;
    private String fxLink;
    private NowBean now;
    private ReferBean refer;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(String updateTime) {
        this.updateTime = updateTime;
    }

    public String getFxLink() {
        return fxLink;
    }

    public void setFxLink(String fxLink) {
        this.fxLink = fxLink;
    }

    public NowBean getNow() {
        return now;
    }

    public void setNow(NowBean now) {
        this.now = now;
    }

    public ReferBean getRefer() {
        return refer;
    }

    public void setRefer(ReferBean refer) {
        this.refer = refer;
    }

    public static class NowBean {

        private String obsTime;
        private String temp;
        private String feelsLike;
        private String icon;
        private String text;
        private String wind360;
        private String windDir;
        private String windScale;
        private String windSpeed;
        private String humidity;
        private String precip;
        private String pressure;
        private String vis;
        private String cloud;
        private String dew;

        public String getObsTime() {
            return obsTime;
        }

        public void setObsTime(String obsTime) {
            this.obsTime = obsTime;
        }

        public String getTemp() {
            return temp;
        }

        public void setTemp(String temp) {
            this.temp = temp;
        }

        public String getFeelsLike() {
            return feelsLike;
        }

        public void setFeelsLike(String feelsLike) {
            this.feelsLike = feelsLike;
        }

        public String getIcon() {
            return icon;
        }

        public void setIcon(String icon) {
            this.icon = icon;
        }

        public String getText() {
            return text;
        }

        public void setText(String text) {
            this.text = text;
        }

        public String getWind360() {
            return wind360;
        }

        public void setWind360(String wind360) {
            this.wind360 = wind360;
        }

        public String getWindDir() {
            return windDir;
        }

        public void setWindDir(String windDir) {
            this.windDir = windDir;
        }

        public String getWindScale() {
            return windScale;
        }

        public void setWindScale(String windScale) {
            this.windScale = windScale;
        }

        public String getWindSpeed() {
            return windSpeed;
        }

        public void setWindSpeed(String windSpeed) {
            this.windSpeed = windSpeed;
        }

        public String getHumidity() {
            return humidity;
        }

        public void setHumidity(String humidity) {
            this.humidity = humidity;
        }

        public String getPrecip() {
            return precip;
        }

        public void setPrecip(String precip) {
            this.precip = precip;
        }

        public String getPressure() {
            return pressure;
        }

        public void setPressure(String pressure) {
            this.pressure = pressure;
        }

        public String getVis() {
            return vis;
        }

        public void setVis(String vis) {
            this.vis = vis;
        }

        public String getCloud() {
            return cloud;
        }

        public void setCloud(String cloud) {
            this.cloud = cloud;
        }

        public String getDew() {
            return dew;
        }

        public void setDew(String dew) {
            this.dew = dew;
        }
    }


    public static class ReferBean {
        private List<String> sources;
        private List<String> license;

        public List<String> getSources() {
            return sources;
        }

        public void setSources(List<String> sources) {
            this.sources = sources;
        }

        public List<String> getLicense() {
            return license;
        }

        public void setLicense(List<String> license) {
            this.license = license;
        }
    }
}

有了实体类,我们就可以去ApiService中增加接口代码了,代码如下所示:

	@GET("/v7/weather/now?key=" + API_KEY)
    Observable<NowResponse> nowWeather(@Query("location") String location);

添加位置如下图所示:

在这里插入图片描述

三、天气数据存储库

  下面我们应该写一个方法调用这个接口去请求网络,还记得上一篇文章中写到的搜索城市存储类SearchCityRepository吗?那么相应的我们可以在repository包下新增一个WeatherRepository类,里面可以写上所有和天气相关的接口请求方法,代码如下:

@SuppressLint("CheckResult")
public class WeatherRepository {

    private static final String TAG = WeatherRepository.class.getSimpleName();

    /**
     * 实况天气
     *
     * @param responseLiveData 成功数据
     * @param failed           错误信息
     * @param cityId           城市ID
     */
    public void nowWeather(MutableLiveData<NowResponse> responseLiveData,
                           MutableLiveData<String> failed, String cityId) {
        String type = "实时天气-->";
        NetworkApi.createService(ApiService.class, ApiType.WEATHER).nowWeather(cityId)
                .compose(NetworkApi.applySchedulers(new BaseObserver<>() {
                    @Override
                    public void onSuccess(NowResponse nowResponse) {
                        if (nowResponse == null) {
                            failed.postValue("实况天气数据为null,请检查城市ID是否正确。");
                            return;
                        }
                        //请求接口成功返回数据,失败返回状态码
                        if (Constant.SUCCESS.equals(nowResponse.getCode())) {
                            responseLiveData.postValue(nowResponse);
                        } else {
                            failed.postValue(type + nowResponse.getCode());
                        }
                    }

                    @Override
                    public void onFailure(Throwable e) {
                        Log.e(TAG, "onFailure: " + e.getMessage());
                        failed.postValue(type + e.getMessage());
                    }
                }));
    }
}

  这里的代码我相信你可以看得懂,就是请求接口拿到返回的数据再通过LiveData传递出去,这里我在请求失败的时候加了一个type,这样我们就可以很清楚的知道是那个接口有问题,那么同样需要修改一下SearchCityRepository类中searchCity()方法,这个就自己去修改吧,依葫芦画瓢就行。

  下面我们就需要在ViewModel中去调用刚才所写的nowWeather()方法,因为都是在MainActivity中请求数据,那么理应将代码写在MainViewModel中,在MainViewModel中增加如下代码:

	public MutableLiveData<NowResponse> nowResponseMutableLiveData = new MutableLiveData<>();

	public void nowWeather(String cityId) {
        new WeatherRepository().nowWeather(nowResponseMutableLiveData,failed, cityId);
    }

现在一切准备就绪了,下面我们应该做什么了?应该请求数据然后显示在页面上了。

四、获取实时天气数据

而要显示在页面上,我们首先需要修改一下activity_main.xml中的内容,毕竟它里面当前只有一个TextView,这可还不够啊,修改代码如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/main_bg"
    tools:context=".MainActivity">

    <!--顶部标题-->
    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/materialToolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="城市天气"
            android:textColor="@color/white"
            android:textSize="@dimen/sp_16" />
    </com.google.android.material.appbar.MaterialToolbar>
    <!--天气状况-->
    <TextView
        android:id="@+id/tv_info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/dp_16"
        android:layout_marginTop="@dimen/dp_8"
        android:text="天气状况"
        android:textColor="@color/white"
        android:textSize="@dimen/sp_18"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/materialToolbar" />
    <!--温度-->
    <TextView
        android:id="@+id/tv_temp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dp_24"
        android:text="0"
        android:textColor="@color/white"
        android:textSize="@dimen/sp_60"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_info" />
    <!--摄氏度符号-->
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""
        android:textColor="@color/white"
        android:textSize="@dimen/sp_24"
        app:layout_constraintStart_toEndOf="@+id/tv_temp"
        app:layout_constraintTop_toTopOf="@+id/tv_temp" />
    <!--城市-->
    <TextView
        android:id="@+id/tv_city"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dp_32"
        android:text="城市"
        android:textColor="@color/white"
        android:textSize="@dimen/sp_20"
        app:layout_constraintEnd_toEndOf="@+id/tv_temp"
        app:layout_constraintStart_toStartOf="@+id/tv_temp"
        app:layout_constraintTop_toBottomOf="@+id/tv_temp" />
    <!--上一次更新时间-->
    <TextView
        android:id="@+id/tv_update_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dp_8"
        android:text="上次更新时间:"
        android:textColor="@color/white"
        android:textSize="@dimen/sp_12"
        app:layout_constraintEnd_toEndOf="@+id/tv_city"
        app:layout_constraintStart_toStartOf="@+id/tv_city"
        app:layout_constraintTop_toBottomOf="@+id/tv_city" />

</androidx.constraintlayout.widget.ConstraintLayout>

  这里面可能你会报错,因为你少了两个内容,一个是背景图main_bg,另一个是资源文件,里面我放置了一些文字和尺寸的值,在values下新建一个dimens.xml文件,里面代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--字体大小-->
    <dimen name="sp_10">10sp</dimen>
    <dimen name="sp_12">12sp</dimen>
    <dimen name="sp_14">14sp</dimen>
    <dimen name="sp_16">16sp</dimen>
    <dimen name="sp_18">18sp</dimen>
    <dimen name="sp_20">20sp</dimen>
    <dimen name="sp_24">24sp</dimen>
    <dimen name="sp_60">60sp</dimen>

    <!--尺寸大小-->
    <dimen name="dp_1">1dp</dimen>
    <dimen name="dp_2">2dp</dimen>
    <dimen name="dp_4">4dp</dimen>
    <dimen name="dp_6">6dp</dimen>
    <dimen name="dp_8">8dp</dimen>
    <dimen name="dp_12">12dp</dimen>
    <dimen name="dp_16">16dp</dimen>
    <dimen name="dp_24">24dp</dimen>
    <dimen name="dp_32">32dp</dimen>

</resources>

  现在你应该只有那个背景图报错了,你可以自己弄一个进去,也可以在我的源码中拿,就放在drawable-nodpi文件夹下,你需要创建这个文件夹,如下图所示:

在这里插入图片描述

现在假设你的xml已经不报错了,我们看一下预览效果图。

在这里插入图片描述

  相比于之前我使用了很多布局嵌套,现在通过ConstraintLayout进行布局,就显得代码量很少简洁,所以如果你还不会使用ConstraintLayout,一定要去学习一下。

好了,下面我们在MainActivity中使用实时天气请求数据,修改一下onObserveData()方法中的代码,如下所示:

	@Override
    protected void onObserveData() {
        if (viewModel != null) {
            //城市数据返回
            viewModel.searchCityResponseMutableLiveData.observe(this, searchCityResponse -> {
                List<SearchCityResponse.LocationBean> location = searchCityResponse.getLocation();
                if (location != null && location.size() > 0) {
                    String id = location.get(0).getId();
                    //获取到城市的ID
                    if (id != null) {
                        //通过城市ID查询城市实时天气
                        viewModel.nowWeather(id);
                    }
                }
            });
            //实况天气返回
            viewModel.nowResponseMutableLiveData.observe(this, nowResponse -> {
                NowResponse.NowBean now = nowResponse.getNow();
                if (now != null) {
                    binding.tvInfo.setText(now.getText());
                    binding.tvTemp.setText(now.getTemp());
                    binding.tvUpdateTime.setText("最近更新时间:" + nowResponse.getUpdateTime());
                }
            });
            //错误信息返回
            viewModel.failed.observe(this, this::showLongMsg);
        }
    }

新增的代码就是这三个地方

在这里插入图片描述

最后我们在onReceiveLocation()方法中去掉之前没有用到的代码,然后设置一下当前所处的城市,如下图所示:

在这里插入图片描述

下面运行一下:

在这里插入图片描述

OK,就是这样。

五、全屏沉浸式

  这里数据确实显示出来了,但是我们注意到屏幕的顶部和底部分别有一些突兀,看起来不是很协调,我们需要让它看起来协调,可以这么做,首先我们修改一下res下的values下的themes.xml和values-night下的theme.xml中的代码:

在这里插入图片描述

主要改动去掉默认的ActionBar,然后我们在BaseActivity中新增一个方法,用于设置全屏沉浸式,代码如下:

	protected void setFullScreenImmersion() {
        Window window = getWindow();
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        int option = window.getDecorView().getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
        window.getDecorView().setSystemUiVisibility(option);
        window.setStatusBarColor(Color.TRANSPARENT);
        window.setNavigationBarColor(Color.TRANSPARENT);
    }

然后在MainActivity的onCreate()方法中调用。

在这里插入图片描述

下面重新运行一下:

在这里插入图片描述

本篇文章就到这里。

六、文章源码

欢迎 StarFork

第五篇文章源码地址:GoodWeather-New-5

旧版-------------------

6. 天气预报

天气预报是预测未来几天的天气,常用列表显示,实现这个功能的业务逻辑是:访问API、获取返回值、列表配置、数据渲染。首先是API接口。

① 新增API接口

根据和风天气中的文档,得知未来3-7天的天气预报接口为:

https://free-api.heweather.net/s6/weather/forecast?key=3086e91d66c04ce588a7f538f917c7f4&location=福田区

在网页上访问得到返回值,生成一个实体
在这里插入图片描述
代码如下:

package com.llw.goodweather.bean;

import java.util.List;

public class WeatherForecastResponse {

    private List<HeWeather6Bean> HeWeather6;

    public List<HeWeather6Bean> getHeWeather6() {
        return HeWeather6;
    }

    public void setHeWeather6(List<HeWeather6Bean> HeWeather6) {
        this.HeWeather6 = HeWeather6;
    }

    public static class HeWeather6Bean {
        /**
         * basic : {"cid":"CN101280603","location":"福田","parent_city":"深圳","admin_area":"广东","cnty":"中国","lat":"22.5410099","lon":"114.05095673","tz":"+8.00"}
         * update : {"loc":"2019-11-19 19:57","utc":"2019-11-19 11:57"}
         * status : ok
         * daily_forecast : [{"cond_code_d":"100","cond_code_n":"101","cond_txt_d":"晴","cond_txt_n":"多云","date":"2019-11-19","hum":"50","mr":"23:52","ms":"12:27","pcpn":"0.0","pop":"20","pres":"1012","sr":"06:39","ss":"17:38","tmp_max":"22","tmp_min":"16","uv_index":"5","vis":"25","wind_deg":"31","wind_dir":"东北风","wind_sc":"3-4","wind_spd":"17"},{"cond_code_d":"101","cond_code_n":"101","cond_txt_d":"多云","cond_txt_n":"多云","date":"2019-11-20","hum":"67","mr":"00:00","ms":"13:14","pcpn":"0.0","pop":"3","pres":"1011","sr":"06:40","ss":"17:38","tmp_max":"24","tmp_min":"16","uv_index":"4","vis":"25","wind_deg":"-1","wind_dir":"无持续风向","wind_sc":"1-2","wind_spd":"5"},{"cond_code_d":"101","cond_code_n":"101","cond_txt_d":"多云","cond_txt_n":"多云","date":"2019-11-21","hum":"73","mr":"00:54","ms":"13:57","pcpn":"0.0","pop":"2","pres":"1009","sr":"06:40","ss":"17:38","tmp_max":"26","tmp_min":"19","uv_index":"3","vis":"25","wind_deg":"-1","wind_dir":"无持续风向","wind_sc":"1-2","wind_spd":"2"}]
         */

        private BasicBean basic;
        private UpdateBean update;
        private String status;
        private List<DailyForecastBean> daily_forecast;

        public BasicBean getBasic() {
            return basic;
        }

        public void setBasic(BasicBean basic) {
            this.basic = basic;
        }

        public UpdateBean getUpdate() {
            return update;
        }

        public void setUpdate(UpdateBean update) {
            this.update = update;
        }

        public String getStatus() {
            return status;
        }

        public void setStatus(String status) {
            this.status = status;
        }

        public List<DailyForecastBean> getDaily_forecast() {
            return daily_forecast;
        }

        public void setDaily_forecast(List<DailyForecastBean> daily_forecast) {
            this.daily_forecast = daily_forecast;
        }

        public static class BasicBean {
            /**
             * cid : CN101280603
             * location : 福田
             * parent_city : 深圳
             * admin_area : 广东
             * cnty : 中国
             * lat : 22.5410099
             * lon : 114.05095673
             * tz : +8.00
             */

            private String cid;
            private String location;
            private String parent_city;
            private String admin_area;
            private String cnty;
            private String lat;
            private String lon;
            private String tz;

            public String getCid() {
                return cid;
            }

            public void setCid(String cid) {
                this.cid = cid;
            }

            public String getLocation() {
                return location;
            }

            public void setLocation(String location) {
                this.location = location;
            }

            public String getParent_city() {
                return parent_city;
            }

            public void setParent_city(String parent_city) {
                this.parent_city = parent_city;
            }

            public String getAdmin_area() {
                return admin_area;
            }

            public void setAdmin_area(String admin_area) {
                this.admin_area = admin_area;
            }

            public String getCnty() {
                return cnty;
            }

            public void setCnty(String cnty) {
                this.cnty = cnty;
            }

            public String getLat() {
                return lat;
            }

            public void setLat(String lat) {
                this.lat = lat;
            }

            public String getLon() {
                return lon;
            }

            public void setLon(String lon) {
                this.lon = lon;
            }

            public String getTz() {
                return tz;
            }

            public void setTz(String tz) {
                this.tz = tz;
            }
        }

        public static class UpdateBean {
            /**
             * loc : 2019-11-19 19:57
             * utc : 2019-11-19 11:57
             */

            private String loc;
            private String utc;

            public String getLoc() {
                return loc;
            }

            public void setLoc(String loc) {
                this.loc = loc;
            }

            public String getUtc() {
                return utc;
            }

            public void setUtc(String utc) {
                this.utc = utc;
            }
        }

        public static class DailyForecastBean {
            /**
             * cond_code_d : 100
             * cond_code_n : 101
             * cond_txt_d : 晴
             * cond_txt_n : 多云
             * date : 2019-11-19
             * hum : 50
             * mr : 23:52
             * ms : 12:27
             * pcpn : 0.0
             * pop : 20
             * pres : 1012
             * sr : 06:39
             * ss : 17:38
             * tmp_max : 22
             * tmp_min : 16
             * uv_index : 5
             * vis : 25
             * wind_deg : 31
             * wind_dir : 东北风
             * wind_sc : 3-4
             * wind_spd : 17
             */

            private String cond_code_d;
            private String cond_code_n;
            private String cond_txt_d;
            private String cond_txt_n;
            private String date;
            private String hum;
            private String mr;
            private String ms;
            private String pcpn;
            private String pop;
            private String pres;
            private String sr;
            private String ss;
            private String tmp_max;
            private String tmp_min;
            private String uv_index;
            private String vis;
            private String wind_deg;
            private String wind_dir;
            private String wind_sc;
            private String wind_spd;

            public String getCond_code_d() {
                return cond_code_d;
            }

            public void setCond_code_d(String cond_code_d) {
                this.cond_code_d = cond_code_d;
            }

            public String getCond_code_n() {
                return cond_code_n;
            }

            public void setCond_code_n(String cond_code_n) {
                this.cond_code_n = cond_code_n;
            }

            public String getCond_txt_d() {
                return cond_txt_d;
            }

            public void setCond_txt_d(String cond_txt_d) {
                this.cond_txt_d = cond_txt_d;
            }

            public String getCond_txt_n() {
                return cond_txt_n;
            }

            public void setCond_txt_n(String cond_txt_n) {
                this.cond_txt_n = cond_txt_n;
            }

            public String getDate() {
                return date;
            }

            public void setDate(String date) {
                this.date = date;
            }

            public String getHum() {
                return hum;
            }

            public void setHum(String hum) {
                this.hum = hum;
            }

            public String getMr() {
                return mr;
            }

            public void setMr(String mr) {
                this.mr = mr;
            }

            public String getMs() {
                return ms;
            }

            public void setMs(String ms) {
                this.ms = ms;
            }

            public String getPcpn() {
                return pcpn;
            }

            public void setPcpn(String pcpn) {
                this.pcpn = pcpn;
            }

            public String getPop() {
                return pop;
            }

            public void setPop(String pop) {
                this.pop = pop;
            }

            public String getPres() {
                return pres;
            }

            public void setPres(String pres) {
                this.pres = pres;
            }

            public String getSr() {
                return sr;
            }

            public void setSr(String sr) {
                this.sr = sr;
            }

            public String getSs() {
                return ss;
            }

            public void setSs(String ss) {
                this.ss = ss;
            }

            public String getTmp_max() {
                return tmp_max;
            }

            public void setTmp_max(String tmp_max) {
                this.tmp_max = tmp_max;
            }

            public String getTmp_min() {
                return tmp_min;
            }

            public void setTmp_min(String tmp_min) {
                this.tmp_min = tmp_min;
            }

            public String getUv_index() {
                return uv_index;
            }

            public void setUv_index(String uv_index) {
                this.uv_index = uv_index;
            }

            public String getVis() {
                return vis;
            }

            public void setVis(String vis) {
                this.vis = vis;
            }

            public String getWind_deg() {
                return wind_deg;
            }

            public void setWind_deg(String wind_deg) {
                this.wind_deg = wind_deg;
            }

            public String getWind_dir() {
                return wind_dir;
            }

            public void setWind_dir(String wind_dir) {
                this.wind_dir = wind_dir;
            }

            public String getWind_sc() {
                return wind_sc;
            }

            public void setWind_sc(String wind_sc) {
                this.wind_sc = wind_sc;
            }

            public String getWind_spd() {
                return wind_spd;
            }

            public void setWind_spd(String wind_spd) {
                this.wind_spd = wind_spd;
            }
        }
    }
}

接下来在ApiService中添加

在这里插入图片描述
代码如下:

	/**
     * 未来3 - 7天天气预报
     */
    @GET("/s6/weather/forecast?key=3086e91d66c04ce588a7f538f917c7f4")
    Call<WeatherForecastResponse> getWeatherForecast(@Query("location") String location);

② 修改订阅器

接下来修改订阅器WeatherContract

在这里插入图片描述
在这里插入图片描述
WeatherContract代码如下:

package com.llw.goodweather.contract;

import android.content.Context;

import com.llw.goodweather.api.ApiService;
import com.llw.goodweather.bean.TodayResponse;
import com.llw.goodweather.bean.WeatherForecastResponse;
import com.llw.mvplibrary.base.BasePresenter;
import com.llw.mvplibrary.base.BaseView;
import com.llw.mvplibrary.net.NetCallBack;
import com.llw.mvplibrary.net.ServiceGenerator;
import retrofit2.Call;
import retrofit2.Response;

/**
 * 天气订阅器
 */
public class WeatherContract {

    public static class WeatherPresenter extends BasePresenter<IWeatherView> {
        /**
         * 当日天气
         * @param context
         * @param location  区/县
         */
        public void todayWeather(final Context context, String location) {
            //得到构建之后的网络请求服务,这里的地址已经拼接完成,只差一个location了
            ApiService service = ServiceGenerator.createService(ApiService.class);
            //设置请求回调  NetCallBack是重写请求回调
            service.getTodayWeather(location).enqueue(new NetCallBack<TodayResponse>() {
                //成功回调
                @Override
                public void onSuccess(Call<TodayResponse> call, Response<TodayResponse> response) {
                    if (getView() != null) {//当视图不会空时返回请求数据
                        getView().getTodayWeatherResult(response);
                    }
                }

                //失败回调
                @Override
                public void onFailed() {
                    if (getView() != null) {//当视图不会空时获取错误信息
                        getView().getDataFailed();
                    }
                }
            });
        }


        /**
         * 天气预报  3-7天(白嫖的就只能看到3天)
         * @param context
         * @param location
         */
        public void weatherForecast(final Context context,String location){
            ApiService service = ServiceGenerator.createService(ApiService.class);
            service.getWeatherForecast(location).enqueue(new NetCallBack<WeatherForecastResponse>() {
                @Override
                public void onSuccess(Call<WeatherForecastResponse> call, Response<WeatherForecastResponse> response) {
                    if(getView() != null){
                        getView().getWeatherForecastResult(response);
                    }
                }

                @Override
                public void onFailed() {
                    if(getView() != null){
                        getView().getDataFailed();
                    }
                }
            });
        }
    }


    public interface IWeatherView extends BaseView {
        //查询当天天气的数据返回
        void getTodayWeatherResult(Response<TodayResponse> response);
        //查询天气预报的数据返回
        void getWeatherForecastResult(Response<WeatherForecastResponse> response);
        //错误返回
        void getDataFailed();
    }
}

接下来修改布局,增加列表和适配器

③ 修改布局,增加列表和适配器

在这里插入图片描述
代码中
在这里插入图片描述
这个时候你的MainActivity.java会报错
在这里插入图片描述
这是因为订阅器里面的内容没有写入。鼠标点击,Alt + Enter
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
接下来创建列表的item
在layout目录下创建item_weather_forecast_list.xml文件

在这里插入图片描述
代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <LinearLayout
        android:padding="@dimen/sp_12"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <!--日期-->
        <TextView
            android:id="@+id/tv_date"
            android:text="1234"
            android:textSize="@dimen/sp_14"
            android:textColor="#FFF"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"/>
        <!--天气描述-->
        <TextView
            android:gravity="center"
            android:id="@+id/tv_info"
            android:textSize="@dimen/sp_14"
            android:textColor="#FFF"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"/>

        <!--最低温、最高温-->
        <TextView
            android:gravity="right"
            android:id="@+id/tv_low_and_height"
            android:textSize="@dimen/sp_14"
            android:textColor="#FFF"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"/>
    </LinearLayout>
</LinearLayout>

接下来创建一个适配器
com.llw.goodweather下新建一个WeatherForecastAdapter适配器
在这里插入图片描述
代码如下:

package com.llw.goodweather.adapter;

import androidx.annotation.Nullable;

import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.llw.goodweather.R;
import com.llw.goodweather.bean.WeatherForecastResponse;

import java.util.List;

/**
 * 天气预报列表展示适配器
 */
public class WeatherForecastAdapter extends BaseQuickAdapter<WeatherForecastResponse.HeWeather6Bean.DailyForecastBean, BaseViewHolder> {

    public WeatherForecastAdapter(int layoutResId, @Nullable List<WeatherForecastResponse.HeWeather6Bean.DailyForecastBean> data) {
        super(layoutResId, data);
    }

    @Override
    protected void convert(BaseViewHolder helper, WeatherForecastResponse.HeWeather6Bean.DailyForecastBean item) {
        helper.setText(R.id.tv_date, item.getDate())//日期
                .setText(R.id.tv_info, item.getCond_txt_d())//天气
                .setText(R.id.tv_low_and_height, item.getTmp_min() + "/" + item.getTmp_max() + "℃");//最低温和最高温
    }
}

④ 使用适配器进行数据展示

在MainActivity.java中增加


	List<WeatherForecastResponse.HeWeather6Bean.DailyForecastBean> mList;//初始化数据源
    WeatherForecastAdapter mAdapter;//初始化适配器
	/**
     * 初始化天气预报数据列表
     */
    private void initList() {
        mList = new ArrayList<>();//声明为ArrayList
        mAdapter = new WeatherForecastAdapter(R.layout.item_weather_forecast_list, mList);//为适配器设置布局和数据源
        LinearLayoutManager manager = new LinearLayoutManager(context);//布局管理,默认是纵向
        rv.setLayoutManager(manager);//为列表配置管理器
        rv.setAdapter(mAdapter);//为列表配置适配器
    }

然后在**initData()**方法中调用
在这里插入图片描述
在这里插入图片描述

返回值做处理

	//查询天气预报,请求成功后的数据返回
    @Override
    public void getWeatherForecastResult(Response<WeatherForecastResponse> response) {
        if (("ok").equals(response.body().getHeWeather6().get(0).getStatus())) {
            //最低温和最高温
            tvLowHeight.setText(response.body().getHeWeather6().get(0).getDaily_forecast().get(0).getTmp_min() + " / " +
                    response.body().getHeWeather6().get(0).getDaily_forecast().get(0).getTmp_max() + "℃");

            if (response.body().getHeWeather6().get(0).getDaily_forecast() != null) {
                List<WeatherForecastResponse.HeWeather6Bean.DailyForecastBean> data
                        = response.body().getHeWeather6().get(0).getDaily_forecast();
                mList.clear();//添加数据之前先清除
                mList.addAll(data);//添加数据
                mAdapter.notifyDataSetChanged();//刷新列表
            } else {
                ToastUtils.showShortToast(context, "天气预报数据为空");
            }
        } else {
            ToastUtils.showShortToast(context, response.body().getHeWeather6().get(0).getStatus());
        }
    }

运行
在这里插入图片描述
这样天气预报这个功能就完成了。
接下来是生活指数。

7. 生活指数

生活指数就是一些生活建议,实现的不走其实和天气预报差不太多,但是比天气预报要简单一些,因为不需要列表显示,文本即可。

① 新增API接口

根据和风天气中的文档,得知生活指数接口为:

https://free-api.heweather.net/s6/weather/lifestyle?key=3086e91d66c04ce588a7f538f917c7f4&location=福田区

在网页上访问得到返回值,生成一个实体
在这里插入图片描述
代码如下:

package com.llw.goodweather.bean;

import java.util.List;

public class LifeStyleResponse {

    private List<HeWeather6Bean> HeWeather6;

    public List<HeWeather6Bean> getHeWeather6() {
        return HeWeather6;
    }

    public void setHeWeather6(List<HeWeather6Bean> HeWeather6) {
        this.HeWeather6 = HeWeather6;
    }

    public static class HeWeather6Bean {
        /**
         * basic : {"cid":"CN101280603","location":"福田","parent_city":"深圳","admin_area":"广东","cnty":"中国","lat":"22.5410099","lon":"114.05095673","tz":"+8.00"}
         * update : {"loc":"2019-11-23 09:55","utc":"2019-11-23 01:55"}
         * status : ok
         * lifestyle : [{"type":"comf","brf":"舒适","txt":"白天不太热也不太冷,风力不大,相信您在这样的天气条件下,应会感到比较清爽和舒适。"},{"type":"drsg","brf":"热","txt":"天气热,建议着短裙、短裤、短薄外套、T恤等夏季服装。"},{"type":"flu","brf":"少发","txt":"各项气象条件适宜,无明显降温过程,发生感冒机率较低。"},{"type":"sport","brf":"适宜","txt":"天气较好,赶快投身大自然参与户外运动,尽情感受运动的快乐吧。"},{"type":"trav","brf":"适宜","txt":"天气较好,温度适宜,是个好天气哦。这样的天气适宜旅游,您可以尽情地享受大自然的风光。"},{"type":"uv","brf":"强","txt":"紫外线辐射强,建议涂擦SPF20左右、PA++的防晒护肤品。避免在10点至14点暴露于日光下。"},{"type":"cw","brf":"适宜","txt":"适宜洗车,未来持续两天无雨天气较好,适合擦洗汽车,蓝天白云、风和日丽将伴您的车子连日洁净。"},{"type":"air","brf":"中","txt":"气象条件对空气污染物稀释、扩散和清除无明显影响。"}]
         */

        private BasicBean basic;
        private UpdateBean update;
        private String status;
        private List<LifestyleBean> lifestyle;

        public BasicBean getBasic() {
            return basic;
        }

        public void setBasic(BasicBean basic) {
            this.basic = basic;
        }

        public UpdateBean getUpdate() {
            return update;
        }

        public void setUpdate(UpdateBean update) {
            this.update = update;
        }

        public String getStatus() {
            return status;
        }

        public void setStatus(String status) {
            this.status = status;
        }

        public List<LifestyleBean> getLifestyle() {
            return lifestyle;
        }

        public void setLifestyle(List<LifestyleBean> lifestyle) {
            this.lifestyle = lifestyle;
        }

        public static class BasicBean {
            /**
             * cid : CN101280603
             * location : 福田
             * parent_city : 深圳
             * admin_area : 广东
             * cnty : 中国
             * lat : 22.5410099
             * lon : 114.05095673
             * tz : +8.00
             */

            private String cid;
            private String location;
            private String parent_city;
            private String admin_area;
            private String cnty;
            private String lat;
            private String lon;
            private String tz;

            public String getCid() {
                return cid;
            }

            public void setCid(String cid) {
                this.cid = cid;
            }

            public String getLocation() {
                return location;
            }

            public void setLocation(String location) {
                this.location = location;
            }

            public String getParent_city() {
                return parent_city;
            }

            public void setParent_city(String parent_city) {
                this.parent_city = parent_city;
            }

            public String getAdmin_area() {
                return admin_area;
            }

            public void setAdmin_area(String admin_area) {
                this.admin_area = admin_area;
            }

            public String getCnty() {
                return cnty;
            }

            public void setCnty(String cnty) {
                this.cnty = cnty;
            }

            public String getLat() {
                return lat;
            }

            public void setLat(String lat) {
                this.lat = lat;
            }

            public String getLon() {
                return lon;
            }

            public void setLon(String lon) {
                this.lon = lon;
            }

            public String getTz() {
                return tz;
            }

            public void setTz(String tz) {
                this.tz = tz;
            }
        }

        public static class UpdateBean {
            /**
             * loc : 2019-11-23 09:55
             * utc : 2019-11-23 01:55
             */

            private String loc;
            private String utc;

            public String getLoc() {
                return loc;
            }

            public void setLoc(String loc) {
                this.loc = loc;
            }

            public String getUtc() {
                return utc;
            }

            public void setUtc(String utc) {
                this.utc = utc;
            }
        }

        public static class LifestyleBean {
            /**
             * type : comf
             * brf : 舒适
             * txt : 白天不太热也不太冷,风力不大,相信您在这样的天气条件下,应会感到比较清爽和舒适。
             */

            private String type;
            private String brf;
            private String txt;

            public String getType() {
                return type;
            }

            public void setType(String type) {
                this.type = type;
            }

            public String getBrf() {
                return brf;
            }

            public void setBrf(String brf) {
                this.brf = brf;
            }

            public String getTxt() {
                return txt;
            }

            public void setTxt(String txt) {
                this.txt = txt;
            }
        }
    }
}

在ApiService中增加
在这里插入图片描述
代码如下:

	/**
     * 生活指数
     */
    @GET("/s6/weather/lifestyle?key=3086e91d66c04ce588a7f538f917c7f4")
    Call<LifeStyleResponse> getLifestyle(@Query("location") String location);

记得将key的值修改为自己的Key

② 修改订阅器

WeatherContract新增生活指数订阅
在这里插入图片描述
在这里插入图片描述

		/**
         * 生活指数
         * @param context
         * @param location
         */
        public void lifeStyle(final Context context,String location){
            ApiService service = ServiceGenerator.createService(ApiService.class);
            service.getLifestyle(location).enqueue(new NetCallBack<LifeStyleResponse>() {
                @Override
                public void onSuccess(Call<LifeStyleResponse> call, Response<LifeStyleResponse> response) {
                    if(getView() != null){
                        getView().getLifeStyleResult(response);
                    }
                }

                @Override
                public void onFailed() {
                    if(getView() != null){
                        getView().getDataFailed();
                    }
                }
            });
        }
		//查询生活指数的数据返回
        void getLifeStyleResult(Response<LifeStyleResponse> response);

③ 修改布局

这次要展示的数据会比较多,所以布局的整体要用NestedScrollView包裹起来,变成一个·可以上下滑动的布局,布局修改后的代码如下(PS:为了不出现问题,这里我贴上全部的布局代码):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:gravity="center"
    android:fitsSystemWindows="true"
    android:background="@drawable/pic_bg"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <!--相对布局-->
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <!--透明度为0.3的黑色背景-->
        <LinearLayout
            android:background="#000"
            android:alpha="0.3"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
        <!--主要的布局文件-->
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <!--标题 沉浸式-->
            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:contentInsetLeft="16dp"
                app:popupTheme="@style/AppTheme.PopupOverlay">
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:textSize="16sp"
                    android:textColor="#FFF"
                    android:text="城市天气" />
            </androidx.appcompat.widget.Toolbar>

            <!--NestedScrollView 里面只能包裹一个大的布局,
            当这个布局长度超出手机展示的部分就可以滚动,其中overScrollMode="never"
            的意思是隐藏掉滚动条到顶部和底部时的水波纹-->
            <androidx.core.widget.NestedScrollView
                android:overScrollMode="never"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
                <!--天气和所在城市 -->
                <LinearLayout
                    android:gravity="center_horizontal"
                    android:orientation="vertical"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">
                    <!--天气状况-->
                    <TextView
                        android:paddingLeft="16dp"
                        android:paddingTop="12dp"
                        android:id="@+id/tv_info"
                        android:textColor="#FFF"
                        android:textSize="18sp"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"/>
                    <!--温度-->
                    <LinearLayout
                        android:gravity="top|center_horizontal"
                        android:layout_marginTop="20dp"
                        android:orientation="horizontal"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content">
                        <TextView
                            android:id="@+id/tv_temperature"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:text="0"
                            android:textColor="#FFF"
                            android:textSize="60sp" />
                        <TextView
                            android:layout_width="wrap_content"
                            android:layout_height="match_parent"
                            android:text="℃"
                            android:textColor="#FFF"
                            android:textSize="24sp" />
                    </LinearLayout>

                    <!--最高温和最低温-->
                    <TextView
                        android:layout_marginTop="12dp"
                        android:id="@+id/tv_low_height"
                        android:textColor="#FFF"
                        android:textSize="@dimen/sp_14"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"/>
                    <!--城市-->
                    <TextView
                        android:layout_marginTop="20dp"
                        android:id="@+id/tv_city"
                        android:textColor="#FFF"
                        android:text="城市"
                        android:textSize="20sp"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"/>
                    <!--上一次更新时间-->
                    <TextView
                        android:layout_marginTop="8dp"
                        android:id="@+id/tv_old_time"
                        android:textColor="#FFF"
                        android:text="上次更新时间:"
                        android:textSize="@dimen/sp_12"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"/>

                    <!--用于显示天气预报数据-->
                    <androidx.recyclerview.widget.RecyclerView
                        android:layout_marginTop="20dp"
                        android:id="@+id/rv"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"/>

                    <!--用于展示生活指数的布局-->
                    <LinearLayout
                        android:orientation="vertical"
                        android:padding="20dp"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content">
                        <!--标题-->
                        <TextView
                            android:textSize="18sp"
                            android:textColor="#FFF"
                            android:text="生活建议"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"/>
                        <!--舒适度-->
                        <TextView
                            android:layout_marginTop="16dp"
                            android:id="@+id/tv_comf"
                            android:text="舒适度:"
                            android:textSize="@dimen/sp_14"
                            android:textColor="#FFF"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"/>
                        <!--旅游指数-->
                        <TextView
                            android:layout_marginTop="16dp"
                            android:id="@+id/tv_trav"
                            android:text="旅游指数:"
                            android:textSize="@dimen/sp_14"
                            android:textColor="#FFF"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"/>
                        <!--运动指数-->
                        <TextView
                            android:layout_marginTop="16dp"
                            android:id="@+id/tv_sport"
                            android:text="运动指数:"
                            android:textSize="@dimen/sp_14"
                            android:textColor="#FFF"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"/>
                        <!--洗车指数-->
                        <TextView
                            android:layout_marginTop="16dp"
                            android:id="@+id/tv_cw"
                            android:text="洗车指数:"
                            android:textSize="@dimen/sp_14"
                            android:textColor="#FFF"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"/>
                        <!--空气指数-->
                        <TextView
                            android:layout_marginTop="16dp"
                            android:id="@+id/tv_air"
                            android:text="空气指数:"
                            android:textSize="@dimen/sp_14"
                            android:textColor="#FFF"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"/>
                        <!--穿衣指数-->
                        <TextView
                            android:layout_marginTop="16dp"
                            android:id="@+id/tv_drsg"
                            android:text="穿衣指数:"
                            android:textSize="@dimen/sp_14"
                            android:textColor="#FFF"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"/>
                        <!--感冒指数-->
                        <TextView
                            android:layout_marginTop="16dp"
                            android:id="@+id/tv_flu"
                            android:text="感冒指数:"
                            android:textSize="@dimen/sp_14"
                            android:textColor="#FFF"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"/>
                    </LinearLayout>
                </LinearLayout>
            </androidx.core.widget.NestedScrollView>
        </LinearLayout>
    </RelativeLayout>
</LinearLayout>

注释已经在代码中写好了,相信你看了就明白了。接下来就是数据返回的处理,和页面数据渲染显示。

④ 数据渲染显示

在这里插入图片描述
由于返回的数据可能会为空,为了使返回数据为空的时候程序不报错,这里要做判断,在模块的utils包下写一个工具类。
在这里插入图片描述
工具类代码如下:

package com.llw.mvplibrary.utils;

import android.os.Build;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;

import androidx.annotation.RequiresApi;
import androidx.collection.LongSparseArray;
import androidx.collection.SimpleArrayMap;

import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Map;

/**
 * 空判断工具类
 */
public final class ObjectUtils {

    private ObjectUtils() {
        throw new UnsupportedOperationException("u can't instantiate me...");
    }

    /**
     * Return whether object is empty.
     *
     * @param obj The object.
     * @return {@code true}: yes<br>{@code false}: no
     */
    public static boolean isEmpty(final Object obj) {
        if (obj == null) {
            return true;
        }
        if (obj.getClass().isArray() && Array.getLength(obj) == 0) {
            return true;
        }
        if (obj instanceof CharSequence && obj.toString().length() == 0) {
            return true;
        }
        if (obj instanceof Collection && ((Collection) obj).isEmpty()) {
            return true;
        }
        if (obj instanceof Map && ((Map) obj).isEmpty()) {
            return true;
        }
        if (obj instanceof SimpleArrayMap && ((SimpleArrayMap) obj).isEmpty()) {
            return true;
        }
        if (obj instanceof SparseArray && ((SparseArray) obj).size() == 0) {
            return true;
        }
        if (obj instanceof SparseBooleanArray && ((SparseBooleanArray) obj).size() == 0) {
            return true;
        }
        if (obj instanceof SparseIntArray && ((SparseIntArray) obj).size() == 0) {
            return true;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            if (obj instanceof SparseLongArray && ((SparseLongArray) obj).size() == 0) {
                return true;
            }
        }
        if (obj instanceof LongSparseArray && ((LongSparseArray) obj).size() == 0) {
            return true;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            if (obj instanceof android.util.LongSparseArray
                    && ((android.util.LongSparseArray) obj).size() == 0) {
                return true;
            }
        }
        return false;
    }

    public static boolean isEmpty(final CharSequence obj) {
        return obj == null || obj.toString().length() == 0;
    }

    public static boolean isEmpty(final Collection obj) {
        return obj == null || obj.isEmpty();
    }

    public static boolean isEmpty(final Map obj) {
        return obj == null || obj.isEmpty();
    }

    public static boolean isEmpty(final SimpleArrayMap obj) {
        return obj == null || obj.isEmpty();
    }

    public static boolean isEmpty(final SparseArray obj) {
        return obj == null || obj.size() == 0;
    }

    public static boolean isEmpty(final SparseBooleanArray obj) {
        return obj == null || obj.size() == 0;
    }

    public static boolean isEmpty(final SparseIntArray obj) {
        return obj == null || obj.size() == 0;
    }

    public static boolean isEmpty(final LongSparseArray obj) {
        return obj == null || obj.size() == 0;
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    public static boolean isEmpty(final SparseLongArray obj) {
        return obj == null || obj.size() == 0;
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    public static boolean isEmpty(final android.util.LongSparseArray obj) {
        return obj == null || obj.size() == 0;
    }

    /**
     * Return whether object is not empty.
     *
     * @param obj The object.
     * @return {@code true}: yes<br>{@code false}: no
     */
    public static boolean isNotEmpty(final Object obj) {
        return !isEmpty(obj);
    }

    public static boolean isNotEmpty(final CharSequence obj) {
        return !isEmpty(obj);
    }

    public static boolean isNotEmpty(final Collection obj) {
        return !isEmpty(obj);
    }

    public static boolean isNotEmpty(final Map obj) {
        return !isEmpty(obj);
    }

    public static boolean isNotEmpty(final SimpleArrayMap obj) {
        return !isEmpty(obj);
    }

    public static boolean isNotEmpty(final SparseArray obj) {
        return !isEmpty(obj);
    }

    public static boolean isNotEmpty(final SparseBooleanArray obj) {
        return !isEmpty(obj);
    }

    public static boolean isNotEmpty(final SparseIntArray obj) {
        return !isEmpty(obj);
    }

    public static boolean isNotEmpty(final LongSparseArray obj) {
        return !isEmpty(obj);
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
    public static boolean isNotEmpty(final SparseLongArray obj) {
        return !isEmpty(obj);
    }

    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    public static boolean isNotEmpty(final android.util.LongSparseArray obj) {
        return !isEmpty(obj);
    }

    /**
     * Return whether object1 is equals to object2.
     *
     * @param o1 The first object.
     * @param o2 The second object.
     * @return {@code true}: yes<br>{@code false}: no
     */
    public static boolean equals(final Object o1, final Object o2) {
        return o1 == o2 || (o1 != null && o1.equals(o2));
    }

    /**
     * Require the objects are not null.
     *
     * @param objects The object.
     * @throws NullPointerException if any object is null in objects
     */
    public static void requireNonNull(final Object... objects) {
        if (objects == null) throw new NullPointerException();
        for (Object object : objects) {
            if (object == null) throw new NullPointerException();
        }
    }

    /**
     * Return the nonnull object or default object.
     *
     * @param object        The object.
     * @param defaultObject The default object to use with the object is null.
     * @param <T>           The value type.
     * @return the nonnull object or default object
     */
    public static <T> T getOrDefault(final T object, final T defaultObject) {
        if (object == null) {
            return defaultObject;
        }
        return object;
    }

    /**
     * Return the hash code of object.
     *
     * @param o The object.
     * @return the hash code of object
     */
    public static int hashCode(final Object o) {
        return o != null ? o.hashCode() : 0;
    }
}

接下来调用方法请求生活指数
在这里插入图片描述

请求返回数据做处理:

	//查询生活指数,请求成功后的数据返回
    @Override
    public void getLifeStyleResult(Response<LifeStyleResponse> response) {
        if(("ok").equals(response.body().getHeWeather6().get(0).getStatus())){
            List<LifeStyleResponse.HeWeather6Bean.LifestyleBean> data = response.body().getHeWeather6().get(0).getLifestyle();
            if(!ObjectUtils.isEmpty(data)){
                for (int i = 0;i<data.size();i++){
                    if(("comf").equals(data.get(i).getType())){
                        tvComf.setText("舒适度:"+data.get(i).getTxt());
                    }else if(("drsg").equals(data.get(i).getType())){
                        tvDrsg.setText("穿衣指数:"+data.get(i).getTxt());
                    }else if(("flu").equals(data.get(i).getType())){
                        tvFlu.setText("感冒指数:"+data.get(i).getTxt());
                    }else if(("sport").equals(data.get(i).getType())){
                        tvSport.setText("运动指数:"+data.get(i).getTxt());
                    }else if(("trav").equals(data.get(i).getType())){
                        tvTrav.setText("旅游指数:"+data.get(i).getTxt());
                    }else if(("cw").equals(data.get(i).getType())){
                        tvCw.setText("洗车指数:"+data.get(i).getTxt());
                    }else if(("air").equals(data.get(i).getType())){
                        tvAir.setText("空气指数:"+data.get(i).getTxt());
                    }
                }

            }else {
                ToastUtils.showShortToast(context, "生活指数数据为空");
            }
        }else {
            ToastUtils.showShortToast(context, response.body().getHeWeather6().get(0).getStatus());
        }
    }

运行一下:
在这里插入图片描述
很明显数据显示不完全,然后向上滑动。
在这里插入图片描述

这样就完成了这个生活指数的数据显示。
但是感觉页面上好多字呀,这时候为了在视觉上舒缓,就要通过会动的东西来勾引,呸,吸引住你。比如风的数据显示,多少级的风,哪个方向,通过风车来增加页面的动。会动的风车喔。

源码地址:GoodWeather
欢迎 StarFork

下一篇:Android 天气APP(六)旋转风车显示风力、风向

  • 16
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 24
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

初学者-Study

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值