Android 天气APP(二十五)地图天气(下)嵌套滑动布局渲染天气数据

简介: Android 天气APP(二十五)地图天气(下)嵌套滑动布局渲染天气数据

上篇文章中,完成了对地图的控制,那么这篇文章中完成对天气数据的渲染。

完成这篇文章实现的效果如下,如果你觉得不行那就可以不用看下面了。


image.gif


一、嵌套布局


在上一篇文章中,实现地图的功能,只用了一个地图控件和一个浮动按钮。而在这一篇文章中,为了提高页面的可用性和用户交互的效果,我用了CoordinatorLayout,这是一个嵌套滑动布局。


接下来来看看详细的布局内容:


20200827163804488.png


然后来看这个里面放了什么


20200827164303176.png


这里我放了一个相对布局,这是用于控制滑动的布局 在页面的底部留有一部分布局,可以通过向上拖动到屏幕的底部,不过为了更好地效果,我在CoordinatorLayout中设置50的上边距。


然后再看这个相对布局里面是什么内容


20200827165612126.png


展开之后里面是NestedScrollView和LinearLayout,


BottomSheetBehavior上滑 展开后主要滑动布局 上滑时会展示里面所有内容,下滑时当没有多余内容时会响应上层BottomSheetBehavior, 从而达到,整体收缩的效果。

下面我贴上NestedScrollView的布局代码:


<androidx.core.widget.NestedScrollView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginTop="@dimen/dp_80">
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:orientation="vertical">
                    <!--渐变背景 -->
                    <View
                        android:layout_width="match_parent"
                        android:layout_height="@dimen/dp_80"
                        android:background="@drawable/shape_gradient_white" />
                    <!--主要内容-->
                    <LinearLayout
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:background="@color/white"
                        android:gravity="center_horizontal"
                        android:orientation="vertical">
                        <!--今日天气简单的文字描述-->
                        <TextView
                            android:id="@+id/tv_today_info"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:padding="@dimen/dp_12"
                            android:textColor="@color/black_4"
                            android:textSize="@dimen/sp_14" />
                        <!--分割线-->
                        <View
                            android:layout_width="match_parent"
                            android:layout_height="@dimen/dp_3"
                            android:background="@color/line" />
                        <!--今日详情-->
                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_margin="@dimen/dp_12"
                            android:text="今日详情"
                            android:textColor="@color/black_4"
                            android:textSize="@dimen/sp_18" />
                        <!--点分割线-->
                        <View
                            android:layout_width="match_parent"
                            android:layout_height="2dp"
                            android:layout_marginLeft="10dp"
                            android:layout_marginRight="10dp"
                            android:background="@drawable/shape_dash_line" />
                        <!--今日天气详情数据列表-->
                        <androidx.recyclerview.widget.RecyclerView
                            android:id="@+id/rv_today_detail"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginTop="@dimen/dp_12"
                            android:layout_marginBottom="@dimen/dp_12"
                            android:paddingLeft="@dimen/dp_12"
                            android:paddingRight="@dimen/dp_12" />
                        <!--点分割线-->
                        <View
                            android:layout_width="match_parent"
                            android:layout_height="2dp"
                            android:layout_marginLeft="10dp"
                            android:layout_marginRight="10dp"
                            android:background="@drawable/shape_dash_line" />
                        <!--未来预报-->
                        <TextView
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_margin="@dimen/dp_12"
                            android:text="未来预报"
                            android:textColor="@color/black_4"
                            android:textSize="@dimen/sp_18" />
                        <!--未来七天天气预报-->
                        <androidx.recyclerview.widget.RecyclerView
                            android:id="@+id/rv_seven_day_daily"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:paddingLeft="@dimen/dp_12"
                            android:paddingRight="@dimen/dp_12" />
                        <!--查看更多天气预报-->
                        <TextView
                            android:id="@+id/tv_more_daily"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:drawableRight="@mipmap/icon_more_gray"
                            android:gravity="center"
                            android:padding="@dimen/dp_12"
                            android:text="15日预报详情"
                            android:textColor="@color/gray_2" />
                        <!--分割线-->
                        <View
                            android:layout_width="match_parent"
                            android:layout_height="@dimen/dp_3"
                            android:background="@color/line" />
                        <!--未完待续。。。-->
                    </LinearLayout>
                </LinearLayout>
            </androidx.core.widget.NestedScrollView>


icon_more_gray.png


20200828094942928.png


然后对里面的一些资源数据做讲解,里面用到了一些样式资源文件,在写之前,先把所有颜色值贴出来。注意是在mvplibrary下的colors.xml中


20200827170516441.png


颜色代码如下:你可以复制粘贴进去


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="arc_bg_color">#C6D7F4</color>
    <color name="arc_progress_color">#FBFEF7</color>
    <color name="white">#ffffff</color><!--白色-->
    <color name="black">#000000</color><!--黑色-->
    <color name="black_2">#1E1E1A</color><!--黑色2-->
    <color name="green">#77d034</color><!--绿色-->
    <color name="blue_one">#9FC8E9</color><!--浅蓝色-->
    <color name="transparent">#00000000</color><!--透明-->
    <color name="transparent_bg">#22000000</color><!--黑色半透明-->
    <color name="transparent_bg_white">#22FFFFFF</color><!--白色半透明-->
    <color name="orange">#F38A50</color><!--橘色-->
    <color name="shallow_orange">#FFEFD5</color><!--浅橘色-->
    <color name="black_3">#454545</color><!--黑色3-->
    <color name="gray">#BABABA</color><!--灰色-->
    <color name="pink">#FFBCB3</color><!--粉色-->
    <color name="pink_one">#FDEBE8</color><!--浅粉色-->
    <color name="shallow_blue">#E7F3FC</color><!--浅蓝色 偏白-->
    <color name="shallow_gray">#F2F2F2</color><!--浅灰色-->
    <color name="dark_gray">#707070</color><!--深灰色-->
    <color name="shallow_black">#6D6D6D</color><!--褐黑色-->
    <color name="red">#FF0A00</color><!--红色-->
    <color name="line_gray">#E3E5E8</color><!--灰色分割线-->
    <color name="shallow_yellow">#E7C373</color><!--浅黄色-->
    <color name="world_city_color">#243440</color>
    <color name="blue_more">#C8DCFF</color><!--浅蓝色-->
    <color name="gray_white">#F8F8F8</color><!--灰白-->
    <color name="transparent_bg_2">#44000000</color><!--黑色半透明 二-->
    <color name="transparent_bg_3">#66000000</color><!--黑色半透明 三-->
    <color name="temp_max_tx">#FF7E45</color><!--高温文字颜色-->
    <color name="temp_min_tx">#B3BCCA</color><!--低温文字颜色-->
    <color name="white_2">#22FFFFFF</color><!--白色透明度22%-->
    <color name="white_4">#44FFFFFF</color><!--白色透明度44%-->
    <color name="white_6">#66FFFFFF</color><!--白色透明度66%-->
    <color name="white_8">#88FFFFFF</color><!--白色透明度88%-->
    <color name="purple">#56004f</color><!--紫色-->
    <color name="gray_white_2">#F6F6F6</color><!--灰白2-->
    <color name="gray_2">#626262</color><!--灰色2-->
    <color name="black_4">#141414</color><!--黑色4-->
    <color name="line">#DEDEE1</color><!--分割线-->
    <color name="point_color">#D9D9D9</color><!--点分割颜色-->
    <color name="gray_3">#818181</color><!--灰色3-->
    <color name="back_white">#f7f8fa</color>
    <color name="attention_text_light">#E9EAEF</color>
    <color name="dark_text_color">#B9C0CA</color>
    <color name="line_back_dark">#3f8DA0BA</color>
    <color name="line_color">#919191</color>
    <color name="search_light_un_color">#666666</color>
    <color name="slategray">#708090</color><!--灰石色 -->
</resources>


下面是样式文件


首先是shape_gradient_white.xml,这是一个白色渐变的背景。


<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <gradient
        android:startColor="@color/white_2"
        android:centerColor="@color/white_6"
        android:endColor="@color/white"
        android:angle="270" />
</shape>


然后是shape_dash_line.xml


<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="line">
    <size android:height="1dp" />
    <stroke
        android:dashGap="4dp"
        android:dashWidth="4dp"
        android:width="1dp"
        android:color="@color/point_color" />
</shape>


然后是LinearLayout的布局代码:


<LinearLayout
                android:layout_width="match_parent"
                android:layout_height="@dimen/dp_160"
                android:layout_marginLeft="@dimen/dp_12"
                android:layout_marginRight="@dimen/dp_12"
                android:background="@drawable/shape_blue_8"
                android:orientation="vertical"
                android:padding="@dimen/dp_12">
                <!--城市-->
                <TextView
                    android:id="@+id/tv_city"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="城市"
                    android:textColor="@color/white"
                    android:textSize="@dimen/sp_16" />
                <!--主要天气数据-->
                <LinearLayout
                    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="@color/white"
                        android:textSize="60sp" />
                    <!--天气状态的图文显示、空气质量、风力-->
                    <LinearLayout
                        android:layout_width="@dimen/dp_0"
                        android:layout_height="match_parent"
                        android:layout_marginLeft="@dimen/dp_8"
                        android:layout_weight="1"
                        android:gravity="center_vertical"
                        android:orientation="vertical">
                        <!--天气状态的图文显示-->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:gravity="center_vertical">
                            <!--天气状态的文字描述-->
                            <TextView
                                android:id="@+id/tv_weather_state_tv"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:text="天气状态"
                                android:textColor="@color/white"
                                android:textSize="@dimen/sp_14" />
                            <!--天气状况图片描述-->
                            <ImageView
                                android:id="@+id/iv_weather"
                                android:layout_width="@dimen/dp_36"
                                android:layout_height="@dimen/dp_36"
                                android:layout_marginLeft="@dimen/dp_12" />
                        </LinearLayout>
                        <!--空气质量、风力-->
                        <LinearLayout
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:gravity="center_vertical"
                            android:orientation="horizontal">
                            <!--空气质量-->
                            <TextView
                                android:id="@+id/tv_air"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:background="@drawable/shape_purple"
                                android:padding="@dimen/dp_2"
                                android:text="AQI 状态"
                                android:textColor="@color/purple"
                                android:textSize="@dimen/sp_14" />
                            <!--风信息描述-->
                            <TextView
                                android:id="@+id/tv_wind_info"
                                android:layout_width="wrap_content"
                                android:layout_height="wrap_content"
                                android:layout_marginLeft="@dimen/dp_12"
                                android:text="风向风力"
                                android:textColor="@color/white"
                                android:textSize="@dimen/sp_14" />
                        </LinearLayout>
                    </LinearLayout>
                </LinearLayout>
                <!--其他相关数据显示-->
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/dp_8"
                    android:orientation="horizontal">
                    <!--紫外线-->
                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:text="紫外线"
                        android:textColor="@color/white_8" />
                    <TextView
                        android:id="@+id/tv_uvIndex"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="@dimen/dp_12"
                        android:text=" "
                        android:textColor="@color/white" />
                    <!--湿度-->
                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="@dimen/dp_16"
                        android:text="湿度"
                        android:textColor="@color/white_8" />
                    <TextView
                        android:id="@+id/tv_humidity"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="@dimen/dp_12"
                        android:text=" "
                        android:textColor="@color/white" />
                    <!--大气压-->
                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="@dimen/dp_16"
                        android:text="大气压"
                        android:textColor="@color/white_8" />
                    <TextView
                        android:id="@+id/tv_pressure"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_marginLeft="@dimen/dp_12"
                        android:text=" "
                        android:textColor="@color/white" />
                </LinearLayout>
            </LinearLayout>


首先是shape_blue_8.xml


<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners android:radius="12dp" />
    <gradient
        android:startColor="@color/blue_one"
        android:centerColor="@color/blue_one"
        android:endColor="#6C84DC"
        android:angle="225" />
</shape>


然后是shape_purple.xml


<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <stroke android:width="1dip"
        android:color="@color/purple" />
    <corners android:radius="5dp" />
</shape>


其他的就没有了,OK,现在布局就已经是写好了。如果你需要完整的布局代码可以评论或者去我的GitHub上面看。

二、item布局


基本的布局写好了,但是里面还有两个列表,两个列表也是对应了两个布局xml的,也来看看吧,在app下的layout文件中新建两个xml文件分别是item_seven_day_daily_list.xml七天天气和item_today_detail.xml当前天气详情数据


20200827171511384.png


item_seven_day_daily_list.xm代码如下:


<?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:gravity="center_vertical"
        android:paddingTop="@dimen/dp_12"
        android:paddingBottom="@dimen/dp_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="@color/black"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"/>
        <!--天气描述  图标-->
        <ImageView
            android:id="@+id/iv_weather_state"
            android:background="@mipmap/icon_100"
            android:layout_width="30dp"
            android:layout_height="30dp"/>
        <!--最低温、最高温-->
        <LinearLayout
            android:gravity="right"
            android:layout_width="@dimen/dp_0"
            android:layout_height="wrap_content"
            android:layout_weight="1">
            <TextView
                android:id="@+id/tv_temp_height"
                android:textSize="@dimen/sp_14"
                android:textColor="@color/black"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
            <TextView
                android:id="@+id/tv_temp_low"
                android:textSize="@dimen/sp_14"
                android:textColor="@color/black"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </LinearLayout>
    <View
        android:background="@color/gray_white_2"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp_1"/>
</LinearLayout>


item_today_detail.xml代码如下:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical">
    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/dp_5"
        app:cardCornerRadius="@dimen/dp_6"
        android:elevation="@dimen/dp_4">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:orientation="vertical"
            android:paddingTop="@dimen/dp_16"
            android:paddingBottom="@dimen/dp_16">
            <ImageView
                android:id="@+id/iv_icon"
                android:scaleType="fitXY"
                android:layout_width="@dimen/dp_30"
                android:layout_height="@dimen/dp_30" />
            <TextView
                android:id="@+id/tv_value"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/dp_8"
                android:textColor="@color/black_4"
                android:textSize="@dimen/sp_16" />
            <TextView
                android:id="@+id/tv_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/dp_8"
                android:textColor="@color/gray_3"
                android:textSize="@dimen/sp_14" />
        </LinearLayout>
    </androidx.cardview.widget.CardView>
</LinearLayout>


然后写适配器


三、适配器


在app下的adapter包中新建一个SevenDailyAdapter

代码如下:


package com.llw.goodweather.adapter;
import android.widget.ImageView;
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.DailyResponse;
import com.llw.goodweather.utils.DateUtils;
import com.llw.goodweather.utils.WeatherUtil;
import java.util.List;
/**
 * 地图天气中 未来七天的天气列表适配器
 */
public class SevenDailyAdapter extends BaseQuickAdapter<DailyResponse.DailyBean, BaseViewHolder> {
    public SevenDailyAdapter(int layoutResId, @Nullable List<DailyResponse.DailyBean> data) {
        super(layoutResId, data);
    }
    @Override
    protected void convert(BaseViewHolder helper, DailyResponse.DailyBean item) {
        helper.setText(R.id.tv_date, DateUtils.dateSplitPlus(item.getFxDate()) +
                DateUtils.Week(item.getFxDate()))//日期
                .setText(R.id.tv_temp_height, item.getTempMax() + "℃")//最高温
                .setText(R.id.tv_temp_low, " / " + item.getTempMin() + "℃");//最低温
        //天气状态图片
        ImageView weatherStateIcon = helper.getView(R.id.iv_weather_state);
        int code = Integer.parseInt(item.getIconDay());//获取天气状态码,根据状态码来显示图标
        WeatherUtil.changeIcon(weatherStateIcon, code);//调用工具类中写好的方法
    }
}


再创建一个TodayDetailAdapter,代码如下:


package com.llw.goodweather.adapter;
import android.widget.ImageView;
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.TodayDetailBean;
import java.util.List;
/**
 * 地图天气中 今日天气详情数据
 */
public class TodayDetailAdapter extends BaseQuickAdapter<TodayDetailBean, BaseViewHolder> {
    public TodayDetailAdapter(int layoutResId, @Nullable List<TodayDetailBean> data) {
        super(layoutResId, data);
    }
    @Override
    protected void convert(BaseViewHolder helper, TodayDetailBean item) {
        ImageView imageView = helper.getView(R.id.iv_icon);
        imageView.setImageResource(item.getIcon());//图标
        helper.setText(R.id.tv_value,item.getValue())//值
                .setText(R.id.tv_name,item.getName());//名称
    }
}


适配器写好了,下面写订阅器


四、订阅器


在app下的contract包下新建一个MapWeatherContract,这个里面放置的就是需要使用的网络请求方法的配置,在Activity里面当前需要什么就放置什么,包含请求和返回方法,代码如下:


package com.llw.goodweather.contract;
import com.llw.goodweather.api.ApiService;
import com.llw.goodweather.bean.AirNowResponse;
import com.llw.goodweather.bean.DailyResponse;
import com.llw.goodweather.bean.NewSearchCityResponse;
import com.llw.goodweather.bean.NowResponse;
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 MapWeatherContract {
    public static class MapWeatherPresenter extends BasePresenter<IMapWeatherView> {
        /**
         * 搜索城市  V7版本中  需要把定位城市的id查询出来,然后通过这个id来查询详细的数据
         * @param location 城市名
         */
        public void searchCity(String location) {//注意这里的4表示新的搜索城市地址接口
            ApiService service = ServiceGenerator.createService(ApiService.class, 4);//指明访问的地址
            service.newSearchCity(location,"exact").enqueue(new NetCallBack<NewSearchCityResponse>() {
                @Override
                public void onSuccess(Call<NewSearchCityResponse> call, Response<NewSearchCityResponse> response) {
                    if(getView() != null){
                        getView().getNewSearchCityResult(response);
                    }
                }
                @Override
                public void onFailed() {
                    if(getView() != null){
                        getView().getDataFailed();
                    }
                }
            });
        }
        /**
         * 实况天气  V7版本
         * @param location  城市名
         */
        public void nowWeather(String location){//这个3 表示使用新的V7API访问地址
            ApiService service = ServiceGenerator.createService(ApiService.class,3);
            service.nowWeather(location).enqueue(new NetCallBack<NowResponse>() {
                @Override
                public void onSuccess(Call<NowResponse> call, Response<NowResponse> response) {
                    if(getView() != null){
                        getView().getNowResult(response);
                    }
                }
                @Override
                public void onFailed() {
                    if(getView() != null){
                        getView().getDataFailed();
                    }
                }
            });
        }
        /**
         * 天气预报  V7版本   7d 表示天气的数据 为了和之前看上去差别小一些,这里先用七天的
         * @param location   城市名
         */
        public void dailyWeather(String location){//这个3 表示使用新的V7API访问地址
            ApiService service = ServiceGenerator.createService(ApiService.class,3);
            service.dailyWeather("7d",location).enqueue(new NetCallBack<DailyResponse>() {
                @Override
                public void onSuccess(Call<DailyResponse> call, Response<DailyResponse> response) {
                    if(getView() != null){
                        getView().getDailyResult(response);
                    }
                }
                @Override
                public void onFailed() {
                    if(getView() != null){
                        getView().getDataFailed();
                    }
                }
            });
        }
        /**
         * 当天空气质量
         * @param location   城市名
         */
        public void airNowWeather(String location){
            ApiService service = ServiceGenerator.createService(ApiService.class,3);
            service.airNowWeather(location).enqueue(new NetCallBack<AirNowResponse>() {
                @Override
                public void onSuccess(Call<AirNowResponse> call, Response<AirNowResponse> response) {
                    if(getView() != null){
                        getView().getAirNowResult(response);
                    }
                }
                @Override
                public void onFailed() {
                    if(getView() != null){
                        getView().getDataFailed();
                    }
                }
            });
        }
    }
    public interface IMapWeatherView extends BaseView {
        //搜索城市返回城市id  通过id才能查下面的数据,否则会提示400  V7
        void getNewSearchCityResult(Response<NewSearchCityResponse> response);
        //实况天气
        void getNowResult(Response<NowResponse> response);
        //天气预报  7天
        void getDailyResult(Response<DailyResponse> response);
        //空气质量
        void getAirNowResult(Response<AirNowResponse> response);
        //错误返回
        void getDataFailed();
    }
}


五、数据渲染


打开MapWeatherActivity,


20200828091551150.png


完成上面一步,然后实现里面的方法。分别是


  @Override
    protected MapWeatherContract.MapWeatherPresenter createPresent() {
        return new MapWeatherContract.MapWeatherPresenter();
    }
  /**
     * 搜索城市返回
     *
     * @param response
     */
    @Override
    public void getNewSearchCityResult(Response<NewSearchCityResponse> response) {
    }
    /**
     * 实况天气返回
     *
     * @param response
     */
    @Override
    public void getNowResult(Response<NowResponse> response) {
    }
    /**
     * 未来七天天气预报数据返回
     *
     * @param response
     */
    @Override
    public void getDailyResult(Response<DailyResponse> response) {
    }
    /**
     * 空气质量数据返回
     *
     * @param response
     */
    @Override
    public void getAirNowResult(Response<AirNowResponse> response) {
    }
    /**
     * 错误返回
     */
    @Override
    public void getDataFailed() {
    }


方法有了,现在通过注解绑定控件id


  @BindView(R.id.tv_city)
    TextView tvCity;//城市
    @BindView(R.id.iv_weather)
    ImageView ivWeather;//天气状态图片描述
    @BindView(R.id.tv_temperature)
    TextView tvTemperature;//温度
    @BindView(R.id.tv_weather_state_tv)
    TextView tvWeatherStateTv;//天气状态文字描述
    @BindView(R.id.tv_wind_info)
    TextView tvWindInfo;//风力风向
    @BindView(R.id.rv_today_detail)
    RecyclerView rvTodayDetail;//今日详情数据列表
    @BindView(R.id.rv_seven_day_daily)
    RecyclerView rvSevenDayDaily;//七天天气预报列表
    @BindView(R.id.tv_more_daily)
    TextView tvMoreDaily;//15日天气预报数据
    @BindView(R.id.tv_uvIndex)
    TextView tvUvIndex;//紫外线强度
    @BindView(R.id.tv_humidity)
    TextView tvHumidity;//湿度
    @BindView(R.id.tv_pressure)
    TextView tvPressure;//大气压
    @BindView(R.id.tv_today_info)
    TextView tvTodayInfo;//今日天气简要描述
    @BindView(R.id.tv_air)
    TextView tvAir;//空气质量
    @BindView(R.id.bottom_sheet_ray)
    RelativeLayout bottomSheetRay;//底部拖动布局


声明需要的数据

  private String locationId;//城市id
    private List<DailyResponse.DailyBean> dailyList = new ArrayList<>();//七天天气预报
    List<TodayDetailBean> todayDetailList = new ArrayList<>();//当前天气详情
    private SevenDailyAdapter mSevenDailyAdapter;//七天天气预报适配器
    private String dayInfo;//今天白天天气描述
    private String nightInfo;//今天晚上天气描述
    private BottomSheetBehavior bottomSheetBehavior;//底部控件


还有一个就是点击事件的改变,之前是只有一个浮动按钮的点击事件,现在多了一个,所以用switch来解决,15日天气预报详情就点击到之前我写好的一个Activity里面,传入数据。当然你现在点击传过去是会出问题的,因为还没有赋值的。

  /**
     * 点击事件
     */
    @OnClick({R.id.btn_auto_location,R.id.tv_more_daily})
    public void onViewClicked(View view) {
        switch (view.getId()){
            case R.id.btn_auto_location://重新定位
                markerLatitude = 0;
                markerLongitude = 0;
                marker.remove();//清除标点
                initLocation();
                break;
            case R.id.tv_more_daily://15日天气预报详情
                Intent intent = new Intent(context, MoreDailyActivity.class);
                intent.putExtra("locationId", locationId);
                intent.putExtra("cityName", tvCity.getText().toString());
                startActivity(intent);
                break;
        }
    }


在上一篇文章中,通过自动定位和手动定位(点击地图定位),在定位返回监听中,通过经纬度的反编译解码得到了实际的所在位置,也就是省、市、区/县、街道等一些数据,但是我只需要一个区/县即可。


20200828093336852.png


在解码后请求这个信息来搜索城市相关信息,然后在搜索城市的返回方法中做处理,来看这个返回方法。


  /**
     * 搜索城市返回
     *
     * @param response
     */
    @Override
    public void getNewSearchCityResult(Response<NewSearchCityResponse> response) {
        if (response.body().getStatus().equals(Constant.SUCCESS_CODE)) {
            if (response.body().getLocation() != null && response.body().getLocation().size() > 0) {
                tvCity.setText(response.body().getLocation().get(0).getName());//城市
                locationId = response.body().getLocation().get(0).getId();//城市Id
                showLoadingDialog();
                mPresent.nowWeather(locationId);//查询实况天气
                mPresent.dailyWeather(locationId);//查询天气预报
                mPresent.airNowWeather(locationId);//空气质量
            } else {
                ToastUtils.showShortToast(context, "数据为空");
            }
        } else {
            tvCity.setText("查询城市失败");
            ToastUtils.showShortToast(context, CodeToStringUtils.WeatherCode(response.body().getStatus()));
        }
    }


在返回中设置城市的名称,给全局变量赋值,这样我点击15日天气预报传过去才有数据。你要知道页面渲染的步骤,当我能够点击按钮时,这个时候数据就都有了。然后请求另外三个接口方法,在返回中做处理,当然后期还会做优化,会涉及到网络状态、数据存储等地方,一步一步来,一口是吃不成胖子的。


  /**
     * 实况天气返回
     *
     * @param response
     */
    @Override
    public void getNowResult(Response<NowResponse> response) {
        if (response.body().getCode().equals(Constant.SUCCESS_CODE)) {//200则成功返回数据
            NowResponse data = response.body();
            if (data != null) {
                Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/Roboto-Light.ttf");
                tvTemperature.setText(response.body().getNow().getTemp() + "°");//温度
                tvTemperature.setTypeface(typeface);//使用字体
                tvWeatherStateTv.setText(data.getNow().getText());//天气状态文字描述
                WeatherUtil.changeIcon(ivWeather, Integer.parseInt(data.getNow().getIcon()));
                tvWindInfo.setText(data.getNow().getWindDir() + data.getNow().getWindScale() + "级");
                tvHumidity.setText(data.getNow().getHumidity() + "%");//湿度
                tvPressure.setText(data.getNow().getPressure() + "hPa");//大气压
                //今日详情
                todayDetailList.clear();
                todayDetailList.add(new TodayDetailBean(R.mipmap.icon_today_temp, data.getNow().getFeelsLike() + "°", "体感温度"));
                todayDetailList.add(new TodayDetailBean(R.mipmap.icon_today_precip, data.getNow().getPrecip() + "mm", "降水量"));
                todayDetailList.add(new TodayDetailBean(R.mipmap.icon_today_humidity, data.getNow().getHumidity() + "%", "湿度"));
                todayDetailList.add(new TodayDetailBean(R.mipmap.icon_today_pressure, data.getNow().getPressure() + "hPa", "大气压强"));
                todayDetailList.add(new TodayDetailBean(R.mipmap.icon_today_vis, data.getNow().getVis() + "KM", "能见度"));
                todayDetailList.add(new TodayDetailBean(R.mipmap.icon_today_cloud, data.getNow().getCloud() + "%", "云量"));
                TodayDetailAdapter adapter = new TodayDetailAdapter(R.layout.item_today_detail, todayDetailList);
                rvTodayDetail.setLayoutManager(new GridLayoutManager(context, 3));
                rvTodayDetail.setAdapter(adapter);
                adapter.notifyDataSetChanged();
            } else {
                ToastUtils.showShortToast(context, "暂无实况天气数据");
            }
        } else {//其他状态返回提示文字
            ToastUtils.showShortToast(context, CodeToStringUtils.WeatherCode(response.body().getCode()));
        }
    }


一些基础数据的赋值,然后组装数据到适配器中渲染。里面用到了6个小图标


20200828094844400.png


icon_today_temp.png


20200828095010705.png


icon_today_pressure.png


20200828095010704.png


icon_today_cloud.png


20200828095010695.png


icon_today_vis.png


20200828095010707.png


icon_today_precip.png


20200828095010697.png


icon_today_humidity.png


20200828095010696.png


然后看下一个返回方法

所有在initView中初始化,当然你也可以把三行代码直接放到返回中也可以


    mSevenDailyAdapter = new SevenDailyAdapter(R.layout.item_seven_day_daily_list, dailyList);
        rvSevenDayDaily.setLayoutManager(new LinearLayoutManager(context));
        rvSevenDayDaily.setAdapter(mSevenDailyAdapter);


在返回中,

  /**
     * 未来七天天气预报数据返回
     *
     * @param response
     */
    @Override
    public void getDailyResult(Response<DailyResponse> response) {
        if (response.body().getCode().equals(Constant.SUCCESS_CODE)) {
            List<DailyResponse.DailyBean> data = response.body().getDaily();
            if (data != null && data.size() > 0) {//判空处理
                for (int i = 0; i < data.size(); i++) {
                    if (data.get(i).getFxDate().equals(DateUtils.getNowDate())) {//今天
                        dayInfo = data.get(i).getTextDay();
                        nightInfo = data.get(i).getTextNight();
                        tvUvIndex.setText(WeatherUtil.uvIndexInfo(data.get(i).getUvIndex()));//紫外线强度
                    }
                }
                dailyList.clear();//添加数据之前先清除
                dailyList.addAll(data);//添加数据
                mSevenDailyAdapter.notifyDataSetChanged();//刷新列表
            } else {
                ToastUtils.showShortToast(context, "天气预报数据为空");
            }
        } else {//异常状态码返回
            ToastUtils.showShortToast(context, CodeToStringUtils.WeatherCode(response.body().getCode()));
        }
    }


这个也是常规的操作了,相信不用说明了。下一个


  /**
     * 空气质量数据返回
     *
     * @param response
     */
    @Override
    public void getAirNowResult(Response<AirNowResponse> response) {
        dismissLoadingDialog();
        if (response.body().getCode().equals(Constant.SUCCESS_CODE)) {
            AirNowResponse.NowBean data = response.body().getNow();
            if (response.body().getNow() != null) {
                tvAir.setText("AQI " + data.getCategory());
                tvTodayInfo.setText("今天白天" + dayInfo + ",晚上" + nightInfo + ",现在" + tvTemperature.getText().toString() + "," + WeatherUtil.apiToTip(data.getCategory()));
            } else {
                ToastUtils.showShortToast(context, "空气质量数据为空");
            }
        }
    }


这个里面有一个apiToTip方法是我新增的,打开WeatherUtil,新增方法代码如下:


  /**
     * 根据api的提示转为更为人性化的提醒
     * @param apiInfo
     * @return
     */
    public static String apiToTip(String apiInfo){
        String result = null;
        String str = null;
        if(apiInfo.contains("AQI ")){
            str = apiInfo.replace("AQI ", " ");
        }else {
            str = apiInfo;
        }
        switch (str){//优,良,轻度污染,中度污染,重度污染,严重污染
            case "优":
                result = "♪(^∇^*) 空气很好。";
                break;
            case "良":
                result = "ヽ(✿゚▽゚)ノ 空气不错。";
                break;
            case "轻度污染":
                result = "(⊙﹏⊙) 空气有些糟糕。";
                break;
            case "中度污染":
                result = " ε=(´ο`*)))唉 空气污染较为严重,注意防护。";
                break;
            case "重度污染":
                result = "o(≧口≦)o 空气污染很严重,记得戴口罩哦!";
                break;
            case "严重污染":
                result = "ヽ(*。>Д<)o゜ 完犊子了!空气污染非常严重,要减少出门,定期检查身体,能搬家就搬家吧!";
                break;
        }
        return result;
    }


最后在错误返回时,一般是网络很差或者没有网络是会提示你。


  /**
     * 错误返回
     */
    @Override
    public void getDataFailed() {
        ToastUtils.showShortToast(context, "连接超时,稍后尝试。");
    }


数据是有了,但是页面的一些布局效果要再修改一下。


六、页面效果优化


  <!--浮动按钮-->
    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/btn_auto_location"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/dp_12"
        android:layout_marginTop="@dimen/dp_20"
        android:clickable="true"
        android:src="@mipmap/icon_auto_location"
        app:backgroundTint="@color/transparent_bg_3"
        app:backgroundTintMode="screen"
        app:borderWidth="@dimen/dp_0"
        app:fabSize="mini"
        app:hoveredFocusedTranslationZ="@dimen/dp_18"
        app:pressedTranslationZ="@dimen/dp_18"
        app:rippleColor="@color/blue_one" />


修改一个activity_map_weather.xml中的浮动按钮,我改动了一下这个按钮的显示位置,现在会出现在屏幕的左上角,当然这个按钮还需要和我们的协调布局做一些效果,就是当我向上拖动底部布局时,此时如果是手动定位则浮动按钮隐藏,向下收缩则浮动按钮显示。


所在要在initView加入,上下拖动的监听。


    /*获取behavior 控制bottomsheet的 显示 与隐藏*/
        bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetRay);
        /*bottomSheet 的 状态改变 根据不同的状态 做不同的事情*/
        bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View bottomSheet, int newState) {
                switch (newState) {
                    case BottomSheetBehavior.STATE_COLLAPSED://收缩
                        //手动定位时,收缩就要显示浮动按钮,因为这时候你可以操作地图了
                        if (markerLatitude != 0) {//自动定位
                            btnAutoLocation.show();//显示自动定位按钮
                        }
                        break;
                    case BottomSheetBehavior.STATE_EXPANDED://展开
                        //手动定位时,展开就要隐藏浮动按钮,因为这时候你是没有办法操作地图的
                        if (markerLatitude != 0) {//自动定位
                            btnAutoLocation.hide();//隐藏自动定位按钮
                        }
                        break;
                }
            }
            @Override
            public void onSlide(@NonNull View bottomSheet, float slideOffset) {
                /*slideOffset bottomSheet 的 移动距离*/
            }
        });


至此数据就写完了,来看一下运行的效果吧


20200828101017101.gif


这个APP还没有结束,未完待续


相关文章
|
3月前
|
Android开发 开发者 iOS开发
APP开发后如何上架,上架Android应用市场前要准备什么
移动应用程序(APP)的开发已经成为现代企业和开发者的常见实践。然而,开发一个成功的APP只是第一步,将其上架到应用商店让用户下载和使用是实现其潜力的关键一步。
|
4天前
|
测试技术 Android开发
Android App获取不到pkgInfo信息问题原因
Android App获取不到pkgInfo信息问题原因
14 0
|
10天前
|
Android开发 开发者
Android网络和数据交互: 请解释Android中的AsyncTask的作用。
Android&#39;s AsyncTask simplifies asynchronous tasks for brief background work, bridging UI and worker threads. It involves execute() for starting tasks, doInBackground() for background execution, publishProgress() for progress updates, and onPostExecute() for returning results to the main thread.
10 0
|
10天前
|
网络协议 安全 API
Android网络和数据交互: 什么是HTTP和HTTPS?在Android中如何进行网络请求?
HTTP和HTTPS是网络数据传输协议,HTTP基于TCP/IP,简单快速,HTTPS则是加密的HTTP,确保数据安全。在Android中,过去常用HttpURLConnection和HttpClient,但HttpClient自Android 6.0起被移除。现在推荐使用支持TLS、流式上传下载、超时配置等特性的HttpsURLConnection进行网络请求。
10 0
|
24天前
|
XML Java Android开发
Android每点击一次按钮就添加一条数据
Android每点击一次按钮就添加一条数据
24 1
|
1月前
|
设计模式 测试技术 数据库
基于Android的食堂点餐APP的设计与实现(论文+源码)_kaic
基于Android的食堂点餐APP的设计与实现(论文+源码)_kaic
|
1月前
|
存储 Android开发 C++
【Android 从入门到出门】第五章:使用DataStore存储数据和测试
【Android 从入门到出门】第五章:使用DataStore存储数据和测试
36 3
|
2月前
|
JavaScript Java 数据安全/隐私保护
安卓逆向 -- POST数据解密
安卓逆向 -- POST数据解密
27 2
|
2月前
|
安全 Java 数据挖掘
当 App 有了系统权限,真的可以为所欲为? Android Performance Systrace
当 App 有了系统权限,真的可以为所欲为? Android Performance Systrace 转载自: https://androidperformance.com/2023/05/14/bad-android-app-with-system-permissions/#/0-Dex-%E6%96%87%E4%BB%B6%E4%BF%A1%E6%81%AF
31 0