【比你想的简单很多!从0开始完成一款App】8.构建主页(2)

个人博客CoorChice,https://chenbingx.github.io/ ,最新文章将会首发CoorChice的博客,欢迎探索哦 !
同时,搜索CoorChice关注我的微信公众号,同期文章也将会优先推送到公众号中,以提醒您有新鲜文章出炉。亦可在文章末尾扫描二维码关注。

【比你想的简单很多!从0开始完成一款App】8.构建主页(2)_第1张图片
封面.jpg

本系列文章列表

由于最近有点忙,所以这个系列的文章最近都没时间更新,断片的同学可以再看看前面的回顾一下,主要需要把该项目的结构设计回顾一下。
好了,言归正传。截止上一篇,我们已经把主页的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。
首先看一下效果。

【比你想的简单很多!从0开始完成一款App】8.构建主页(2)_第2张图片
RecyclerView效果

上一篇我已经展示了我们RecyclerView使用的是LinearLayoutManager布局管理器,因为其实这个结构也并复杂,我把它分成上下两个部分。

  • 上半部分是一条条简略的天气信息。
上半部分

item_future_weather_info.xml




  

  

  

  


  • 下半部分

item_today_detail_info.xml

【比你想的简单很多!从0开始完成一款App】8.构建主页(2)_第3张图片
下半部分



  

  

  

  

    

      

      

      

      

      

      
    

    

      

      

      

      

      

      

    

  


  • 下面看看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

好了,现在我们已经完成了效果图的需求。这个项目最重要的几个部分已经基本完成了。后面就是迭代优化了。

项目地址GitHub

【比你想的简单很多!从0开始完成一款App】8.构建主页(2)_第4张图片
CoorChice的公众号

你可能感兴趣的:(【比你想的简单很多!从0开始完成一款App】8.构建主页(2))