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

個人博客CoorChice,https://chenbingx.github.io/ ,最新文章將會首發(fā)CoorChice的博客,歡迎探索哦 !
同時,搜索CoorChice關(guān)注我的微信公眾號,同期文章也將會優(yōu)先推送到公眾號中,以提醒您有新鮮文章出爐。亦可在文章末尾掃描二維碼關(guān)注。

封面.jpg

本系列文章列表

截止上一篇,我們已經(jīng)完成了歡迎頁,并且成功的請求到了定位地點(diǎn)的天氣數(shù)據(jù),并且緩存起來。本篇我們將開始構(gòu)建我們的核心主頁,它用于展示天氣數(shù)據(jù)。

主頁需求

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

暫時先這幾條,我們后面還可以再添加?,F(xiàn)在我們開始著手實(shí)現(xiàn)這幾個需求。

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

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

效果圖

主要xml文件

這個頁面很簡單,它由Activity和Fragment構(gòu)成。Fragments被放到了ViewPager中。
下面來看看各個布局文件。

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"
  >

//用來容納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"
    />


//后期會在底部添加操作欄,所以先留出位置。
  <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>

//用來裝未來天氣數(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

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

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;  //依賴Presenter的抽象
  private List<BaseFragment> fragments = new ArrayList<>(); //這個數(shù)組用來裝每個地區(qū)的天氣頁面

  @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,把默認(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中還沒內(nèi)容,但是我們?nèi)稳灰凑占軜?gòu)寫,因?yàn)楹竺婵赡軙砑訓(xùn)|西進(jìn)去。

//Presenter接口
public interface MainActivityPresenterApi extends BasePresenter {

}

//Presenter實(shí)現(xiàn),因?yàn)闀簳r不處理事物,所以先不寫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)建了一個Fragment,下面就來看看這個Fragment是什么樣的。
Fragment同樣需要按照MVP模式來,首先需要改造一下BaseFragment。

public abstract class BaseFragment extends Fragment {

  abstract protected void initData();

  abstract protected void initView();

  abstract protected void addListener();

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

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

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

View模塊

Fragment的接口。

public interface WeatherDetailFragmentView extends MvpView {

  void onWeatherDataUpdate(WeatherData data);  //通知View更新數(shù)據(jù),這個方法供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; //依賴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);  //儲存需要顯示的地區(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);  //請求天氣數(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的第一個Item滑動到溫度TextView的1/8時,溫度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滑動的距離,上滑為負(fù),下滑為正

        float translationY = (float) (totalDy * 0.3);  //取滑動距離的30%來移動上半部分View才能造成層次感
        header.setTranslationY(translationY);  //設(shè)置整個上半部分View的TranslationY,實(shí)現(xiàn)圖中的滑動效果。  
        //translationY初始值為0,負(fù)值向上移動,正值向下移動,與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);
    }
  }
}

由于篇幅問題,本篇先講到這。后面的文章中我們在一起完成Fragment的Presenter、Model,以及編寫RecyclerView的Adapter。
本項(xiàng)目已上傳至GitHub,詳細(xì)源碼請到GitHub查看。

項(xiàng)目地址GitHub

截止上一篇,我們已經(jīng)完成了歡迎頁,并且成功的請求到了定位地點(diǎn)的天氣數(shù)據(jù),并且緩存起來。本篇我們將開始構(gòu)建我們的核心主頁,它用于展示天氣數(shù)據(jù)。

主頁需求

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

暫時先這幾條,我們后面還可以再添加?,F(xiàn)在我們開始著手實(shí)現(xiàn)這幾個需求。

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

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

效果圖

主要xml文件

這個頁面很簡單,它由Activity和Fragment構(gòu)成。Fragments被放到了ViewPager中。
下面來看看各個布局文件。

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"
  >

//用來容納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"
    />


//后期會在底部添加操作欄,所以先留出位置。
  <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>

//用來裝未來天氣數(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

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

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;  //依賴Presenter的抽象
  private List<BaseFragment> fragments = new ArrayList<>(); //這個數(shù)組用來裝每個地區(qū)的天氣頁面

  @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,把默認(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中還沒內(nèi)容,但是我們?nèi)稳灰凑占軜?gòu)寫,因?yàn)楹竺婵赡軙砑訓(xùn)|西進(jìn)去。

//Presenter接口
public interface MainActivityPresenterApi extends BasePresenter {

}

//Presenter實(shí)現(xiàn),因?yàn)闀簳r不處理事物,所以先不寫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)建了一個Fragment,下面就來看看這個Fragment是什么樣的。
Fragment同樣需要按照MVP模式來,首先需要改造一下BaseFragment。

public abstract class BaseFragment extends Fragment {

  abstract protected void initData();

  abstract protected void initView();

  abstract protected void addListener();

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

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

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

View模塊

Fragment的接口。

public interface WeatherDetailFragmentView extends MvpView {

  void onWeatherDataUpdate(WeatherData data);  //通知View更新數(shù)據(jù),這個方法供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; //依賴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);  //儲存需要顯示的地區(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);  //請求天氣數(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的第一個Item滑動到溫度TextView的1/8時,溫度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滑動的距離,上滑為負(fù),下滑為正

        float translationY = (float) (totalDy * 0.3);  //取滑動距離的30%來移動上半部分View才能造成層次感
        header.setTranslationY(translationY);  //設(shè)置整個上半部分View的TranslationY,實(shí)現(xiàn)圖中的滑動效果。  
        //translationY初始值為0,負(fù)值向上移動,正值向下移動,與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);
    }
  }
}

由于篇幅問題,本篇先講到這。后面的文章中我們在一起完成Fragment的Presenter、Model,以及編寫RecyclerView的Adapter。
本項(xiàng)目已上傳至GitHub,詳細(xì)源碼請到GitHub查看。

項(xiàng)目地址GitHub

CoorChice的公眾號
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容