美文网首页程序员Android开发安卓资源收集
【比你想的简单很多!从0开始完成一款App】8.构建主页(2)

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

作者: CoorChice | 来源:发表于2016-12-17 21:13 被阅读333次

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

    封面.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。
    首先看一下效果。

    RecyclerView效果

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

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

    item_future_weather_info.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:tools="http://schemas.android.com/tools"
      android:layout_width="match_parent"
      android:layout_height="40dp"
      android:orientation="horizontal"
      tools:background="@color/opacity_9_yellow"
      >
    
      <TextView
        android:id="@+id/week"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:gravity="center"
        tools:text="星期日"
        android:layout_marginLeft="10dp"
        android:textColor="@color/white"
        />
    
      <ImageView android:id="@+id/weather_pic"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/sun"
        android:padding="10dp"
        />
    
      <TextView
        android:id="@+id/low_temperature"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_alignParentRight="true"
        android:gravity="center"
        tools:text="-7"
        android:layout_marginRight="10dp"
        android:textColor="@color/gray"
        />
    
      <TextView
        android:id="@+id/high_temperature"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true"
        android:layout_toStartOf="@+id/low_temperature"
        android:layout_marginRight="15dp"
        android:gravity="center"
        android:textColor="@color/white"
        tools:text="10"/>
    
    </RelativeLayout>
    
    • 下半部分

    item_today_detail_info.xml

    下半部分
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:tools="http://schemas.android.com/tools"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:background="@color/transparent"
      android:orientation="vertical"
      tools:background="@color/black"
      >
    
      <include layout="@layout/line_white"/>
    
      <TextView
        android:id="@+id/chuanyi"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@color/white"
        tools:text="天气冷,建议着棉服、羽绒服、皮夹克加羊毛衫等冬季服装。年老体弱者宜着厚棉衣、冬大衣或厚羽绒服。"
        android:padding="10dp"
        />
    
      <include layout="@layout/line_white"/>
    
      <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal"
        android:padding="10dp"
        >
    
        <LinearLayout
          android:layout_width="0dp"
          android:layout_height="match_parent"
          android:layout_weight="1"
          android:orientation="vertical"
          android:paddingRight="10dp"
          >
    
          <TextView
            android:id="@+id/update_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:text="更新时间:"
            android:textColor="@color/white"
            />
    
          <TextView
            android:id="@+id/moon"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:text="农历:"
            android:textColor="@color/white"
            />
    
          <TextView
            android:id="@+id/wind_direct"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_marginTop="10dp"
            android:text="风向:"
            android:textColor="@color/white"
            />
    
          <TextView
            android:id="@+id/wind_power"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:text="等级:"
            android:textColor="@color/white"
            />
    
          <TextView
            android:id="@+id/air_quality_index"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_marginTop="10dp"
            android:text="空气质量指数:"
            android:textColor="@color/white"
            />
    
          <TextView
            android:id="@+id/air_quality"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:text="空气质量:"
            android:textColor="@color/white"
            />
        </LinearLayout>
    
        <LinearLayout
          android:layout_width="0dp"
          android:layout_height="match_parent"
          android:layout_weight="1"
          android:orientation="vertical"
          >
    
          <TextView
            android:id="@+id/update_time_data"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left"
            android:paddingLeft="10dp"
            android:textColor="@color/white"
            tools:text="2016年12月05日16时"
            />
    
          <TextView
            android:id="@+id/moon_data"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left"
            android:paddingLeft="10dp"
            android:textColor="@color/white"
            tools:text="十一月初七"
            />
    
          <TextView
            android:id="@+id/wind_direct_data"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left"
            android:paddingLeft="10dp"
            android:paddingTop="10dp"
            android:textColor="@color/white"
            tools:text="西北风"
            />
    
          <TextView
            android:id="@+id/wind_power_data"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left"
            android:paddingLeft="10dp"
            android:textColor="@color/white"
            tools:text="4级"
            />
    
          <TextView
            android:id="@+id/air_quality_index_data"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left"
            android:paddingLeft="10dp"
            android:paddingTop="10dp"
            android:textColor="@color/white"
            tools:text="38"
            />
    
          <TextView
            android:id="@+id/air_quality_data"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="left"
            android:paddingLeft="10dp"
            android:textColor="@color/white"
            tools:text="优"
            />
    
        </LinearLayout>
    
      </LinearLayout>
    
    </LinearLayout>
    
    • 下面看看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<Weather> 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看这一部分代码。

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

    项目地址GitHub

    CoorChice的公众号

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/aahamttx.html