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

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

作者: CoorChice | 来源:发表于2016-12-06 15:28 被阅读288次

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

    封面.jpg

    本系列文章列表

    截止上一篇,我们已经完成了欢迎页,并且成功的请求到了定位地点的天气数据,并且缓存起来。本篇我们将开始构建我们的核心主页,它用于展示天气数据。

    主页需求

    • 能够展示未来几天的天气;
    • 能够展示当日的详细天气情况;
    • 能够展示多地天气。

    暂时先这几条,我们后面还可以再添加。现在我们开始着手实现这几个需求。

    实现需求

    根据以上需求,我们不但需要展示一天的详细数据和未来几天的天气数据,还要具备展示多地天气数据。所以大概可以设计一下,使用列表控件 来展示一个地区的详细数据,使用ViewPager 来展示不同地区的详细数据。话不多说,来看看这样的效果图。

    效果图

    主要xml文件

    这个页面很简单,它由Activity和Fragment构成。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="空气质量:危害健康"
          />
    
        <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>
    
    //用来装未来天气数据和当日详细信息
      <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的结构和之前的欢迎页差不多。

    View模块

    首先创建MainActivity的接口。

    public interface MainActivityView extends MvpView {
    
    }
    

    继承接口,创建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<>(); //这个数组用来装每个地区的天气页面
    
      @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));  //创建Fragment
        //这里之所以穿Null是因为我在后面处理时,把默认天气数据设置为定位地区的
      }
    
      @Override
      protected void initView() {
        setWindowProperties();
        //设置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() {
        // 实现透明状态栏
        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本身并不处理什么任务,所以Presenter中还没内容,但是我们任然要按照架构写,因为后面可能会添加东西进去。

    //Presenter接口
    public interface MainActivityPresenterApi extends BasePresenter {
    
    }
    
    //Presenter实现,因为暂时不处理事物,所以先不写Model了
    public class MainActivityPresenter implements MainActivityPresenterApi {
    
      private MainActivityView view;
    
      public MainActivityPresenter(MainActivityView view) {
        this.view = view;
      }
    
      @Override
      public void destroy() {
        view = null;
      }
    }
    

    Fragment

    上面代码中可以看到,我们已经创建了一个Fragment,下面就来看看这个Fragment是什么样的。
    Fragment同样需要按照MVP模式来,首先需要改造一下BaseFragment。

    public abstract class BaseFragment extends Fragment {
    
      abstract protected void initData();
    
      abstract protected void initView();
    
      abstract protected void addListener();
    
      /**
       * 创建Presenter后必须重写这个方法,将其作为返回值
       */
      abstract protected BasePresenter getPresenter();
    
      @Override
      public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        view.setClickable(true);  //这个操作是为了防止Fragment出现点透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更新数据,这个方法供Presenter调用
    }
    

    实现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) {  //用静态方法创建Fragment
        WeatherDetailFragment instance = new WeatherDetailFragment();
        Bundle args = new Bundle();
        args.putString(CITY_NAME, cityName);  //储存需要显示的地区名字在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);  //创建Fragment的Presenter
        initData();
        initView();
        addListener();
        return rootView;
      }
    
      @Override
      protected void initData() {
        String cityName = getArguments().getString(CITY_NAME);
        presenter.getWeatherData(cityName);  //请求天气数据
      }
    
      @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() {
        //下面是在计算设置温度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滑动的距离,上滑为负,下滑为正
    
            float translationY = (float) (totalDy * 0.3);  //取滑动距离的30%来移动上半部分View才能造成层次感
            header.setTranslationY(translationY);  //设置整个上半部分View的TranslationY,实现图中的滑动效果。  
            //translationY初始值为0,负值向上移动,正值向下移动,与Android坐标系的方向是一致的
    
            float alpha = 1 - (float) (Math.abs(totalDy * 0.3)) / alphaReferenceValue;
            temperature.setAlpha(alpha);  //设置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));  //在这里才创建RecyclerView的Adapter
        } else {
          adapter.updateData(data);
        }
      }
    }
    

    由于篇幅问题,本篇先讲到这。后面的文章中我们在一起完成Fragment的Presenter、Model,以及编写RecyclerView的Adapter。
    本项目已上传至GitHub,详细源码请到GitHub查看。

    项目地址GitHub

    截止上一篇,我们已经完成了欢迎页,并且成功的请求到了定位地点的天气数据,并且缓存起来。本篇我们将开始构建我们的核心主页,它用于展示天气数据。

    主页需求

    • 能够展示未来几天的天气;
    • 能够展示当日的详细天气情况;
    • 能够展示多地天气。

    暂时先这几条,我们后面还可以再添加。现在我们开始着手实现这几个需求。

    实现需求

    根据以上需求,我们不但需要展示一天的详细数据和未来几天的天气数据,还要具备展示多地天气数据。所以大概可以设计一下,使用列表控件 来展示一个地区的详细数据,使用ViewPager 来展示不同地区的详细数据。话不多说,来看看这样的效果图。

    效果图

    主要xml文件

    这个页面很简单,它由Activity和Fragment构成。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="空气质量:危害健康"
          />
    
        <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>
    
    //用来装未来天气数据和当日详细信息
      <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的结构和之前的欢迎页差不多。

    View模块

    首先创建MainActivity的接口。

    public interface MainActivityView extends MvpView {
    
    }
    

    继承接口,创建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<>(); //这个数组用来装每个地区的天气页面
    
      @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));  //创建Fragment
        //这里之所以穿Null是因为我在后面处理时,把默认天气数据设置为定位地区的
      }
    
      @Override
      protected void initView() {
        setWindowProperties();
        //设置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() {
        // 实现透明状态栏
        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本身并不处理什么任务,所以Presenter中还没内容,但是我们任然要按照架构写,因为后面可能会添加东西进去。

    //Presenter接口
    public interface MainActivityPresenterApi extends BasePresenter {
    
    }
    
    //Presenter实现,因为暂时不处理事物,所以先不写Model了
    public class MainActivityPresenter implements MainActivityPresenterApi {
    
      private MainActivityView view;
    
      public MainActivityPresenter(MainActivityView view) {
        this.view = view;
      }
    
      @Override
      public void destroy() {
        view = null;
      }
    }
    

    Fragment

    上面代码中可以看到,我们已经创建了一个Fragment,下面就来看看这个Fragment是什么样的。
    Fragment同样需要按照MVP模式来,首先需要改造一下BaseFragment。

    public abstract class BaseFragment extends Fragment {
    
      abstract protected void initData();
    
      abstract protected void initView();
    
      abstract protected void addListener();
    
      /**
       * 创建Presenter后必须重写这个方法,将其作为返回值
       */
      abstract protected BasePresenter getPresenter();
    
      @Override
      public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        view.setClickable(true);  //这个操作是为了防止Fragment出现点透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更新数据,这个方法供Presenter调用
    }
    

    实现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) {  //用静态方法创建Fragment
        WeatherDetailFragment instance = new WeatherDetailFragment();
        Bundle args = new Bundle();
        args.putString(CITY_NAME, cityName);  //储存需要显示的地区名字在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);  //创建Fragment的Presenter
        initData();
        initView();
        addListener();
        return rootView;
      }
    
      @Override
      protected void initData() {
        String cityName = getArguments().getString(CITY_NAME);
        presenter.getWeatherData(cityName);  //请求天气数据
      }
    
      @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() {
        //下面是在计算设置温度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滑动的距离,上滑为负,下滑为正
    
            float translationY = (float) (totalDy * 0.3);  //取滑动距离的30%来移动上半部分View才能造成层次感
            header.setTranslationY(translationY);  //设置整个上半部分View的TranslationY,实现图中的滑动效果。  
            //translationY初始值为0,负值向上移动,正值向下移动,与Android坐标系的方向是一致的
    
            float alpha = 1 - (float) (Math.abs(totalDy * 0.3)) / alphaReferenceValue;
            temperature.setAlpha(alpha);  //设置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));  //在这里才创建RecyclerView的Adapter
        } else {
          adapter.updateData(data);
        }
      }
    }
    

    由于篇幅问题,本篇先讲到这。后面的文章中我们在一起完成Fragment的Presenter、Model,以及编写RecyclerView的Adapter。
    本项目已上传至GitHub,详细源码请到GitHub查看。

    项目地址GitHub

    CoorChice的公众号

    相关文章

      网友评论

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

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