前言
反正国内的天气也做好了,不妨做一下做一下国外的天气,国外的天气数据的展示,我将重新设计一套UI,区别于国内的天气UI,开搞~
运行效果图
正文
既然要访问国外的天气,那么肯定不能像国内这样,省、市、区/县这样来详细展示,所以只展示热门的城市,因为我相信那些去国外玩的朋友肯定也不会去一个不知名的地方吧,不然回来之后你怎么炫耀呢?
① API地址
和风天气有这方面的API,白嫖侠申请出战!地址如下
https://search.heweather.net/top?key=3086e91d66c04ce588a7f538f917c7f4&group=overseas&number=50&lang=zh
可以用浏览器测试一下
然后返回数据生成一个实体bean
在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
/** * 海外热门城市 */ @GET("/top?key=3086e91d66c04ce588a7f538f917c7f4&group=overseas&number=50&lang=zh") Call<HotCityResponse> hotCity();
② 热门城市订阅器
在contract包下创建HotCityContract
代码如下:
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并设计布局
接下来就可以设计布局,再此之前,要放一些需要的东西,
首先在mvplibrary里增加一些颜色
<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><!--浅粉色-->
然后是图标,一个小飞机图片
下面是图片
上面是图片
因为是白色,你看不见很正常,保存到本地就可以了。
然后是样式,一个渐变的圆角背景
在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
效果如下
代码如下
<?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
效果如下
代码如下:
<?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
代码如下:
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里去使用了
@BindView(R.id.toolbar) Toolbar toolbar;//标题bar @BindView(R.id.rv) RecyclerView rv;//列表 List<HotCityResponse.HeWeather6Bean.BasicBean> mList = new ArrayList<>(); HotCityAdapter mAdapter;
连接之前创建好的订阅,需要实现五个方法。
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
增加的代码如下:
<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方法,增加
然后运行起来
数据就加载出来了。
⑤ 热门城市的天气信息展示
这里就需要创建一个新的页面了,因为我希望区别于MainActivity的样式。
在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>
然后创建一个订阅器
代码如下:
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页面的代码
首先衔接这个订阅器
然后绑定控件
@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下
然后要进入热门城市的天气页面还得在列表页面做item的点击事件处理才行,打开HotCityActivity
Intent intent = new Intent(context,HotCityWeatherActivity.class); intent.putExtra("location",mList.get(position).getLocation()); startActivity(intent);
运行一下
⑥ TabLayout+ViewPager+Fragment使用
然后要加入TabLayout和ViewPager,通过viewPager来切换不同的Fragment,这里我添加了一个第三方的库,效果比较好,微博的顶部TabLayout切换的效果和这个是一样的。
在mvplibrary下的build.gradle的dependencies中加入如下图的依赖
//蠕虫蠕动动画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布局
<!--动画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和两个布局文件
我特别新建了一个fragment包来存放ForecastFragment和TodayFragment。然后是对应的布局文件
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了
运行一下
这个时候你就可以左右滑动或者是点击tab来切换不同的Fragment
⑦ Activity和Fragment通讯
这里我们使用EventBus来进行通讯,主要原因就是代码量少,页面简洁
在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方法中传递这两个事件
//传递数据到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下面的。第一步当然是创建样式了。
这应该一目了然吧。
样式代码:
<!--折线图--> <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>
这里创建了两个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对应不同的列表样式。
首先创建一个圆角背景
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布局文件
为了区别于之前创建的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,再运行一下