Android 天气APP(十六)热门城市 - 海外城市

简介: Android 天气APP(十六)热门城市 - 海外城市

前言


反正国内的天气也做好了,不妨做一下做一下国外的天气,国外的天气数据的展示,我将重新设计一套UI,区别于国内的天气UI,开搞~


运行效果图

image.gif


正文


既然要访问国外的天气,那么肯定不能像国内这样,省、市、区/县这样来详细展示,所以只展示热门的城市,因为我相信那些去国外玩的朋友肯定也不会去一个不知名的地方吧,不然回来之后你怎么炫耀呢?


API地址


和风天气有这方面的API,白嫖侠申请出战!地址如下


https://search.heweather.net/top?key=3086e91d66c04ce588a7f538f917c7f4&group=overseas&number=50&lang=zh


可以用浏览器测试一下


20200609164048902.png


然后返回数据生成一个实体bean


20200609164204953.png


在bean包下创建一个HotCityResponse,代码如下:


package com.llw.goodweather.bean;
import java.util.List;
/**
 * 热门城市数据实体
 */
public class HotCityResponse {
    private List<HeWeather6Bean> HeWeather6;
    public List<HeWeather6Bean> getHeWeather6() {
        return HeWeather6;
    }
    public void setHeWeather6(List<HeWeather6Bean> HeWeather6) {
        this.HeWeather6 = HeWeather6;
    }
    public static class HeWeather6Bean {
        private String status;
        private List<BasicBean> basic;
        public String getStatus() {
            return status;
        }
        public void setStatus(String status) {
            this.status = status;
        }
        public List<BasicBean> getBasic() {
            return basic;
        }
        public void setBasic(List<BasicBean> basic) {
            this.basic = basic;
        }
        public static class BasicBean {
            /**
             * cid : FR2988507
             * location : 巴黎
             * parent_city : 巴黎
             * admin_area : 法兰西岛大区
             * cnty : 法国
             * lat : 48.85694504
             * lon : 2.35138893
             * tz : +1.00
             * type : city
             */
            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;
            private String type;
            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 String getType() {
                return type;
            }
            public void setType(String type) {
                this.type = type;
            }
        }
    }
}


然后打开ApiService.java


20200609164524369.png


  /**
     * 海外热门城市
     */
    @GET("/top?key=3086e91d66c04ce588a7f538f917c7f4&group=overseas&number=50&lang=zh")
    Call<HotCityResponse> hotCity();


② 热门城市订阅器


contract包下创建HotCityContract


20200609165306918.png


代码如下:

package com.llw.goodweather.contract;
import android.content.Context;
import com.llw.goodweather.api.ApiService;
import com.llw.goodweather.bean.HotCityResponse;
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 HotCityContract {
    public static class HotCityPresenter extends BasePresenter<IHotCityView> {
        /**
         * 热门城市城市 - 海外
         * @param context
         */
        public void hotCity(final Context context) {
            ApiService service = ServiceGenerator.createService(ApiService.class, 2);//指明访问的地址
            service.hotCity().enqueue(new NetCallBack<HotCityResponse>() {
                @Override
                public void onSuccess(Call<HotCityResponse> call, Response<HotCityResponse> response) {
                    if(getView() != null){
                        getView().getHotCityResult(response);
                    }
                }
                @Override
                public void onFailed() {
                    if(getView() != null){
                        getView().getDataFailed();
                    }
                }
            });
        }
    }
    public interface IHotCityView extends BaseView {
        //热门城市返回数据
        void getHotCityResult(Response<HotCityResponse> response);
        //错误返回
        void getDataFailed();
    }
}


接下来就要创建一个新的Activity,用于展示热门城市的数据


③ 创建Activity并设计布局


20200609170111625.png


20200609165927311.png


接下来就可以设计布局,再此之前,要放一些需要的东西,

首先在mvplibrary里增加一些颜色


20200612155446673.png


  <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><!--浅粉色-->


然后是图标,一个小飞机图片


下面是图片

20200612155617210.png

上面是图片


因为是白色,你看不见很正常,保存到本地就可以了。


然后是样式,一个渐变的圆角背景


20200612155831884.png


在app的drawable下创建shape_orange_8.xml


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


现在可以创建布局了


app下面的layout下创建item_hot_city_list.xml


效果如下


20200612160139115.png


代码如下


<?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"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <androidx.cardview.widget.CardView
        android:id="@+id/item_hot_city"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/dp_6"
        android:foreground="@drawable/bg_white"
        app:cardBackgroundColor="@color/white"
        app:cardCornerRadius="@dimen/dp_8"
        app:cardElevation="@dimen/dp_4">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:orientation="horizontal">
            <ImageView
                android:layout_width="@dimen/dp_80"
                android:layout_height="@dimen/dp_80"
                android:background="@drawable/shape_orange_8"
                android:gravity="center"
                android:padding="@dimen/dp_20"
                android:src="@mipmap/icon_hot_city" />
            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center_vertical"
                android:orientation="vertical"
                android:paddingLeft="@dimen/dp_16">
                <TextView
                    android:id="@+id/tv_hot_city_name"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="巴黎"
                    android:textColor="@color/black_3"
                    android:textSize="@dimen/sp_16"
                    android:textStyle="bold" />
                <TextView
                    android:id="@+id/tv_cnty_and_area"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/dp_8"
                    android:text="巴黎"
                    android:textColor="@color/gray"
                    android:textSize="@dimen/sp_14"
                    android:textStyle="bold" />
            </LinearLayout>
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginRight="@dimen/dp_12"
                android:src="@mipmap/icon_open_orange" />
        </LinearLayout>
    </androidx.cardview.widget.CardView>
</LinearLayout>


然后修改HotCityActivity对应的activity_hot_city.xml


效果如下

202006121603594.png


代码如下:

<?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:layout_width="match_parent"
    android:fitsSystemWindows="true"
    android:background="@color/shallow_orange"
    android:orientation="vertical"
    android:layout_height="match_parent"
    tools:context=".ui.HotCityActivity">
    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/orange"
        app:layout_constraintEnd_toEndOf="parent"
        app:navigationIcon="@mipmap/icon_return_white"
        app:contentInsetLeft="@dimen/dp_16"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:popupTheme="@style/AppTheme.PopupOverlay">
        <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:textSize="@dimen/sp_16"
            android:textColor="@color/white"
            android:text="海外热门城市" />
    </androidx.appcompat.widget.Toolbar>
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>


④ 创建热门城市列表适配及数据渲染


在adapter包下创建HotCityAdapter.java


2020061216062448.png


代码如下:

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.HotCityResponse;
import java.util.ArrayList;
import java.util.List;
/**
 * 热门城市列表适配器
 */
public class HotCityAdapter extends BaseQuickAdapter<HotCityResponse.HeWeather6Bean.BasicBean, BaseViewHolder> {
    public HotCityAdapter(int layoutResId, @Nullable List<HotCityResponse.HeWeather6Bean.BasicBean> data) {
        super(layoutResId, data);
    }
    @Override
    protected void convert(BaseViewHolder helper, HotCityResponse.HeWeather6Bean.BasicBean item) {
        helper.setText(R.id.tv_hot_city_name,item.getLocation())
                .setText(R.id.tv_cnty_and_area,item.getCnty()+" —— "+item.getAdmin_area());
        helper.addOnClickListener(R.id.item_hot_city);
    }
}


然后就是到HotCityActivity里去使用了


20200612161110708.png

  @BindView(R.id.toolbar)
    Toolbar toolbar;//标题bar
    @BindView(R.id.rv)
    RecyclerView rv;//列表
    List<HotCityResponse.HeWeather6Bean.BasicBean> mList = new ArrayList<>();
    HotCityAdapter mAdapter;

20200612161153856.png


连接之前创建好的订阅,需要实现五个方法。


initData


  @Override
    public void initData(Bundle savedInstanceState) {
        showLoadingDialog();//加载弹窗
        StatusBarUtil.setStatusBarColor(context, R.color.orange);//白色状态栏
        Back(toolbar);//返回
        initList();//初始化列表数据
    }


getLayoutId


  @Override
    public int getLayoutId() {
        return R.layout.activity_hot_city;
    }


createPresent

  @Override
    protected HotCityContract.HotCityPresenter createPresent() {
        return new HotCityContract.HotCityPresenter();
    }


getHotCityResult

  //返回数据处理
  @Override
    public void getHotCityResult(Response<HotCityResponse> response) {
        dismissLoadingDialog();
        if (("ok").equals(response.body().getHeWeather6().get(0).getStatus())) {
            //数据赋值
            if(response.body().getHeWeather6().get(0).getBasic()!=null && response.body().getHeWeather6().get(0).getBasic().size()>0){
                mList.clear();
                mList.addAll(response.body().getHeWeather6().get(0).getBasic());
                mAdapter.notifyDataSetChanged();
                runLayoutAnimation(rv);//刷新适配器
            }else {
                ToastUtils.showShortToast(context,"数据为空");
            }
        } else {
            ToastUtils.showShortToast(context, response.body().getHeWeather6().get(0).getStatus());
        }
    }


getDataFailed

//异常返回
    @Override
    public void getDataFailed() {
        dismissLoadingDialog();
        ToastUtils.showShortToast(context, "请求超时");
    }


还有一个initList

 private void initList() {
        mAdapter = new HotCityAdapter(R.layout.item_hot_city_list,mList);
        rv.setLayoutManager(new LinearLayoutManager(context));
        rv.setAdapter(mAdapter);
        //item 点击事件
        mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
            @Override
            public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
                //点击之后的业务逻辑处理
            }
        });
        mPresent.hotCity(context);
    }


将原来的onCreate方法删掉,现在这个页面的代码是写完了,但是从哪里进来呢,这个入口还没有写呢,修改window_add.xml


20200612175508163.png


增加的代码如下:

    <TextView
            android:id="@+id/tv_hot_city"
            android:gravity="center"
            android:layout_width="@dimen/dp_140"
            android:layout_height="@dimen/dp_48"
            android:text="热门城市"
            android:foreground="@drawable/bg_white"
            android:textColor="@color/black"
            android:textSize="@dimen/sp_16"/>


然后进入MainActivity找到showAddWindow方法,增加


20200612175902286.png


然后运行起来


20200612161806297.png


数据就加载出来了。


⑤ 热门城市的天气信息展示


这里就需要创建一个新的页面了,因为我希望区别于MainActivity的样式。


20200612162029990.png


在ui包下创建一个新的Activity,名为HotCityWeatherActivity.java,对应的xml文件是activity_hot_city_weather.xml


首先更改布局文件

<?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:layout_width="match_parent"
    android:orientation="vertical"
    android:fitsSystemWindows="true"
    android:layout_height="match_parent"
    tools:context=".ui.HotCityWeatherActivity">
    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/dp_50"
        android:background="@color/pink"
        app:layout_constraintEnd_toEndOf="parent"
        app:navigationIcon="@mipmap/icon_return_white"
        app:contentInsetLeft="@dimen/dp_16"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:popupTheme="@style/AppTheme.PopupOverlay">
    </androidx.appcompat.widget.Toolbar>
    <LinearLayout
        android:orientation="vertical"
        android:gravity="center_horizontal"
        android:background="@color/pink"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="@dimen/dp_20">
        <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:textSize="@dimen/sp_24"
            android:textColor="@color/white"
            android:text="城市" />
        <!--温度-->
        <RelativeLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView
                android:id="@+id/tv_temperature"
                android:layout_centerHorizontal="true"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="0"
                android:textColor="@color/white"
                android:textSize="72sp" />
            <TextView
                android:layout_toRightOf="@+id/tv_temperature"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="℃"
                android:textColor="@color/white"
                android:textSize="24sp" />
        </RelativeLayout>
        <LinearLayout
            android:gravity="center"
            android:layout_marginTop="12dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
            <!--气候图标-->
            <ImageView
                android:id="@+id/iv_weather_state"
                android:scaleType="fitXY"
                android:background="@mipmap/icon_100"
                android:layout_width="@dimen/dp_24"
                android:layout_height="@dimen/dp_24"/>
            <!--最高温和最低温-->
            <TextView
                android:id="@+id/tv_tem_max"
                android:textColor="@color/white"
                android:textSize="@dimen/sp_14"
                android:layout_marginLeft="@dimen/dp_8"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
            <TextView
                android:id="@+id/tv_tem_min"
                android:textColor="@color/white"
                android:textSize="@dimen/sp_14"
                android:alpha="0.5"
                android:layout_marginLeft="@dimen/dp_8"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>


然后创建一个订阅器


20200612162737825.png


代码如下:

package com.llw.goodweather.contract;
import android.content.Context;
import com.llw.goodweather.api.ApiService;
import com.llw.goodweather.bean.HotCityResponse;
import com.llw.goodweather.bean.WeatherResponse;
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 HotCityWeatherContract {
    public static class HotCityPresenter extends BasePresenter<IHotCityView> {
        /**
         * 天气所有数据
         * @param context
         * @param location
         */
        public void weatherData(final Context context,String location){
            ApiService service = ServiceGenerator.createService(ApiService.class,0);
            service.weatherData(location).enqueue(new NetCallBack<WeatherResponse>() {
                @Override
                public void onSuccess(Call<WeatherResponse> call, Response<WeatherResponse> response) {
                    if(getView() != null){
                        getView().getWeatherDataResult(response);
                    }
                }
                @Override
                public void onFailed() {
                    if(getView() != null){
                        getView().getDataFailed();
                    }
                }
            });
        }
    }
    public interface IHotCityView extends BaseView {
        //查询天气所有数据
        void getWeatherDataResult(Response<WeatherResponse> response);
        //错误返回
        void getDataFailed();
    }
}


然后修改HotCityWeatherActivity页面的代码


首先衔接这个订阅器


20200612163026655.png

然后绑定控件

  @BindView(R.id.tv_title)
    TextView tvTitle;//城市
    @BindView(R.id.toolbar)
    Toolbar toolbar;//标题bar
    @BindView(R.id.tv_temperature)
    TextView tvTemperature;//温度
    @BindView(R.id.iv_weather_state)
    ImageView ivWeatherState;//天气状况图片
    @BindView(R.id.tv_tem_max)
    TextView tvTemMax;//最高温
    @BindView(R.id.tv_tem_min)
    TextView tvTemMin;//最低温


相应实现那五个方法,这里我就一起贴出来


  @Override
    public void initData(Bundle savedInstanceState) {
        showLoadingDialog();
        StatusBarUtil.setStatusBarColor(context, R.color.pink);//设置状态栏背景颜色
        Back(toolbar);
        String location = getIntent().getStringExtra("location");
        mPresent.weatherData(context, location);
    }
    @Override
    public int getLayoutId() {
        return R.layout.activity_hot_city_weather;
    }
    @Override
    protected HotCityWeatherContract.HotCityPresenter createPresent() {
        return new HotCityWeatherContract.HotCityPresenter();
    }
   //天气信息数据返回
    @Override
    public void getWeatherDataResult(Response<WeatherResponse> response) {
        if (("ok").equals(response.body().getHeWeather6().get(0).getStatus())) {
            if (response.body().getHeWeather6().get(0).getBasic() != null) {//得到数据不为空则进行数据显示
                //基本天气信息
                WeatherResponse.HeWeather6Bean.NowBean nowBean = response.body().getHeWeather6().get(0).getNow();
                tvTitle.setText(response.body().getHeWeather6().get(0).getBasic().getLocation());
                Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/Roboto-Light.ttf");
                tvTemperature.setText(nowBean.getTmp());
                tvTemperature.setTypeface(typeface);//使用字体
                int code = Integer.parseInt(nowBean.getCond_code());//获取天气状态码,根据状态码来显示图标
                WeatherUtil.changeIcon(ivWeatherState, code);//调用工具类中写好的方法
                //最低温和最高温
                tvTemMax.setText(response.body().getHeWeather6().get(0).getDaily_forecast().get(0).getTmp_max());
                tvTemMin.setText(" / "+response.body().getHeWeather6().get(0).getDaily_forecast().get(0).getTmp_min()+ " ℃");
                dismissLoadingDialog();
            } else {
                ToastUtils.showShortToast(context, CodeToStringUtils.WeatherCode(response.body().getHeWeather6().get(0).getStatus()));
            }
        }
    }
  //异常返回
    @Override
    public void getDataFailed() {
        dismissLoadingDialog();
        ToastUtils.showShortToast(context, "请求超时");
    }


在getWeatherDataResult里面用到了字体样式,这里放上链接可以直接下载,点击文件提取链接,文件提取码 kn72。下载之后赋值到assets下


20200612163835131.png


然后要进入热门城市的天气页面还得在列表页面做item的点击事件处理才行,打开HotCityActivity


20200612180111117.png


    Intent intent = new Intent(context,HotCityWeatherActivity.class);
        intent.putExtra("location",mList.get(position).getLocation());
        startActivity(intent);


运行一下


20200612164053442.png

⑥ TabLayout+ViewPager+Fragment使用


然后要加入TabLayout和ViewPager,通过viewPager来切换不同的Fragment,这里我添加了一个第三方的库,效果比较好,微博的顶部TabLayout切换的效果和这个是一样的。


在mvplibrary下的build.gradle的dependencies中加入如下图的依赖


image.png


  //蠕虫蠕动动画TabLayout
    api 'com.ogaclejapan.smarttablayout:library:2.0.0@aar'
    //Optional: see how to use the utility.
    api 'com.ogaclejapan.smarttablayout:utils-v4:2.0.0@aar'


然后Sync同步一下,

接下来就是修改activity_hot_city_weather.xml布局


20200612164709952.png

  <!--动画TabLayout-->
    <com.ogaclejapan.smarttablayout.SmartTabLayout
        android:id="@+id/tab"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        app:stl_indicatorAlwaysInCenter="false"
        app:stl_indicatorWithoutPadding="false"
        app:stl_indicatorInFront="false"
        app:stl_indicatorInterpolation="smart"
        app:stl_indicatorGravity="bottom"
        app:stl_indicatorColor="@color/pink"
        app:stl_indicatorThickness="4dp"
        app:stl_indicatorWidth="auto"
        app:stl_dividerColor="@color/transparent"
        app:stl_indicatorCornerRadius="2dp"
        app:stl_overlineColor="@color/transparent"
        app:stl_overlineThickness="0dp"
        app:stl_underlineColor="@color/transparent"
        app:stl_underlineThickness="1dp"
        app:stl_defaultTabBackground="@color/transparent"
        app:stl_defaultTabTextAllCaps="false"
        app:stl_defaultTabTextColor="#FC000000"
        app:stl_defaultTabTextSize="12sp"
        app:stl_defaultTabTextHorizontalPadding="16dp"
        app:stl_defaultTabTextMinWidth="0dp"
        app:stl_distributeEvenly="false"
        app:stl_clickable="true"
        app:stl_titleOffset="24dp"
        app:stl_drawDecorationAfterTab="false"
        />
    <!--ViewPager-->
    <androidx.viewpager.widget.ViewPager
        android:id="@+id/vp"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


然后创建两个Fragment和两个布局文件


20200612164852383.png


我特别新建了一个fragment包来存放ForecastFragment和TodayFragment。然后是对应的布局文件


20200612165040827.png


fragment_forecast.xml和fragment_today.xml

两个布局文件的代码是一样的


<?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"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</LinearLayout>


这个时候进入ForecastFragment修改代码,继承BaseFragment,然后设置视图


public class ForecastFragment extends BaseFragment {
    @Override
    public void initData(Bundle savedInstanceState) {
    }
    @Override
    public int getLayoutId() {
        return R.layout.fragment_forecast;
    }
}


然后是TodayFragment


public class TodayFragment extends BaseFragment {
    @Override
    public void initData(Bundle savedInstanceState) {
    }
    @Override
    public int getLayoutId() {
        return R.layout.fragment_today;
    }
}


Fragment都准备好了,然后就是在HotCityWeatherActivity使用了,之前已经在布局中添加过视图了,现在就是绑定视图


  @BindView(R.id.tab)
    SmartTabLayout tab;//标签视图
    @BindView(R.id.vp)
    ViewPager vp;


然后创建一个方法用来装载两个Fragment


private void initView() {
        FragmentPagerItemAdapter adapter = new FragmentPagerItemAdapter(
                getSupportFragmentManager(), FragmentPagerItems.with(this)
                .add("Today", TodayFragment.class)
                .add("Forecast", ForecastFragment.class)
                .create());
        vp.setAdapter(adapter);
        tab.setViewPager(vp);
    }


最后就是在InitData中调用initView了


20200612170234962.png


运行一下


20200612170312420.png


这个时候你就可以左右滑动或者是点击tab来切换不同的Fragment


⑦ Activity和Fragment通讯


这里我们使用EventBus来进行通讯,主要原因就是代码量少,页面简洁


20200612170602277.png


在eventbus包下创建ForecastEvent和TodayHourlyEvent,代码如下:


TodayHourlyEvent.java


package com.llw.goodweather.eventbus;
import com.llw.goodweather.bean.WeatherResponse;
import java.util.List;
/**
 * 热门城市当天逐三小时天气信息事件
 */
public class TodayHourlyEvent {
    public List<WeatherResponse.HeWeather6Bean.HourlyBean> mHourlyBean;
    public TodayHourlyEvent(List<WeatherResponse.HeWeather6Bean.HourlyBean> hourlyBean) {
        this.mHourlyBean = hourlyBean;
    }
}


ForecastEvent.java


package com.llw.goodweather.eventbus;
import com.llw.goodweather.bean.WeatherResponse;
import java.util.List;
/**
 * 热门城市天气预报事件
 */
public class ForecastEvent {
    public List<WeatherResponse.HeWeather6Bean.DailyForecastBean> mForecastBean;
    public ForecastEvent(List<WeatherResponse.HeWeather6Bean.DailyForecastBean> forecastBean) {
        this.mForecastBean = forecastBean;
    }
}


事件写好了,接下来就是使用,首先在HotCityWeatherActivity页面中的getWeatherDataResult方法中传递这两个事件


20200612171333876.png


  //传递数据到TodayFragment和ForcastFragment
    EventBus.getDefault().post(new TodayHourlyEvent(response.body().getHeWeather6().get(0).getHourly()));
    EventBus.getDefault().post(new ForecastEvent(response.body().getHeWeather6().get(0).getDaily_forecast()));


然后到两个Fragment里面去接收


使用之前要先注册,如果不是第一次使用的话,就要先判断是否注册过,判断的业务逻辑写在initData里面,同时销毁的时候也要判断


如下所示


TodayFragment

  @Override
    public void initData(Bundle savedInstanceState) {
        if (!EventBus.getDefault().isRegistered(this)) {
            EventBus.getDefault().register(this);
        }
    }
  @Override
    public void onDestroyView() {
        super.onDestroyView();
        if (EventBus.getDefault().isRegistered(this)) {
            EventBus.getDefault().unregister(this);
        }
    }


然后才是接收传递过来的消息

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(TodayHourlyEvent event) {//接收
        ToastUtils.showShortToast(context, "有" + event.mHourlyBean.size() + "条数据");
    }


同样的代码在ForecastFragment也只是接收的代码不一样而已,我这里贴一下

@Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(ForecastEvent event) {//接收
        ToastUtils.showShortToast(context, "有" + event.mForecastBean.size() + "条数据");
    }


你可以再运行一下,当你的页面出现两个Toast的时候就说明通讯成功了,这里图我就不贴了。


⑧ 温度折线图


温度折线图,这个地方需要使用Android的图表,我从网络上找了一个自定义的控件,然后再加以改动就符合自己使用的业务场景了,同样这个自定义VIew也是放在mvplibrary下面的。第一步当然是创建样式了。


20200612172836784.png


这应该一目了然吧。

样式代码:

   <!--折线图-->
    <declare-styleable name="WeatherChartView">
        <attr name="textColor" format="color"/>
        <attr name="dayTextColor" format="color"/>
        <attr name="nightTextColor" format="color"/>
        <attr name="dayColor" format="color"/>
        <attr name="nightColor" format="color"/>
        <attr name="textSize" format="dimension"/>
    </declare-styleable>

20200612173030796.png


这里创建了两个View,用于应对不同的业务需求

WeatherChartView.java

package com.llw.mvplibrary.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import com.llw.mvplibrary.R;
/**
 * 温度折线
 */
public class WeatherChartView extends View {
    /**
     * x轴集合
     */
    private float mXAxis[] = new float[8];
    /**
     * 白天y轴集合
     */
    private float mYAxisDay[] = new float[8];
    /**
     * 夜间y轴集合
     */
    private float mYAxisNight[] = new float[8];
    /**
     * x,y轴集合数
     */
    private static final int LENGTH = 8;
    /**
     * 白天温度集合
     */
    private int mTempDay[] = new int[8];
    /**
     * 夜间温度集合
     */
    private int mTempNight[] = new int[8];
    /**
     * 控件高
     */
    private int mHeight;
    /**
     * 字体大小
     */
    private float mTextSize;
    /**
     * 圓半径
     */
    private float mRadius;
    /**
     * 圓半径今天
     */
    private float mRadiusToday;
    /**
     * 文字移动位置距离
     */
    private float mTextSpace;
    /**
     * 白天折线颜色
     */
    private int mColorDay;
    /**
     * 夜间折线颜色
     */
    private int mColorNight;
    /**
     * 屏幕密度
     */
    private float mDensity;
    /**
     * 控件边的空白空间
     */
    private float mSpace;
    /**
     * 线画笔
     */
    private Paint mLinePaint;
    /**
     * 点画笔
     */
    private Paint mPointPaint;
    /**
     * 字体画笔
     */
    private Paint mTextPaint;
    public WeatherChartView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }
    @SuppressWarnings("deprecation")
    private void init(Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WeatherChartView);
        float densityText = getResources().getDisplayMetrics().scaledDensity;
        mTextSize = a.getDimensionPixelSize(R.styleable.WeatherChartView_textSize,
                (int) (14 * densityText));
        mColorDay = a.getColor(R.styleable.WeatherChartView_dayColor,
                getResources().getColor(R.color.pink));
        mColorNight = a.getColor(R.styleable.WeatherChartView_nightColor,
                getResources().getColor(R.color.blue_one));
        int textColor = a.getColor(R.styleable.WeatherChartView_textColor, Color.WHITE);
        a.recycle();
        mDensity = getResources().getDisplayMetrics().density;
        mRadius = 3 * mDensity;
        mRadiusToday = 5 * mDensity;
        mSpace = 3 * mDensity;
        mTextSpace = 10 * mDensity;
        float stokeWidth = 2 * mDensity;
        mLinePaint = new Paint();
        mLinePaint.setAntiAlias(true);
        mLinePaint.setStrokeWidth(stokeWidth);
        mLinePaint.setStyle(Paint.Style.STROKE);
        mPointPaint = new Paint();
        mPointPaint.setAntiAlias(true);
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(textColor);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
    }
    public WeatherChartView(Context context) {
        super(context);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mHeight == 0) {
            // 设置控件高度,x轴集合
            setHeightAndXAxis();
        }
        // 计算y轴集合数值
        computeYAxisValues();
        if (mTempDay[0] != 0 && mTempDay[7] != 0) {
            // 画白天折线图
            drawChart(canvas, mColorDay, mTempDay, mYAxisDay, 0);
        }
        if (mTempNight[0] != 0 && mTempNight[7] != 0) {
            // 画夜间折线图
            drawChart(canvas, mColorNight, mTempNight, mYAxisNight, 1);
        }
    }
    /**
     * 计算y轴集合数值
     */
    private void computeYAxisValues() {
        // 存放白天最低温度
        int minTempDay = mTempDay[0];
        // 存放白天最高温度
        int maxTempDay = mTempDay[0];
        for (int item : mTempDay) {
            if (item < minTempDay) {
                minTempDay = item;
            }
            if (item > maxTempDay) {
                maxTempDay = item;
            }
        }
        // 存放夜间最低温度
        int minTempNight = mTempNight[0];
        // 存放夜间最高温度
        int maxTempNight = mTempNight[0];
        for (int item : mTempNight) {
            if (item < minTempNight) {
                minTempNight = item;
            }
            if (item > maxTempNight) {
                maxTempNight = item;
            }
        }
        // 白天,夜间中的最低温度
        int minTemp = minTempNight < minTempDay ? minTempNight : minTempDay;
        // 白天,夜间中的最高温度
        int maxTemp = maxTempDay > maxTempNight ? maxTempDay : maxTempNight;
        // 份数(白天,夜间综合温差)
        float parts = maxTemp - minTemp;
        // y轴一端到控件一端的距离
        float length = mSpace + mTextSize + mTextSpace + mRadius;
        // y轴高度
        float yAxisHeight = mHeight - length * 2;
        // 当温度都相同时(被除数不能为0)
        if (parts == 0) {
            for (int i = 0; i < LENGTH; i++) {
                mYAxisDay[i] = yAxisHeight / 2 + length;
                mYAxisNight[i] = yAxisHeight / 2 + length;
            }
        } else {
            float partValue = yAxisHeight / parts;
            for (int i = 0; i < LENGTH; i++) {
                mYAxisDay[i] = mHeight - partValue * (mTempDay[i] - minTemp) - length;
                mYAxisNight[i] = mHeight - partValue * (mTempNight[i] - minTemp) - length;
            }
        }
    }
    /**
     * 画折线图
     *
     * @param canvas 画布
     * @param color  画图颜色
     * @param temp   温度集合
     * @param yAxis  y轴集合
     * @param type   折线种类:0,白天;1,夜间
     */
    private void drawChart(Canvas canvas, int color, int temp[], float[] yAxis, int type) {
        mLinePaint.setColor(color);
        mPointPaint.setColor(color);
        int alpha1 = 102;
        int alpha2 = 255;
        for (int i = 0; i < LENGTH; i++) {
            // 画线
            if (i < LENGTH - 1) {
                mLinePaint.setAlpha(alpha2);
                mLinePaint.setPathEffect(null);
                canvas.drawLine(mXAxis[i], yAxis[i], mXAxis[i + 1], yAxis[i + 1], mLinePaint);
            }
            // 画点
            mPointPaint.setAlpha(alpha2);
            canvas.drawCircle(mXAxis[i], yAxis[i], mRadiusToday, mPointPaint);
            // 画字
            mTextPaint.setAlpha(alpha2);
            drawText(canvas, mTextPaint, i, temp, yAxis, type);
        }
    }
    /**
     * 绘制文字
     *
     * @param canvas    画布
     * @param textPaint 画笔
     * @param i         索引
     * @param temp      温度集合
     * @param yAxis     y轴集合
     * @param type      折线种类:0,白天;1,夜间
     */
    private void drawText(Canvas canvas, Paint textPaint, int i, int[] temp, float[] yAxis, int type) {
        switch (type) {
            case 0:
                // 显示白天气温
                canvas.drawText(temp[i] + "°", mXAxis[i], yAxis[i] - mRadius - mTextSpace, textPaint);
                break;
            case 1:
                // 显示夜间气温
                canvas.drawText(temp[i] + "°", mXAxis[i], yAxis[i] + mTextSpace + mTextSize, textPaint);
                break;
        }
    }
    /**
     * 设置高度,x轴集合
     */
    private void setHeightAndXAxis() {
        mHeight = getHeight();
        // 控件宽
        int width = getWidth();
        // 每一份宽
        float w = width / 16;
        mXAxis[0] = w;
        mXAxis[1] = w * 3;
        mXAxis[2] = w * 5;
        mXAxis[3] = w * 7;
        mXAxis[4] = w * 9;
        mXAxis[5] = w * 11;
        mXAxis[6] = w * 13;
        mXAxis[7] = w * 15;
    }
    /**
     * 设置白天温度
     *
     * @param tempDay 温度数组集合
     */
    public void setTempDay(int[] tempDay) {
        mTempDay = tempDay;
    }
    /**
     * 设置夜间温度
     *
     * @param tempNight 温度数组集合
     */
    public void setTempNight(int[] tempNight) {
        mTempNight = tempNight;
    }
}


WeatherChartViewForecast.java


package com.llw.mvplibrary.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import com.llw.mvplibrary.R;
/**
 * 折线温度双曲线
 */
public class WeatherChartViewForecast extends View {
    /**
     * x轴集合
     */
    private float mXAxis[] = new float[7];
    /**
     * 白天y轴集合
     */
    private float mYAxisDay[] = new float[7];
    /**
     * 夜间y轴集合
     */
    private float mYAxisNight[] = new float[7];
    /**
     * x,y轴集合数
     */
    private static final int LENGTH = 7;
    /**
     * 白天温度集合
     */
    private int mTempMax[] = new int[7];
    /**
     * 夜间温度集合
     */
    private int mTempMin[] = new int[7];
    /**
     * 控件高
     */
    private int mHeight;
    /**
     * 字体大小
     */
    private float mTextSize;
    /**
     * 圓半径
     */
    private float mRadius;
    /**
     * 圓半径今天
     */
    private float mRadiusToday;
    /**
     * 文字移动位置距离
     */
    private float mTextSpace;
    /**
     * 白天折线颜色
     */
    private int mColorDay;
    /**
     * 白天文字颜色
     */
    private int mColorTextDay;
    /**
     * 夜间折线颜色
     */
    private int mColorNight;
    /**
     * 夜间文字颜色
     */
    private int mColorTextNight;
    /**
     * 屏幕密度
     */
    private float mDensity;
    /**
     * 控件边的空白空间
     */
    private float mSpace;
    /**
     * 线画笔
     */
    private Paint mLinePaint;
    /**
     * 点画笔
     */
    private Paint mPointPaint;
    /**
     * 字体画笔
     */
    private Paint mTextPaint;
    public WeatherChartViewForecast(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }
    @SuppressWarnings("deprecation")
    private void init(Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WeatherChartView);
        float densityText = getResources().getDisplayMetrics().scaledDensity;
        mTextSize = a.getDimensionPixelSize(R.styleable.WeatherChartView_textSize,
                (int) (14 * densityText));
        mColorDay = a.getColor(R.styleable.WeatherChartView_dayColor,
                getResources().getColor(R.color.pink));
        mColorNight = a.getColor(R.styleable.WeatherChartView_nightColor,
                getResources().getColor(R.color.blue_one));
        mColorTextDay = a.getColor(R.styleable.WeatherChartView_dayTextColor,
                getResources().getColor(R.color.pink));
        mColorTextNight = a.getColor(R.styleable.WeatherChartView_nightTextColor,
                getResources().getColor(R.color.blue_one));
        mDensity = getResources().getDisplayMetrics().density;
        mRadius = 3 * mDensity;
        mRadiusToday = 5 * mDensity;
        mSpace = 3 * mDensity;
        mTextSpace = 10 * mDensity;
        float stokeWidth = 2 * mDensity;
        mLinePaint = new Paint();
        mLinePaint.setAntiAlias(true);
        mLinePaint.setStrokeWidth(stokeWidth);
        mLinePaint.setStyle(Paint.Style.STROKE);
        mPointPaint = new Paint();
        mPointPaint.setAntiAlias(true);
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
    }
    public WeatherChartViewForecast(Context context) {
        super(context);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mHeight == 0) {
            // 设置控件高度,x轴集合
            setHeightAndXAxis();
        }
        // 计算y轴集合数值
        computeYAxisValues();
        if (mTempMax[0] != 0 && mTempMax[6] != 0) {
            // 画最高温折线图
            drawChart(canvas, mColorDay, mTempMax, mYAxisDay, 0);
        }
        if (mTempMin[0] != 0 && mTempMin[6] != 0) {
            // 画最低温折线图
            drawChart(canvas, mColorNight, mTempMin, mYAxisNight, 1);
        }
    }
    /**
     * 计算y轴集合数值
     */
    private void computeYAxisValues() {
        // 存放最高温中的最低温度
        int minTempDay = mTempMax[0];
        // 存放最高温中的最高温度
        int maxTempDay = mTempMax[0];
        for (int item : mTempMax) {
            if (item < minTempDay) {
                minTempDay = item;
            }
            if (item > maxTempDay) {
                maxTempDay = item;
            }
        }
        // 最低温中的最低温度
        int minTempNight = mTempMin[0];
        // 最低温中的最高温度
        int maxTempNight = mTempMin[0];
        for (int item : mTempMin) {
            if (item < minTempNight) {
                minTempNight = item;
            }
            if (item > maxTempNight) {
                maxTempNight = item;
            }
        }
        // 白天,夜间中的最低温度
        int minTemp = minTempNight < minTempDay ? minTempNight : minTempDay;
        // 白天,夜间中的最高温度
        int maxTemp = maxTempDay > maxTempNight ? maxTempDay : maxTempNight;
        // 份数(白天,夜间综合温差)
        float parts = maxTemp - minTemp;
        // y轴一端到控件一端的距离
        float length = mSpace + mTextSize + mTextSpace + mRadius;
        // y轴高度
        float yAxisHeight = mHeight - length * 2;
        // 当温度都相同时(被除数不能为0)
        if (parts == 0) {
            for (int i = 0; i < LENGTH; i++) {
                mYAxisDay[i] = yAxisHeight / 2 + length;
                mYAxisNight[i] = yAxisHeight / 2 + length;
            }
        } else {
            float partValue = yAxisHeight / parts;
            for (int i = 0; i < LENGTH; i++) {
                mYAxisDay[i] = mHeight - partValue * (mTempMax[i] - minTemp) - length;
                mYAxisNight[i] = mHeight - partValue * (mTempMin[i] - minTemp) - length;
            }
        }
    }
    /**
     * 画折线图
     *
     * @param canvas 画布
     * @param color  画图颜色
     * @param temp   温度集合
     * @param yAxis  y轴集合
     * @param type   折线种类:0,最高温;1,最低温
     */
    private void drawChart(Canvas canvas, int color, int temp[], float[] yAxis, int type) {
        mLinePaint.setColor(color);
        mPointPaint.setColor(color);
        int alpha1 = 102;
        int alpha2 = 255;
        for (int i = 0; i < LENGTH; i++) {
            // 画线
            if (i < LENGTH - 1) {
                mLinePaint.setAlpha(alpha2);
                mLinePaint.setPathEffect(null);
                canvas.drawLine(mXAxis[i], yAxis[i], mXAxis[i + 1], yAxis[i + 1], mLinePaint);
            }
            // 画点
            mPointPaint.setAlpha(alpha2);
            canvas.drawCircle(mXAxis[i], yAxis[i], mRadiusToday, mPointPaint);
            // 画字
            mTextPaint.setAlpha(alpha2);
            drawText(canvas, mTextPaint, i, temp, yAxis, type);
        }
    }
    /**
     * 绘制文字
     *
     * @param canvas    画布
     * @param textPaint 画笔
     * @param i         索引
     * @param temp      温度集合
     * @param yAxis     y轴集合
     * @param type      折线种类:0,白天;1,夜间
     */
    private void drawText(Canvas canvas, Paint textPaint, int i, int[] temp, float[] yAxis, int type) {
        switch (type) {
            case 0:
                // 显示白天气温
                textPaint.setColor(mColorTextDay);
                canvas.drawText(temp[i] + "°", mXAxis[i], yAxis[i] - mRadius - mTextSpace, textPaint);
                break;
            case 1:
                // 显示夜间气温
                textPaint.setColor(mColorTextNight);
                canvas.drawText(temp[i] + "°", mXAxis[i], yAxis[i] + mTextSpace + mTextSize, textPaint);
                break;
        }
    }
    /**
     * 设置高度,x轴集合
     */
    private void setHeightAndXAxis() {
        mHeight = getHeight();
        // 控件宽
        int width = getWidth();
        // 每一份宽
        float w = width / 14;
        mXAxis[0] = w;
        mXAxis[1] = w * 3;
        mXAxis[2] = w * 5;
        mXAxis[3] = w * 7;
        mXAxis[4] = w * 9;
        mXAxis[5] = w * 11;
        mXAxis[6] = w * 13;
    }
    /**
     * 设置最高温度
     * @param tempMax 
     */
    public void setTempMax(int[] tempMax) {
        mTempMax = tempMax;
    }
    /**
     * 设置最低温度
     */
    public void setTempMin(int[] tempMin) {
        mTempMin = tempMin;
    }
}


⑨ 自定义数据列表


自定义View创建好了,页面可不能只有一个折线图,那样太空了,所以还需要一个列表来展示,两个Fragment对应不同的列表样式。


首先创建一个圆角背景


20200612173708916.png


shape_pink_8.xml


<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners android:radius="8dp" />
    <solid android:color="@color/pink" />
</shape>


接下里创建两个不同item布局文件


20200612173825389.png


为了区别于之前创建的item,我特意在中间加了一个hot,表示这是热门城市数据列表的中item布局。


item_weather_forecast_hot_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item_forecast"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="@dimen/dp_12"
    android:background="@drawable/shape_pink_8"
    android:foreground="@drawable/bg_white"
    android:orientation="vertical">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:orientation="vertical"
        android:padding="@dimen/dp_12">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView
                android:id="@+id/tv_day"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="今天"
                android:textColor="@color/white" />
            <TextView
                android:id="@+id/tv_date"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="日期"
                android:textColor="@color/white" />
        </LinearLayout>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView
                android:id="@+id/tv_tem_max_min"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="最高温/最低温"
                android:textColor="@color/white"
                android:textSize="@dimen/sp_40" />
            <TextView
                android:text="°"
                android:textColor="@color/white"
                android:textSize="@dimen/sp_40"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"/>
            <TextView
                android:id="@+id/tv_wind_dir_and_sc"
                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
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginRight="@dimen/dp_6"
                    android:text="紫外线"
                    android:textColor="@color/pink_one"
                    android:textSize="@dimen/sp_14" />
                <TextView
                    android:id="@+id/tv_uv_index"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="紫外线"
                    android:textColor="@color/white"
                    android:textSize="@dimen/sp_14" />
            </LinearLayout>
            <LinearLayout
                android:layout_marginLeft="@dimen/dp_6"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginRight="@dimen/dp_6"
                    android:text="相对湿度"
                    android:textColor="@color/pink_one"
                    android:textSize="@dimen/sp_14" />
                <TextView
                    android:id="@+id/tv_hum"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="相对湿度"
                    android:textColor="@color/white"
                    android:textSize="@dimen/sp_14" />
            </LinearLayout>
            <LinearLayout
                android:layout_marginLeft="@dimen/dp_6"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content">
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginRight="@dimen/dp_6"
                    android:text="大气压强"
                    android:textColor="@color/pink_one"
                    android:textSize="@dimen/sp_14" />
                <TextView
                    android:id="@+id/tv_pres"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="大气压强"
                    android:textColor="@color/white"
                    android:textSize="@dimen/sp_14" />
            </LinearLayout>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>


item_weather_hourly_hot_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:paddingLeft="@dimen/dp_16"
    android:paddingRight="@dimen/dp_16"
    android:paddingTop="@dimen/dp_8"
    android:paddingBottom="@dimen/dp_8"
    android:background="@drawable/shape_pink_8"
    android:gravity="center_vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <!--时间-->
    <TextView
        android:id="@+id/tv_time"
        android:textColor="#FFF"
        android:text="上午10:00"
        android:textSize="14sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <!--气候图标-->
    <LinearLayout
        android:gravity="center"
        android:layout_width="@dimen/dp_0"
        android:layout_height="wrap_content"
        android:layout_weight="1">
        <ImageView
            android:id="@+id/iv_weather_state"
            android:background="@mipmap/icon_100"
            android:layout_width="30dp"
            android:layout_height="30dp"/>
    </LinearLayout>
    <!--温度-->
    <TextView
        android:textSize="20sp"
        android:id="@+id/tv_temperature"
        android:textColor="#FFF"
        android:text="25℃"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>


两个item布局都写好了,接下来就是修改两个Fragment中绑定的布局了,之前他们里面都是空的。


fragment_today.xml

<?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"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <com.llw.mvplibrary.view.WeatherChartView
                android:layout_marginTop="@dimen/dp_20"
                android:id="@+id/line_char"
                android:layout_width="match_parent"
                android:layout_height="@dimen/dp_100"
                android:layout_centerInParent="true"
                app:dayColor="@color/pink"
                app:nightColor="@color/blue_one"
                app:textColor="@color/pink"
                app:textSize="14sp"/>
            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/rv_hourly"
                android:layout_margin="@dimen/dp_16"
                android:background="@drawable/shape_pink_8"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
        </LinearLayout>
    </androidx.core.widget.NestedScrollView>
</LinearLayout>


fragment_forecast.xml

<?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:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent">
    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <com.llw.mvplibrary.view.WeatherChartViewForecast
                android:layout_marginTop="@dimen/dp_20"
                android:id="@+id/line_char"
                android:layout_width="match_parent"
                android:layout_height="@dimen/dp_160"
                android:layout_centerInParent="true"
                app:dayColor="@color/pink"
                app:nightColor="@color/blue_one"
                app:dayTextColor="@color/pink"
                app:nightTextColor="@color/blue_one"
                app:textSize="14sp"/>
            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/rv_forecast"
                android:layout_margin="@dimen/dp_16"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
        </LinearLayout>
    </androidx.core.widget.NestedScrollView>
</LinearLayout>


现在布局写好了,可以写代码了,这里会用到两个列表适配器,其中WeatherHourlyAdapter可以复用,不需要改动,但是另一个是需要重新写的。


WeatherForecastHotAdapter.java


package com.llw.goodweather.adapter;
import android.graphics.Typeface;
import android.widget.ImageView;
import android.widget.TextView;
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.WeatherResponse;
import com.llw.goodweather.utils.DateUtils;
import com.llw.goodweather.utils.WeatherUtil;
import java.util.List;
/**
 * 热门城市天气预报列表展示适配器
 */
public class WeatherForecastHotAdapter extends BaseQuickAdapter<WeatherResponse.HeWeather6Bean.DailyForecastBean, BaseViewHolder> {
    public WeatherForecastHotAdapter(int layoutResId, @Nullable List<WeatherResponse.HeWeather6Bean.DailyForecastBean> data) {
        super(layoutResId, data);
    }
    @Override
    protected void convert(BaseViewHolder helper, WeatherResponse.HeWeather6Bean.DailyForecastBean item) {
        helper.setText(R.id.tv_date, item.getDate())//日期
                .setText(R.id.tv_day, DateUtils.Week(item.getDate()))
                .setText(R.id.tv_wind_dir_and_sc, item.getWind_dir() + item.getWind_sc() + "级")
                .setText(R.id.tv_uv_index, uvIndex(Integer.parseInt(item.getUv_index())))//紫外线强度
                .setText(R.id.tv_hum, item.getHum() + "%")//相对湿度
                .setText(R.id.tv_pres, item.getPres() + "hPa")//大气压强
        ;
        //最低温和最高温
        TextView minAndMax = helper.getView(R.id.tv_tem_max_min);
        minAndMax.setText(item.getTmp_min() + "/" + item.getTmp_max());
        Typeface typeface = Typeface.createFromAsset(mContext.getAssets(), "fonts/Roboto-Light.ttf");
        minAndMax.setTypeface(typeface);
        helper.addOnClickListener(R.id.item_forecast);//绑定点击事件的id
    }
    //紫外线等级
    private String uvIndex(int level) {//最弱、弱、中等、强、很强
        String result = null;
        if (level <= 2) {
            result = "最弱";
        } else if (level > 2 && level < 5) {
            result = "弱";
        } else if (level > 4 && level < 7) {
            result = "中等";
        } else if (level > 6 && level < 10) {
            result = "强";
        } else {
            result = "很强";
        }
        return result;
    }
}


那么现在可以在Fragment里面做数据的渲染展示了。


首先是TodayFragment

  @BindView(R.id.line_char)
    WeatherChartView lineChar;//折线图
    @BindView(R.id.rv_hourly)
    RecyclerView rvHourly;//逐三小时天气列表
    List<WeatherResponse.HeWeather6Bean.HourlyBean> mList = new ArrayList<>();
    WeatherHourlyAdapter mAdapter;


  @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(TodayHourlyEvent event) {//接收
        List<WeatherResponse.HeWeather6Bean.HourlyBean> data = new ArrayList<>();
        data.addAll(event.mHourlyBean);
        Log.i("dayArray", data.get(0).getCond_txt());
        int[] dayArray = new int[data.size()];
        for (int i = 0; i < data.size(); i++) {
            dayArray[i] = Integer.parseInt(event.mHourlyBean.get(i).getTmp());
        }
        Log.i("dayArray", dayArray.toString());
        lineChar.setTempDay(dayArray);
        lineChar.invalidate();
        //列表
        initList(data);
    }
    private void initList(List<WeatherResponse.HeWeather6Bean.HourlyBean> data) {
        mAdapter = new WeatherHourlyAdapter(R.layout.item_weather_hourly_hot_list, mList);
        rvHourly.setLayoutManager(new LinearLayoutManager(context));
        rvHourly.setAdapter(mAdapter);
        mList.clear();
        mList.addAll(data);
        mAdapter.notifyDataSetChanged();
    }


将里面的Toast去掉,进行业务处理即可


然后是ForecastFragment


  @BindView(R.id.line_char)
    WeatherChartViewForecast lineChar;//折线图
    @BindView(R.id.rv_forecast)
    RecyclerView rvForecast;//未来七天天气预报
    List<WeatherResponse.HeWeather6Bean.DailyForecastBean> mList = new ArrayList<>();
    WeatherForecastHotAdapter mAdapter;


  @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(ForecastEvent event) {//接收
        List<WeatherResponse.HeWeather6Bean.DailyForecastBean> data = new ArrayList<>();
        data.addAll(event.mForecastBean);
        int[] maxArray = new int[data.size()];
        int[] minArray = new int[data.size()];
        for (int i = 0; i < data.size(); i++) {
            maxArray[i] = Integer.parseInt(event.mForecastBean.get(i).getTmp_max());
            minArray[i] = Integer.parseInt(event.mForecastBean.get(i).getTmp_min());
        }
        lineChar.setTempMax(maxArray);
        lineChar.setTempMin(minArray);
        lineChar.invalidate();
        //列表数据
        initList(data);
    }
    private void initList(List<WeatherResponse.HeWeather6Bean.DailyForecastBean> data) {
        mAdapter = new WeatherForecastHotAdapter(R.layout.item_weather_forecast_hot_list,mList);
        rvForecast.setLayoutManager(new LinearLayoutManager(context));
        rvForecast.setAdapter(mAdapter);
        mList.clear();
        mList.addAll(data);
        mAdapter.notifyDataSetChanged();
    }


OK,再运行一下


20200612155244103.gif



相关文章
|
1月前
|
XML Java 数据库
安卓项目:app注册/登录界面设计
本文介绍了如何设计一个Android应用的注册/登录界面,包括布局文件的创建、登录和注册逻辑的实现,以及运行效果的展示。
118 0
安卓项目:app注册/登录界面设计
|
2月前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
107 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
2月前
|
存储 开发工具 Android开发
使用.NET MAUI开发第一个安卓APP
【9月更文挑战第24天】使用.NET MAUI开发首个安卓APP需完成以下步骤:首先,安装Visual Studio 2022并勾选“.NET Multi-platform App UI development”工作负载;接着,安装Android SDK。然后,创建新项目时选择“.NET Multi-platform App (MAUI)”模板,并仅针对Android平台进行配置。了解项目结构,包括`.csproj`配置文件、`Properties`配置文件夹、平台特定代码及共享代码等。
123 2
|
2月前
|
XML Android开发 数据格式
🌐Android国际化与本地化全攻略!让你的App走遍全球无障碍!🌍
在全球化背景下,实现Android应用的国际化与本地化至关重要。本文以一款旅游指南App为例,详细介绍如何通过资源文件拆分与命名、适配布局与方向、处理日期时间及货币格式、考虑文化习俗等步骤,完成多语言支持和本地化调整。通过邀请用户测试并收集反馈,确保应用能无缝融入不同市场,提升用户体验与满意度。
85 3
|
1月前
|
安全 网络安全 Android开发
深度解析:利用Universal Links与Android App Links实现无缝网页至应用跳转的安全考量
【10月更文挑战第2天】在移动互联网时代,用户经常需要从网页无缝跳转到移动应用中。这种跳转不仅需要提供流畅的用户体验,还要确保安全性。本文将深入探讨如何利用Universal Links(仅限于iOS)和Android App Links技术实现这一目标,并分析其安全性。
169 0
|
2月前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android应用开发中的多线程编程,涵盖基本概念、常见实现方式及最佳实践。主要内容包括主线程与工作线程的作用、多线程的多种实现方法(如 `Thread`、`HandlerThread`、`Executors` 和 Kotlin 协程),以及如何避免内存泄漏和合理使用线程池。通过有效的多线程管理,可以显著提升应用性能和用户体验。
67 10
|
2月前
|
XML 数据库 Android开发
10分钟手把手教你用Android手撸一个简易的个人记账App
该文章提供了使用Android Studio从零开始创建一个简单的个人记账应用的详细步骤,包括项目搭建、界面设计、数据库处理及各功能模块的实现方法。
|
21天前
|
JSON 小程序 JavaScript
uni-app开发微信小程序的报错[渲染层错误]排查及解决
uni-app开发微信小程序的报错[渲染层错误]排查及解决
328 7
|
21天前
|
小程序 JavaScript 前端开发
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
402 1
|
7天前
|
小程序 数据挖掘 UED
开发1个上门家政小程序APP系统,都有哪些功能?
在快节奏的现代生活中,家政服务已成为许多家庭的必需品。针对传统家政服务存在的问题,如服务质量不稳定、价格不透明等,我们历时两年开发了一套全新的上门家政系统。该系统通过完善信用体系、提供奖励机制、优化复购体验、多渠道推广和多样化盈利模式,解决了私单、复购、推广和盈利四大痛点,全面提升了服务质量和用户体验,旨在成为家政行业的领导者。