Plaid源码阅读笔记

Table of Contents

作者主要通过该开源项目阐述了Material Design的一些设计和编码手法. 本文是阅读源码过程中的一些笔记.

源码地址:https://github.com/nickbutcher/plaid 应用地址:https://play.google.com/store/apps/details?id=io.plaidapp

style

定义应用的整体style

并在AndroidMainifest.xml的"application"中使用, 常用字段

  1. colorPrimary: is Main theme colors, used for the default action bar background
  2. colorAccent: theme UI controls like checkboxes and text fields
  3. statusBarColor:标题栏颜色
  4. navigationBarColor: 底部导航栏颜色

页面的style

除了定义应用的整体style的属性外, 常用属性还包括:

  1. windowDrawsSystemBarBackgrounds: 状态栏是否覆盖在页面上, 即页面的顶部和系统状态栏的顶部相同. 相当于代码 ~android:fitsSystemWindows="true"~
  2. windowActionBarOverlay: action bar覆盖在内容之上
  3. windowActionModeOverlay: 当触发显示一些系统上下文的action bar时 (例如长按文字弹出的复制,粘贴对话框), 该属性设置为true可以使得这些bar覆盖在页面之上, 而不是占据页面空间(使页面上/下移动)

View的常用属性

  1. clipToPadding: 属性定义了是否允许ViewGroup在padding中绘制, 该值默认为true, 即不允许. 例如若我们给ListView设置了 android:paddingTop="70dip" android:paddingBottom="70dip" 那么我们可以看到ListView的头部以上和尾部以下都占有70大小的padding, 在滑动ListView的过程中这个padding当然是存在的. 在padding部分是看不到ListView的item的,本质上是在这两部分没有绘制 我们的ListView.

Home

全屏布局设置

这个布局设置方法常见于FrameLayout中.

home页的根布局是DrawerLayout, 其主页面是一个FrameLayout. 由于设置了全屏模式. 为了避免toolbar和底部的按钮位置太靠上或靠下. 在onCreate中为这个DrawerLayout添加了一个 OnApplyWindowInsetsListener, 并在listener中设置页面布局:

  1. 获取系统顶部状态栏的位置信息, 然后将toolbar的margin设置为获取到的数据 以实现toolbar显示在状态栏的下方.

       ViewGroup.MarginLayoutParams lpToolbar = (ViewGroup.MarginLayoutParams) toolbar
            .getLayoutParams();
    lpToolbar.topMargin += insets.getSystemWindowInsetTop();
    lpToolbar.leftMargin += insets.getSystemWindowInsetLeft();
    lpToolbar.rightMargin += insets.getSystemWindowInsetRight();
    toolbar.setLayoutParams(lpToolbar);
    
  2. RecyclerView也调用setPadding设置其布局参数, 保证在 toolbar下方.

    grid.setPadding(
            grid.getPaddingLeft() + insets.getSystemWindowInsetLeft(), // landscape
            insets.getSystemWindowInsetTop()
                    + ViewUtils.getActionBarSize(HomeActivity.this),
            grid.getPaddingRight() + insets.getSystemWindowInsetRight(), // landscape
            grid.getPaddingBottom() + insets.getSystemWindowInsetBottom());
    
  3. 通过获取导航栏的位置信息布局按钮. 然后设置按钮的margin来调整其位置.

    ViewGroup.MarginLayoutParams lpFab = (ViewGroup.MarginLayoutParams) fab
             .getLayoutParams();
     lpFab.bottomMargin += insets.getSystemWindowInsetBottom(); // portrait
     lpFab.rightMargin += insets.getSystemWindowInsetRight(); // landscape
     fab.setLayoutParams(lpFab);
    
  4. ViewStub占位 使用ViewStub可以在使用的时候才初始化控件.

    <ViewStub
             android:id="@+id/stub_no_connection"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="center"
             android:layout="@layout/no_connection"/>
    

设置actionbar

在布局文件中定义Toolbar控件, 然后onCreate中 调用setActionBar()来设置这个控件为Actionbar.

  1. 该函数后可以做一些toolbar动画. 例如标题文字 的慢慢扩大展现.

    private void animateToolbar() {
        // this is gross but toolbar doesn't expose it's children to animate them :(
        View t = toolbar.getChildAt(0);
        if (t != null && t instanceof TextView) {
            TextView title = (TextView) t;
    
            // fade in and space out the title.  Animating the letterSpacing performs horribly so
            // fake it by setting the desired letterSpacing then animating the scaleX ¯\_(ツ)_/¯
            title.setAlpha(0f);
            title.setScaleX(0.8f);
    
            title.animate()
                    .alpha(1f)
                    .scaleX(1f)
                    .setStartDelay(300)
                    .setDuration(2000)
                    .setInterpolator(AnimUtils.getFastOutSlowInInterpolator(this));
        }
    }
    
  2. onCreateOptionsMenu()函数中去加载menu. 通过设置menu项的"showAsAction"属性来决定其是否 显示在ActionBar上.

    <?xml version="1.0" encoding="utf-8"?>
    
    <!--
      Copyright 2015 Google Inc.
    
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
    
           http://www.apache.org/licenses/LICENSE-2.0
    
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License.
      -->
    
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item
            android:id="@+id/menu_search"
            android:title="@string/search"
            android:icon="@drawable/ic_search_24dp"
            android:showAsAction="always" />
    
        <item
            android:id="@+id/menu_filter"
            android:title="@string/filter"
            android:icon="@drawable/ic_filter"
            android:showAsAction="always" />
    
        <item
            android:id="@+id/menu_dribbble_login"
            android:title="@string/dribbble_login"
            android:showAsAction="never" />
    
        <item
            android:id="@+id/menu_designer_news_login"
            android:title="@string/designer_news_login"
            android:showAsAction="never" />
    
        <item
            android:id="@+id/menu_about"
            android:title="@string/about"
            android:showAsAction="never" />
    
    </menu>
    
  3. onPrepareOptionsMenu()来更新menu的展示状态.
  4. onOptionsItemSelected()中设置点击函数.

抽屉控件

通过对控件设置"layout_gravity"设置该控件为 DrawerLayout的抽屉组件.

数据加载和UI逻辑

主要是展示数据加载(更新)过程的UI效果.

基本组件

  1. DataManager用于加载数据.
  2. RecyclerView用于展示数据.
  3. ProgressBar用于表示正在加载.
  4. ViewStub用于占位, 表示无法连接网络.
  5. checkEmptyState()函数:
    • 如果RV中有数据, PB设为GONE.
    • 如果没有
      • 设置toolbar的Z轴为0.
      • 如果有网络, 设置PB可见.
  6. checkConnectivity()函数检查网络
    • 如果断网, PB消失.
    • (如果没有初始化断网控件, 则调用ViewStub的inflate()函数初始化) 展示断网动画.

逻辑

  1. onCreate()阶段第一次加载数据.
  2. onCreate()的末尾调用checkEmptyState()函数. 检查当前状态.
  3. 每次进入onResume()都去调用checkConnectivity.
  4. 网络状态发生切换时的处理. 当网络可用时, 如果当前没有数据, 则显示PB, 并使用DataManager加载数据.
  5. 加载更多数据. 通过实现RV的OnScrollListener来根据当前的数据显示情况, 然后实现加载逻辑. 同时, RV的adapter也注册了加载监听. 当发现要加载更多数据时, 会展示一个PB项目.
  6. 上划. 上划会覆盖toolbar,这是通过设置了RV的另一个ScrollListener,
    • 如果当前可见的第一个item是RV的第一个item,并且 第一个view的top等于RV的paddingTop. 则将toolbar的 Z轴设置为0.
    • 否则设为-1, 实现覆盖效果.

Created At <2017-01-01 Sun 23:25> by Luis Xu. Email: xuzhengchaojob@gmail.com