【比你想的簡(jiǎn)單很多!從0開(kāi)始完成一款A(yù)pp】8.構(gòu)建主頁(yè)(1)

個(gè)人博客CoorChice躲撰,https://chenbingx.github.io/ 针贬,最新文章將會(huì)首發(fā)CoorChice的博客,歡迎探索哦 !
同時(shí)拢蛋,搜索CoorChice關(guān)注我的微信公眾號(hào)桦他,同期文章也將會(huì)優(yōu)先推送到公眾號(hào)中,以提醒您有新鮮文章出爐谆棱。亦可在文章末尾掃描二維碼關(guān)注快压。

封面.jpg

本系列文章列表

截止上一篇圆仔,我們已經(jīng)完成了歡迎頁(yè),并且成功的請(qǐng)求到了定位地點(diǎn)的天氣數(shù)據(jù)蔫劣,并且緩存起來(lái)坪郭。本篇我們將開(kāi)始構(gòu)建我們的核心主頁(yè),它用于展示天氣數(shù)據(jù)脉幢。

主頁(yè)需求

  • 能夠展示未來(lái)幾天的天氣截粗;
  • 能夠展示當(dāng)日的詳細(xì)天氣情況;
  • 能夠展示多地天氣鸵隧。

暫時(shí)先這幾條绸罗,我們后面還可以再添加。現(xiàn)在我們開(kāi)始著手實(shí)現(xiàn)這幾個(gè)需求豆瘫。

實(shí)現(xiàn)需求

根據(jù)以上需求珊蟀,我們不但需要展示一天的詳細(xì)數(shù)據(jù)和未來(lái)幾天的天氣數(shù)據(jù),還要具備展示多地天氣數(shù)據(jù)外驱。所以大概可以設(shè)計(jì)一下育灸,使用列表控件 來(lái)展示一個(gè)地區(qū)的詳細(xì)數(shù)據(jù),使用ViewPager 來(lái)展示不同地區(qū)的詳細(xì)數(shù)據(jù)昵宇。話不多說(shuō)磅崭,來(lái)看看這樣的效果圖。

效果圖

主要xml文件

這個(gè)頁(yè)面很簡(jiǎn)單瓦哎,它由Activity和Fragment構(gòu)成砸喻。Fragments被放到了ViewPager中。
下面來(lái)看看各個(gè)布局文件蒋譬。

Activity.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:id="@+id/activity_main"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="@color/themeBlueDeep"
  android:clipChildren="false"
  android:gravity="center"
  tools:background="@color/themeBlueDeep"
  tools:context="com.chenbing.oneweather.View.activitys.MainActivity"
  >

//用來(lái)容納Fragment
  <android.support.v4.view.ViewPager
    android:id="@+id/pager_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_above="@+id/bottom_opration"
    android:layout_alignParentStart="true"
    android:background="@color/transparent"
    tools:background="@color/opacity_7_white"
    />


//后期會(huì)在底部添加操作欄割岛,所以先留出位置。
  <RelativeLayout
    android:id="@+id/bottom_opration"
    android:layout_width="match_parent"
    android:layout_height="46dp"
    android:background="@color/transparent"
    tools:background="@color/opacity_5_red"
    android:layout_alignParentBottom="true"
    android:layout_alignParentStart="true">

    <include layout="@layout/line_white"/>


    <ImageView
      android:id="@+id/right_button"
      android:layout_width="wrap_content"
      android:layout_height="match_parent"
      android:padding="8dp"
      tools:src="@drawable/sun"
      />

    <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:layout_toLeftOf="@+id/left_button"
      android:layout_toRightOf="@+id/right_button"
      >

    </LinearLayout>

    <ImageView
      android:id="@+id/left_button"
      android:layout_width="wrap_content"
      android:layout_height="match_parent"
      android:layout_alignParentRight="true"
      android:padding="8dp"
      tools:src="@drawable/sun"
      />
  </RelativeLayout>
</RelativeLayout>

Fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
  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"
  tools:background="@color/colorPrimary"
  >

  <LinearLayout android:id="@+id/header"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    >

    <TextView
      android:id="@+id/city_name"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginTop="60dp"
      android:gravity="center"
      android:textColor="@color/white"
      android:textSize="35sp"
      tools:text="北京市"
      />

    <TextView
      android:id="@+id/weather_info"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginTop="5dp"
      android:gravity="center"
      android:textColor="@color/white"
      android:textSize="14sp"
      tools:text="多云"
      />

    <TextView
      android:id="@+id/air_quality"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginTop="2dp"
      android:gravity="center"
      android:textColor="@color/white"
      android:textSize="14sp"
      tools:text="空氣質(zhì)量:危害健康"
      />

    <TextView
      android:id="@+id/temperature"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginTop="0dp"
      android:gravity="center_horizontal"
      android:textColor="@color/white"
      android:textSize="80sp"
      tools:text="3°"/>
  </LinearLayout>

//用來(lái)裝未來(lái)天氣數(shù)據(jù)和當(dāng)日詳細(xì)信息
  <android.support.v7.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    android:paddingTop="270dp"
    tools:listitem="@layout/item_future_weather_info"
    />
</FrameLayout>

Activity

這個(gè)Activity的結(jié)構(gòu)和之前的歡迎頁(yè)差不多犯助。

View模塊

首先創(chuàng)建MainActivity的接口癣漆。

public interface MainActivityView extends MvpView {

}

繼承接口,創(chuàng)建Activity剂买。

public class MainActivity extends BaseActivity implements MainActivityView {

  @BindView(R.id.pager_container)
  ViewPager pagerContainer;

  private MainActivityPresenterApi presenter;  //依賴(lài)Presenter的抽象
  private List<BaseFragment> fragments = new ArrayList<>(); //這個(gè)數(shù)組用來(lái)裝每個(gè)地區(qū)的天氣頁(yè)面

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    presenter = new MainActivityPresenter(this);
    ButterKnife.bind(this);
    initData();
    initView();
    addListener();
  }

  @Override
  protected void initData() {
    fragments.add(WeatherDetailFragment.newInstance(null));  //創(chuàng)建Fragment
    //這里之所以穿Null是因?yàn)槲以诤竺嫣幚頃r(shí)惠爽,把默認(rèn)天氣數(shù)據(jù)設(shè)置為定位地區(qū)的
  }

  @Override
  protected void initView() {
    setWindowProperties();
    //設(shè)置ViewPager的適配器
    pagerContainer.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
      @Override
      public Fragment getItem(int position) {
        return fragments.get(position);
      }

      @Override
      public int getCount() {
        return fragments.size();
      }
    });

    pagerContainer.setPageTransformer(false, new ZoomOutPageTransformer());
  }

  private void setWindowProperties() {
    // 實(shí)現(xiàn)透明狀態(tài)欄
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
        | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
    getWindow().setFormat(PixelFormat.TRANSPARENT);
  }

  @Override
  protected void addListener() {

  }

  @Override
  protected BasePresenter getPresenter() {
    return presenter;
  }
}

Presenter模塊

由于目前Activity本身并不處理什么任務(wù),所以Presenter中還沒(méi)內(nèi)容瞬哼,但是我們?nèi)稳灰凑占軜?gòu)寫(xiě)婚肆,因?yàn)楹竺婵赡軙?huì)添加?xùn)|西進(jìn)去。

//Presenter接口
public interface MainActivityPresenterApi extends BasePresenter {

}

//Presenter實(shí)現(xiàn)倒槐,因?yàn)闀簳r(shí)不處理事物旬痹,所以先不寫(xiě)Model了
public class MainActivityPresenter implements MainActivityPresenterApi {

  private MainActivityView view;

  public MainActivityPresenter(MainActivityView view) {
    this.view = view;
  }

  @Override
  public void destroy() {
    view = null;
  }
}

Fragment

上面代碼中可以看到,我們已經(jīng)創(chuàng)建了一個(gè)Fragment讨越,下面就來(lái)看看這個(gè)Fragment是什么樣的两残。
Fragment同樣需要按照MVP模式來(lái),首先需要改造一下BaseFragment把跨。

public abstract class BaseFragment extends Fragment {

  abstract protected void initData();

  abstract protected void initView();

  abstract protected void addListener();

  /**
   * 創(chuàng)建Presenter后必須重寫(xiě)這個(gè)方法人弓,將其作為返回值
   */
  abstract protected BasePresenter getPresenter();

  @Override
  public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    view.setClickable(true);  //這個(gè)操作是為了防止Fragment出現(xiàn)點(diǎn)透Bug
  }

  @Override
  public void onDestroyView() {
    super.onDestroyView();
    if (getPresenter() != null){
      getPresenter().destroy(); //銷(xiāo)毀Presenter,避免Activity對(duì)象因被Presenter持有而不能被銷(xiāo)毀
    }
  }
}

View模塊

Fragment的接口着逐。

public interface WeatherDetailFragmentView extends MvpView {

  void onWeatherDataUpdate(WeatherData data);  //通知View更新數(shù)據(jù)崔赌,這個(gè)方法供Presenter調(diào)用
}

實(shí)現(xiàn)Fragment,包含了交互操作耸别。

public class WeatherDetailFragment extends BaseFragment implements WeatherDetailFragmentView {

  public static final String CITY_NAME = "city_name";

  @BindView(R.id.header)
  ViewGroup header;
  @BindView(R.id.city_name)
  TextView cityName;
  @BindView(R.id.weather_info)
  TextView weatherInfo;
  @BindView(R.id.air_quality)
  TextView airQuality;
  @BindView(R.id.temperature)
  TextView temperature;
  @BindView(R.id.recyclerView)
  RecyclerView recyclerView;

  private WeatherDetailFragmentPresenterApi presenter; //依賴(lài)Presenter的抽象
  private WeatherData data;
  private FutureWeathersAdapter adapter;
  private int totalDy;
  private int alphaReferenceValue;


  public static BaseFragment newInstance(String cityName) {  //用靜態(tài)方法創(chuàng)建Fragment
    WeatherDetailFragment instance = new WeatherDetailFragment();
    Bundle args = new Bundle();
    args.putString(CITY_NAME, cityName);  //儲(chǔ)存需要顯示的地區(qū)名字在Fragment的Argument中
    instance.setArguments(args);
    return instance;
  }

  @Nullable
  @Override
  public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
      @Nullable Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_weather_detail, container, false);
    ButterKnife.bind(this, rootView);
    presenter = new WeatherDetailFragmentPresenter(this);  //創(chuàng)建Fragment的Presenter
    initData();
    initView();
    addListener();
    return rootView;
  }

  @Override
  protected void initData() {
    String cityName = getArguments().getString(CITY_NAME);
    presenter.getWeatherData(cityName);  //請(qǐng)求天氣數(shù)據(jù)
  }

  @Override
  protected void initView() {
    //看上面效果圖健芭,我們只需要使用LinearLayoutManager就可以了
    recyclerView.setLayoutManager(
        new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
    recyclerView.setItemAnimator(new DefaultItemAnimator());
  }



  @Override
  protected void addListener() {
    listenRecyclerView();
    temperature.getViewTreeObserver()
        .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
          @Override
          public void onGlobalLayout() {
            computeAlphaReferenceValue();
            temperature.getViewTreeObserver().removeOnGlobalLayoutListener(this);
          }
        });
  }

  private void computeAlphaReferenceValue() {
    //下面是在計(jì)算設(shè)置溫度TextView的Alpha值的參考值,效果是在RecyclerView的第一個(gè)Item滑動(dòng)到溫度TextView的1/8時(shí)秀姐,溫度TextView剛好完全透明
    int temperatureY = temperature.getBottom();
    int temperatureHeight = temperature.getMeasuredHeight();
    int recyclerViewPaddingTop = recyclerView.getPaddingTop();
    alphaReferenceValue = recyclerViewPaddingTop - temperatureY + temperatureHeight / 8;
  }

  private void listenRecyclerView() {
    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
      @Override
      public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        totalDy -= dy;  //記錄RecyclerView滑動(dòng)的距離慈迈,上滑為負(fù),下滑為正

        float translationY = (float) (totalDy * 0.3);  //取滑動(dòng)距離的30%來(lái)移動(dòng)上半部分View才能造成層次感
        header.setTranslationY(translationY);  //設(shè)置整個(gè)上半部分View的TranslationY省有,實(shí)現(xiàn)圖中的滑動(dòng)效果痒留。  
        //translationY初始值為0,負(fù)值向上移動(dòng)蠢沿,正值向下移動(dòng)伸头,與Android坐標(biāo)系的方向是一致的

        float alpha = 1 - (float) (Math.abs(totalDy * 0.3)) / alphaReferenceValue;
        temperature.setAlpha(alpha);  //設(shè)置Alpha值
        super.onScrolled(recyclerView, dx, dy);
      }
    });
  }

  @Override
  protected BasePresenter getPresenter() {
    return presenter;
  }

  @Override
  public void onWeatherDataUpdate(WeatherData data) {
    if (data != null) {
      updateView(data.getData());  
    }
  }

  private void updateView(WeatherData.Data data) {
    setHeaderView(data);
    setRecyclerView(data);
  }

  private void setHeaderView(WeatherData.Data data) {
    String cityName = data.getRealtime().getCity_name();
    this.cityName.setText(cityName);

    String weatherInfo = data.getRealtime().getWeather().getInfo();
    this.weatherInfo.setText(weatherInfo);

    String airQualityStrFormat = getString(R.string.air_quality);
    String airQuality = String.format(airQualityStrFormat, data.getPm25().getPm25().getQuality());
    this.airQuality.setText(airQuality);

    String temperatureFormat = getString(R.string.temperature);
    String temperature =
        String.format(temperatureFormat, data.getRealtime().getWeather().getTemperature());
    this.temperature.setText(temperature);
  }

  private void setRecyclerView(WeatherData.Data data) {
    if (data == null) {
      return;
    }
    if (adapter == null) {
      recyclerView.setAdapter(new FutureWeathersAdapter(getActivity(), data));  //在這里才創(chuàng)建RecyclerView的Adapter
    } else {
      adapter.updateData(data);
    }
  }
}

由于篇幅問(wèn)題,本篇先講到這舷蟀。后面的文章中我們?cè)谝黄鹜瓿蒄ragment的Presenter恤磷、Model,以及編寫(xiě)RecyclerView的Adapter野宜。
本項(xiàng)目已上傳至GitHub碗殷,詳細(xì)源碼請(qǐng)到GitHub查看。

項(xiàng)目地址GitHub

截止上一篇速缨,我們已經(jīng)完成了歡迎頁(yè)锌妻,并且成功的請(qǐng)求到了定位地點(diǎn)的天氣數(shù)據(jù),并且緩存起來(lái)。本篇我們將開(kāi)始構(gòu)建我們的核心主頁(yè)颈渊,它用于展示天氣數(shù)據(jù)班缎。

主頁(yè)需求

  • 能夠展示未來(lái)幾天的天氣;
  • 能夠展示當(dāng)日的詳細(xì)天氣情況吭历;
  • 能夠展示多地天氣。

暫時(shí)先這幾條擂橘,我們后面還可以再添加∩吻現(xiàn)在我們開(kāi)始著手實(shí)現(xiàn)這幾個(gè)需求。

實(shí)現(xiàn)需求

根據(jù)以上需求,我們不但需要展示一天的詳細(xì)數(shù)據(jù)和未來(lái)幾天的天氣數(shù)據(jù)朗若,還要具備展示多地天氣數(shù)據(jù)恼五。所以大概可以設(shè)計(jì)一下,使用列表控件 來(lái)展示一個(gè)地區(qū)的詳細(xì)數(shù)據(jù)哭懈,使用ViewPager 來(lái)展示不同地區(qū)的詳細(xì)數(shù)據(jù)灾馒。話不多說(shuō),來(lái)看看這樣的效果圖遣总。

效果圖

主要xml文件

這個(gè)頁(yè)面很簡(jiǎn)單睬罗,它由Activity和Fragment構(gòu)成。Fragments被放到了ViewPager中旭斥。
下面來(lái)看看各個(gè)布局文件容达。

Activity.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:id="@+id/activity_main"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:background="@color/themeBlueDeep"
  android:clipChildren="false"
  android:gravity="center"
  tools:background="@color/themeBlueDeep"
  tools:context="com.chenbing.oneweather.View.activitys.MainActivity"
  >

//用來(lái)容納Fragment
  <android.support.v4.view.ViewPager
    android:id="@+id/pager_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_above="@+id/bottom_opration"
    android:layout_alignParentStart="true"
    android:background="@color/transparent"
    tools:background="@color/opacity_7_white"
    />


//后期會(huì)在底部添加操作欄,所以先留出位置垂券。
  <RelativeLayout
    android:id="@+id/bottom_opration"
    android:layout_width="match_parent"
    android:layout_height="46dp"
    android:background="@color/transparent"
    tools:background="@color/opacity_5_red"
    android:layout_alignParentBottom="true"
    android:layout_alignParentStart="true">

    <include layout="@layout/line_white"/>


    <ImageView
      android:id="@+id/right_button"
      android:layout_width="wrap_content"
      android:layout_height="match_parent"
      android:padding="8dp"
      tools:src="@drawable/sun"
      />

    <LinearLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:layout_toLeftOf="@+id/left_button"
      android:layout_toRightOf="@+id/right_button"
      >

    </LinearLayout>

    <ImageView
      android:id="@+id/left_button"
      android:layout_width="wrap_content"
      android:layout_height="match_parent"
      android:layout_alignParentRight="true"
      android:padding="8dp"
      tools:src="@drawable/sun"
      />
  </RelativeLayout>
</RelativeLayout>

Fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
  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"
  tools:background="@color/colorPrimary"
  >

  <LinearLayout android:id="@+id/header"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    >

    <TextView
      android:id="@+id/city_name"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginTop="60dp"
      android:gravity="center"
      android:textColor="@color/white"
      android:textSize="35sp"
      tools:text="北京市"
      />

    <TextView
      android:id="@+id/weather_info"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginTop="5dp"
      android:gravity="center"
      android:textColor="@color/white"
      android:textSize="14sp"
      tools:text="多云"
      />

    <TextView
      android:id="@+id/air_quality"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginTop="2dp"
      android:gravity="center"
      android:textColor="@color/white"
      android:textSize="14sp"
      tools:text="空氣質(zhì)量:危害健康"
      />

    <TextView
      android:id="@+id/temperature"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginTop="0dp"
      android:gravity="center_horizontal"
      android:textColor="@color/white"
      android:textSize="80sp"
      tools:text="3°"/>
  </LinearLayout>

//用來(lái)裝未來(lái)天氣數(shù)據(jù)和當(dāng)日詳細(xì)信息
  <android.support.v7.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    android:paddingTop="270dp"
    tools:listitem="@layout/item_future_weather_info"
    />
</FrameLayout>

Activity

這個(gè)Activity的結(jié)構(gòu)和之前的歡迎頁(yè)差不多花盐。

View模塊

首先創(chuàng)建MainActivity的接口。

public interface MainActivityView extends MvpView {

}

繼承接口圆米,創(chuàng)建Activity卒暂。

public class MainActivity extends BaseActivity implements MainActivityView {

  @BindView(R.id.pager_container)
  ViewPager pagerContainer;

  private MainActivityPresenterApi presenter;  //依賴(lài)Presenter的抽象
  private List<BaseFragment> fragments = new ArrayList<>(); //這個(gè)數(shù)組用來(lái)裝每個(gè)地區(qū)的天氣頁(yè)面

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    presenter = new MainActivityPresenter(this);
    ButterKnife.bind(this);
    initData();
    initView();
    addListener();
  }

  @Override
  protected void initData() {
    fragments.add(WeatherDetailFragment.newInstance(null));  //創(chuàng)建Fragment
    //這里之所以穿Null是因?yàn)槲以诤竺嫣幚頃r(shí),把默認(rèn)天氣數(shù)據(jù)設(shè)置為定位地區(qū)的
  }

  @Override
  protected void initView() {
    setWindowProperties();
    //設(shè)置ViewPager的適配器
    pagerContainer.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
      @Override
      public Fragment getItem(int position) {
        return fragments.get(position);
      }

      @Override
      public int getCount() {
        return fragments.size();
      }
    });

    pagerContainer.setPageTransformer(false, new ZoomOutPageTransformer());
  }

  private void setWindowProperties() {
    // 實(shí)現(xiàn)透明狀態(tài)欄
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
        | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
    getWindow().setFormat(PixelFormat.TRANSPARENT);
  }

  @Override
  protected void addListener() {

  }

  @Override
  protected BasePresenter getPresenter() {
    return presenter;
  }
}

Presenter模塊

由于目前Activity本身并不處理什么任務(wù)娄帖,所以Presenter中還沒(méi)內(nèi)容也祠,但是我們?nèi)稳灰凑占軜?gòu)寫(xiě),因?yàn)楹竺婵赡軙?huì)添加?xùn)|西進(jìn)去近速。

//Presenter接口
public interface MainActivityPresenterApi extends BasePresenter {

}

//Presenter實(shí)現(xiàn)诈嘿,因?yàn)闀簳r(shí)不處理事物,所以先不寫(xiě)Model了
public class MainActivityPresenter implements MainActivityPresenterApi {

  private MainActivityView view;

  public MainActivityPresenter(MainActivityView view) {
    this.view = view;
  }

  @Override
  public void destroy() {
    view = null;
  }
}

Fragment

上面代碼中可以看到削葱,我們已經(jīng)創(chuàng)建了一個(gè)Fragment奖亚,下面就來(lái)看看這個(gè)Fragment是什么樣的。
Fragment同樣需要按照MVP模式來(lái)析砸,首先需要改造一下BaseFragment昔字。

public abstract class BaseFragment extends Fragment {

  abstract protected void initData();

  abstract protected void initView();

  abstract protected void addListener();

  /**
   * 創(chuàng)建Presenter后必須重寫(xiě)這個(gè)方法,將其作為返回值
   */
  abstract protected BasePresenter getPresenter();

  @Override
  public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    view.setClickable(true);  //這個(gè)操作是為了防止Fragment出現(xiàn)點(diǎn)透Bug
  }

  @Override
  public void onDestroyView() {
    super.onDestroyView();
    if (getPresenter() != null){
      getPresenter().destroy(); //銷(xiāo)毀Presenter首繁,避免Activity對(duì)象因被Presenter持有而不能被銷(xiāo)毀
    }
  }
}

View模塊

Fragment的接口作郭。

public interface WeatherDetailFragmentView extends MvpView {

  void onWeatherDataUpdate(WeatherData data);  //通知View更新數(shù)據(jù),這個(gè)方法供Presenter調(diào)用
}

實(shí)現(xiàn)Fragment弦疮,包含了交互操作夹攒。

public class WeatherDetailFragment extends BaseFragment implements WeatherDetailFragmentView {

  public static final String CITY_NAME = "city_name";

  @BindView(R.id.header)
  ViewGroup header;
  @BindView(R.id.city_name)
  TextView cityName;
  @BindView(R.id.weather_info)
  TextView weatherInfo;
  @BindView(R.id.air_quality)
  TextView airQuality;
  @BindView(R.id.temperature)
  TextView temperature;
  @BindView(R.id.recyclerView)
  RecyclerView recyclerView;

  private WeatherDetailFragmentPresenterApi presenter; //依賴(lài)Presenter的抽象
  private WeatherData data;
  private FutureWeathersAdapter adapter;
  private int totalDy;
  private int alphaReferenceValue;


  public static BaseFragment newInstance(String cityName) {  //用靜態(tài)方法創(chuàng)建Fragment
    WeatherDetailFragment instance = new WeatherDetailFragment();
    Bundle args = new Bundle();
    args.putString(CITY_NAME, cityName);  //儲(chǔ)存需要顯示的地區(qū)名字在Fragment的Argument中
    instance.setArguments(args);
    return instance;
  }

  @Nullable
  @Override
  public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
      @Nullable Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_weather_detail, container, false);
    ButterKnife.bind(this, rootView);
    presenter = new WeatherDetailFragmentPresenter(this);  //創(chuàng)建Fragment的Presenter
    initData();
    initView();
    addListener();
    return rootView;
  }

  @Override
  protected void initData() {
    String cityName = getArguments().getString(CITY_NAME);
    presenter.getWeatherData(cityName);  //請(qǐng)求天氣數(shù)據(jù)
  }

  @Override
  protected void initView() {
    //看上面效果圖,我們只需要使用LinearLayoutManager就可以了
    recyclerView.setLayoutManager(
        new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false));
    recyclerView.setItemAnimator(new DefaultItemAnimator());
  }



  @Override
  protected void addListener() {
    listenRecyclerView();
    temperature.getViewTreeObserver()
        .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
          @Override
          public void onGlobalLayout() {
            computeAlphaReferenceValue();
            temperature.getViewTreeObserver().removeOnGlobalLayoutListener(this);
          }
        });
  }

  private void computeAlphaReferenceValue() {
    //下面是在計(jì)算設(shè)置溫度TextView的Alpha值的參考值胁塞,效果是在RecyclerView的第一個(gè)Item滑動(dòng)到溫度TextView的1/8時(shí)咏尝,溫度TextView剛好完全透明
    int temperatureY = temperature.getBottom();
    int temperatureHeight = temperature.getMeasuredHeight();
    int recyclerViewPaddingTop = recyclerView.getPaddingTop();
    alphaReferenceValue = recyclerViewPaddingTop - temperatureY + temperatureHeight / 8;
  }

  private void listenRecyclerView() {
    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
      @Override
      public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        totalDy -= dy;  //記錄RecyclerView滑動(dòng)的距離压语,上滑為負(fù),下滑為正

        float translationY = (float) (totalDy * 0.3);  //取滑動(dòng)距離的30%來(lái)移動(dòng)上半部分View才能造成層次感
        header.setTranslationY(translationY);  //設(shè)置整個(gè)上半部分View的TranslationY编检,實(shí)現(xiàn)圖中的滑動(dòng)效果胎食。  
        //translationY初始值為0,負(fù)值向上移動(dòng)蒙谓,正值向下移動(dòng)斥季,與Android坐標(biāo)系的方向是一致的

        float alpha = 1 - (float) (Math.abs(totalDy * 0.3)) / alphaReferenceValue;
        temperature.setAlpha(alpha);  //設(shè)置Alpha值
        super.onScrolled(recyclerView, dx, dy);
      }
    });
  }

  @Override
  protected BasePresenter getPresenter() {
    return presenter;
  }

  @Override
  public void onWeatherDataUpdate(WeatherData data) {
    if (data != null) {
      updateView(data.getData());  
    }
  }

  private void updateView(WeatherData.Data data) {
    setHeaderView(data);
    setRecyclerView(data);
  }

  private void setHeaderView(WeatherData.Data data) {
    String cityName = data.getRealtime().getCity_name();
    this.cityName.setText(cityName);

    String weatherInfo = data.getRealtime().getWeather().getInfo();
    this.weatherInfo.setText(weatherInfo);

    String airQualityStrFormat = getString(R.string.air_quality);
    String airQuality = String.format(airQualityStrFormat, data.getPm25().getPm25().getQuality());
    this.airQuality.setText(airQuality);

    String temperatureFormat = getString(R.string.temperature);
    String temperature =
        String.format(temperatureFormat, data.getRealtime().getWeather().getTemperature());
    this.temperature.setText(temperature);
  }

  private void setRecyclerView(WeatherData.Data data) {
    if (data == null) {
      return;
    }
    if (adapter == null) {
      recyclerView.setAdapter(new FutureWeathersAdapter(getActivity(), data));  //在這里才創(chuàng)建RecyclerView的Adapter
    } else {
      adapter.updateData(data);
    }
  }
}

由于篇幅問(wèn)題训桶,本篇先講到這累驮。后面的文章中我們?cè)谝黄鹜瓿蒄ragment的Presenter、Model舵揭,以及編寫(xiě)RecyclerView的Adapter谤专。
本項(xiàng)目已上傳至GitHub,詳細(xì)源碼請(qǐng)到GitHub查看午绳。

項(xiàng)目地址GitHub

CoorChice的公眾號(hào)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末置侍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子拦焚,更是在濱河造成了極大的恐慌蜡坊,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赎败,死亡現(xiàn)場(chǎng)離奇詭異秕衙,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)僵刮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)据忘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人搞糕,你說(shuō)我怎么就攤上這事勇吊。” “怎么了窍仰?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵汉规,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我驹吮,道長(zhǎng)针史,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任钥屈,我火速辦了婚禮悟民,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘篷就。我一直安慰自己射亏,他們只是感情好近忙,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著智润,像睡著了一般及舍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上窟绷,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天锯玛,我揣著相機(jī)與錄音,去河邊找鬼兼蜈。 笑死攘残,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的为狸。 我是一名探鬼主播歼郭,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼辐棒!你這毒婦竟也來(lái)了病曾?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤漾根,失蹤者是張志新(化名)和其女友劉穎泰涂,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體辐怕,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逼蒙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秘蛇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片其做。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖赁还,靈堂內(nèi)的尸體忽然破棺而出妖泄,到底是詐尸還是另有隱情,我是刑警寧澤艘策,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布蹈胡,位于F島的核電站,受9級(jí)特大地震影響朋蔫,放射性物質(zhì)發(fā)生泄漏罚渐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一驯妄、第九天 我趴在偏房一處隱蔽的房頂上張望荷并。 院中可真熱鬧,春花似錦青扔、人聲如沸源织。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)谈息。三九已至缘屹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間侠仇,已是汗流浹背轻姿。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留逻炊,地道東北人互亮。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像嗅骄,于是被迫代替她去往敵國(guó)和親胳挎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子饼疙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容