个人博客CoorChice,https://chenbingx.github.io/ ,最新文章将会首发CoorChice的博客,欢迎探索哦 !
同时,搜索CoorChice
关注我的微信公众号,同期文章也将会优先推送到公众号中,以提醒您有新鲜文章出炉。亦可在文章末尾扫描二维码关注。
本系列文章列表
由于最近有点忙,所以这个系列的文章最近都没时间更新,断片的同学可以再看看前面的回顾一下,主要需要把该项目的结构设计回顾一下。
好了,言归正传。截止上一篇,我们已经把主页的Activity完成了,里面嵌套的Fragment的布局也完成了。下面我们需要一起来完成Fragment的Presenter和Model,以及Fragment中用于展示详细信息的RecyclerView的Adapter。完成这些,就可以看到我们预期的效果了。
Model模块
由于同属于天气请求,并且业务逻辑差别不大,所以我们可以直接把该Fragment的Model业务整合到之前的WeatherDataModel 中。
在这个Fragment中我们需要根据城市名称来获取相应的天气数据,当与定位城市相同时,我们就可以直接使用之前缓存好的数据,否则请求新的数据。
- 在WeatherDataModelApi中增加一个接口,因为功能类似,所以我们直接重载之前的接口就好。
void requestWeatherData(String cityName); //根据城市名称获取天气数据
- 相应的需要在WeatherDataModel实现上面接口的逻辑。
@Override
public void requestWeatherData(String cityName) {
WeatherData data;
if (isGetCacheData(cityName)){ //先判断传入的名字是否和定位城市名字一样。
getCacheData(); //一样,获取缓存的天气数据
} else {
getWeatherData(cityName); //不一样,从网络请求新数据
}
}
private boolean isGetCacheData(String cityName) {
//这里我确定的规则是,当传入null、“”或定位城市名,都按定位城市名处理
return cityName == null || TextUtils.isEmpty(cityName)
|| DataCache.getInstance().getCurrentLocation().getCity().contains(cityName);
}
private void getCacheData() {
WeatherData data;
data = DataCache.getInstance().getWeatherData(); //从缓存中取出定位城市天气数据
if (requestWeatherDataListener != null) {
//这是一个回调,待会儿咱们再来聊聊这个回调
requestWeatherDataListener.onRequestWeatherDataSuccess(data); // 请求成功
}
}
private void getWeatherData(String cityname) {
//根据城市名称请求数据
ApiClient.getWeatherData(cityname, data -> {
LogUtils.i("requestWeatherData: " + GsonUtils.getSingleInstance().toJson(data));
if (requestWeatherDataListener != null) {
requestWeatherDataListener.onRequestWeatherDataSuccess(data); // 请求成功
}
}, message -> {
if (requestWeatherDataListener != null) {
requestWeatherDataListener.onRequestWeatherDataFailure(message);
}
});
}
好了现在我们已经可以在Presenter中调用Model的方法了,但是由于数据请求是异步完成,所以我们使用返回值的方式返回数据很不方便,于是回调机制就再次需要被使用。
- 因为我们的Presenter只持有Model的接口,所以需要把添加回调的方法定义在WeatherDataModelApi中。其次,由于是专用回调,干脆把监听回调也写到其中。
void setRequestWeatherDataListener(RequestWeatherDataListener requestWeatherDataListener); //设置回调的接口
// 因为每个Model可能需要请求多个接口,所以每个回调可能不同,就把它写到内部了
interface RequestWeatherDataListener {
//成功获取数据的回调
void onRequestWeatherDataSuccess(WeatherData data);
//获取数据失败的回调
void onRequestWeatherDataFailure(String message);
}
- 接下来,自然是要在Model中实现一下setRequestWeatherDataListener() 。就是一个普通的set方法,我就不展示。至于回调的调用时机,在上面的代码中已经包含了。
Presenter模块
完成了Model模块就可以接着来编写Presenter模块了。
- 首先自然是要先写接口喽WeatherDetailFragmentPresenterApi
public interface WeatherDetailFragmentPresenterApi extends BasePresenter {
void getWeatherData(String cityName);
}
- 接着完成WeatherDetailFragmentPresenter,上个完整的。可以看到,只要按照约定好的结构填内容就行。记住要依赖抽象,不要依赖具体。
public class WeatherDetailFragmentPresenter implements WeatherDetailFragmentPresenterApi, WeatherDataModelApi.RequestWeatherDataListener {
private WeatherDetailFragmentView view;
private WeatherDataModelApi model;
public WeatherDetailFragmentPresenter(WeatherDetailFragmentView view) {
this.view = view;
model = new WeatherDataModel();
//我们在这里把回调添加了
model.setRequestWeatherDataListener(this);
}
public void getWeatherData(String cityName){
model.requestWeatherData(cityName); //请求数据
}
@Override
public void onRequestWeatherDataSuccess(WeatherData data) {
view.onWeatherDataUpdate(data); //通知View更新UI
}
@Override
public void onRequestWeatherDataFailure(String message) {
}
@Override
public void destroy() {
//记得销毁时把对象显示的释放一下
model.setRequestWeatherDataListener(null);
model = null;
view = null;
}
}
Adapter
接下来看看比较重要的一个对象,Adapter。
首先看一下效果。
上一篇我已经展示了我们RecyclerView使用的是LinearLayoutManager布局管理器,因为其实这个结构也并复杂,我把它分成上下两个部分。
- 上半部分是一条条简略的天气信息。
item_future_weather_info.xml
- 下半部分
item_today_detail_info.xml
- 下面看看Adapter,主要就是用getItemViewType() 区分一下Item的类型就行
public class FutureWeathersAdapter extends RecyclerView.Adapter {
public static final int WEATHER_ITEM = 1;
public static final int TODAY_INFO_ITEM = 3;
private Context mContext;
private List weathers = new ArrayList<>();
private WeatherData.Data data;
public FutureWeathersAdapter(Context mContext, WeatherData.Data data) {
this.mContext = mContext;
this.data = data;
this.weathers.addAll(data.getWeather());
//为了避免在其它地方错误的数据操作,这里采用addAll()的方法来添加数据到Adapter中专门的集合
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == WEATHER_ITEM) { //天气简略信息
return new BaseItemViewHolder(new FutureWeatherItem(mContext));
} else if(viewType == TODAY_INFO_ITEM){ //今天的详细信息
return new BaseItemViewHolder(new TodayDetailInfoItem(mContext));
} else {
return null;
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (getItemViewType(position) == WEATHER_ITEM){
((FutureWeatherItem)holder.itemView).setData(weathers.get(position), position);
} else if (getItemViewType(position) == TODAY_INFO_ITEM){
((TodayDetailInfoItem)holder.itemView).setData(data, position);
}
}
@Override
public int getItemViewType(int position) {
if (position < weathers.size()){
return WEATHER_ITEM;
} else if (position == weathers.size()){
return TODAY_INFO_ITEM;
} else {
return 0;
}
}
@Override
public int getItemCount() {
return weathers.size() + 1;
}
public void updateData(WeatherData.Data data){
this.data = data;
this.weathers.clear();
this.weathers.addAll(data.getWeather());
notifyDataSetChanged();
}
}
ItemView通过BaseItemViewHolder 创建,就是填充数据,没什么特别的。具体的请异步GitHub看这一部分代码。
- FutureWeatherItem
- TodayDetailInfoItem
好了,现在我们已经完成了效果图的需求。这个项目最重要的几个部分已经基本完成了。后面就是迭代优化了。