DesignSupportLibrary

android design supprot library

几行代,让app更优雅。

注:来自对一篇文章的摘录。
地址: http://www.jianshu.com/p/1078568e859f

设计风格

首先开始:

activity的风格为:
在stytle里面添加:

<item name="colorPrimary">#2196f3</item>
<item name="colorPrimaryDark">#1565c0</item>
<item name="colorAccent">#E91e63</item>

添加android design supprot library的依赖,
在app的build.gradle文件添加一行代码:

compile 'com.android.support:design:22.0.0'

添加FAB

Floating Action Button (FAB) 是一个有阴影的圆形按钮,悬浮在app上面。
需要一些父类来使它在屏幕的右下方位置对齐,所以用 FrameLayout 来包裹 FloatingActionButton。

<android.support.v4.widget.DrawerLayout ...
   xmlns:app="http://schemas.android.com/apk/res-auto"
   ....>
   <FrameLayout
       android:id="@+id/rootLayout"
       android:layout_width="match_parent"
       android:layout_height="match_parent">

       <android.support.design.widget.FloatingActionButton
           android:id="@+id/fabBtn"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_gravity="bottom|right"
           android:src="@drawable/ic_plus"
           app:fabSize="normal" />
   </FrameLayout>
   ...
</android.support.v4.widget.DrawerLayout>

android:src 是用来定义的资源文件ID,而app:fabSize=”normal” 是用来定义FAB的大小的,
normal是指标准尺寸56dp的按钮,mini是小一点的,大小为40dp。

此时在4.4上,

而在5.0上面是这样的效果:

这是尚未完善的bug,利用配置资源来解决这个问题L:

在 res/values/dimens.xml
<dimen name="codelab_fab_margin_right">0dp</dimen>
<dimen name="codelab_fab_margin_bottom">0dp</dimen>  
在res/values-21/dimens.xml
<dimen name="codelab_fab_margin_right">0dp</dimen>
<dimen name="codelab_fab_margin_bottom">0dp</dimen>
res/layout/xml里面:
<android.support.design.widget.FloatingActionButton
     ...
     android:layout_marginBottom="@dimen/codelab_fab_margin_bottom"
     android:layout_marginRight="@dimen/codelab_fab_margin_right"
     .../>

这样就可以了。

设置阴影:
利用:app:borderWidth=”0” 来解决。

使用snackbar

snackbar 屏幕的地步一个微小的黑色线条显示一条简短的消息,类似于Toast,
它是作为UI的一部分,而不是覆盖在屏幕上的。

由下面代码实现:

Snackbar.make(someView, "hello,I am Snackbar!", Snackbar.LENGTH_SHORT)
          .setAction("undo", new View.OnClickListener() {
               @Override
               public void onClick(View v) {
                    //TODO
               }
          })
          .show();

第一个参数view或是layout,在它的底部显示一个snackbar,
setAction()是用在设置动作显示在snackbar的右侧,并有对应的监听,这不是必须的,可以移除。

如果是这样实现:

fabBtn = (FloatingActionButton) findViewById(R.id.fabBtn);
fabBtn.setOnClickListener(new View.OnClickListener() {  
     @Override
     public void onClick(View v) {  
           Snackbar.make(rootLayout, "Hello. I am Snackbar!", Snackbar.LENGTH_SHORT)
               .setAction("Undo", new View.OnClickListener() {
                   @Override
                   public void onClick(View v) {
                   }
               })
               .show();

     }
});

则效果是这样的:

这样不完善,需要设置snackbar 和FAB 之间的关联。
需要一个新的特殊的布局,使子views协调工作,是CoordinatorLayout

coordinatorlayout

CoordinatorLayout是一个让子views协调工作的布局
在CoordinatorLayout包裹一个FAB:

<android.support.design.widget.CoordinatorLayout
     android:id="@+id/rootLayout"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
     <android.support.design.widget.FloatingActionButton
          ... />
</android.support.design.widget.CoordinatorLayout>

现在结果: FAB随着Snackbar 的出现和消失而移动,也可以滑动使Snackbar消失。

但是在低于5.0的系统上面,
当Snackbar滑动消失时,FAB忘记移动下来了,


这是一个bug,可以设置FAB的margin bottom 和margin right 为一些非零的正数值时,
它是正常实现的,修改:
res/values/dimens/xml

<dimen name="codelab_fab_margin_right">0.1dp</dimen>
<dimen name="codelab_fab_margin_bottom">0.1dp</dimen>

结果:

如果计划使用android design support library, 要注意CoordinatorLayout,
它就像是整个library的核心。

使用toolbar代替actionbar

在style属性里面隐藏actionbar:

<style name="AppTheme" parent="Theme.App.Compat.Light.DarkActionBar">
     <item name="windowActionBar">false</item>
     <item name="windowNoTitle">true</item>
</style>   

然后在CoordinatorLayout里面的FAB之前正确的放一个toolbar组件。

<android.support.design.widget.CoordinatorLayout
     ...>
     <android.support.v7.widget.Toolbar
          android:id="@+id/toolbar"
          android:layout_width="match_parent"
          android:layout_height="?attr/actionBarSize"
          android:background="?attr/colorPrimary"
          app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
          app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
          />
     <android.support.design.widget.FloatingActionButton
          ...>
     </android.support.design.widget.FloatingActionButton>
</android.support.design.widget.CoordinatorLayout>     

现在要在.java里面说明,要使用toolbar:

Toolbar toolbar;
private void initInstances() {
     toolbar = (Toolbar) findViewById(R.id.toolbar);
     setSupportActionBar(toolbar);
     ...
}

由于放在CoordinatorLayout的东西必须被设计和实现成与它一起合作,否则将不与其view协作。
但是toolbar是不合适的,所以准备了另外一个组件AppBarLayout包裹toolbar:

<android.support.design.widget.CoordinatorLayout
     ...>
     <android.support.design.widget.AppBarLayout
          android:layout_width="match_parent"
          android:layout_height="wrap_content">
          <android.support.v7.widget.Toolbar
               ...>
     <android.support.design.widget.FloatingActionBar
          ...>
     </android.support.design.widget.FloatingActionBar     
</android.support.design.widget.CoordinatorLayout     

现在就可以了!

在内容区域里放东西。

放两个button,

     ...
</android.support.design.widget.AppBarLayout>
<LinearLayout
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical">
     <Button
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="Yo Yo"/>
     <Button
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="Yo Yo"/>
</LinearLayout>
<android.support.design.widget.FloatingActionButton
     ...>


这些按钮放在了toolbar的下面,,有些不协调。
这是因为linearlayout没有被设计成与CoordinatorLayout协同工作,类似与之前的toolbar,
它的解决方法更简单一些,在linearlayout里面添加一个属性,告诉它滚动的行为:

<LinearLayout
     ...
     app:layout_behavior="@string/appbar_scrolling_view_behavior"
     ...>

现在就可以了:

TabLayout

应该把tab放在屏幕的顶部而不是底部,它应该在阴影部分的上面,所以应该把它放在
AppBarLayout(里面有toolbar)里面,沿着toolbar,这是可行的,因为AppBarLayout
是继承一个垂直的LinearLayout.

<android.support.design.widget.AppBarLayout ...>
     <android.support.v7.widget.Toolbar ... />
     <android.support.design.widget.TabLayout
          android:id="@+id/tabLayout"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"/>
</android.support.design.widget.AppBarLayout>

在Java里面添加一些tabs

TabLayout tabLayout;
private void initInstances() {
     tabLayout = (TabLayout) findViewById(R.id.tabLayout);
     tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
     tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
     tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));
     ...
}

结果:

背景色会自动设置为primary color(主题色),而导航线的颜色则是强调色,
tab的字体颜色仍然是黑色的,希望是白色的,在tablayout添加属性:

<android.support.design.widget.TabLayout
     ...
     app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

现在是白色的了,结果:

可以像上面那样手动控制tabLayout,或者让它和viewPager一起工作,自动调用
setupWithViewPager()…

调整两个属性来显示TabLayout:
app:tabMode 如果想在屏幕上显示每个单独的tab,就设置tab为fixed;
只适合少数的tab,或者设置为scrollable让用户去滚动tab,想GooglePlayStroe.

app:tabGravity 如果想要分配所有的空间给每个tab,则设置这个属性为fill,
如果想要所有的tab在屏幕的中间设为center,注意如果tabMode设为scrollable,
则这个属性被忽略。

当随着内容滚动时,让AppBarLayout退出屏幕

让AppBar可以随着内容滚出屏幕,来获得更多的空间显示内容。
首先在linearLayout里面多加几个button,使屏幕需要滚动,
然后用ScrollView 包裹这个linearLayout,不要忘记把linearLayout里面的
layout_behavior 移动到ScrollView,因为现在ScrollView是CoordinatorLayout的
最直接的子view。

<ScrollView
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:fillViewport="true"
     app:layout_behavior="@string/appbar_scrolling_view_behavior">
     <LinearLayout
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical">
          ...
     </LinearLayout>
</ScrollView>

然后为toolbar添加一个滚动的标志,

<android,support.v7.widget.Toolbar
     ...
     app:layout_scrollFlags="scroll|enterAlways" />

结果为:

toolbar并没有随着内容而滚出屏幕,原因和上面一样,因为ScrollView没有设计成与
CoordinatorLayout协同工作,需要利用另外一个view: NestedScrollView,Android
Support Library v4 里有提供,设计出来的目的就是为了与CoordinatorLayout协同
工作的。

<android.support.v4.widget.NestedScrollView
     ...>
     <LinearLayout
          ...>
     </LinearLayout>

</android.support.v4.widget.NestedScrollView>

注意:listview也是和CoordinatorLayout不能协同工作的,只有recyclerView可以。
以后的发展也许会的。
结果:

注意到toolbar滚出了屏幕,但tabLayout仍然还在,因为没有给tabLayout
设置任何滚动标志,如果想要tablayout也在屏幕上消失,只需要设置属性:

<android.support.design.widget.TabLayout
     ...
     app:layout_scrollFlags="scroll|enterAlways" />

结果:

这里会有一些手势上的bug,把它拉回屏幕是很困难的,要等下一个版本修复了。
看一下四个属性值:
scroll 设置这个view随着内容滚动,
enterAlwaysCollapsed 定义view是如何回到屏幕的,当view已经声明了一个最小高度
(minHeight)并且使用了这个标志,那么view只有回到最小高度的时候才会展开,
只有当view已经到达顶部后它才会重新展开全部高度,滚动标志这样使用:
scroll|enterAlwaysCollapsed

enterAlways 这个标志确保了任何向下滚动的操作都会让这个view变得可见,达到快速返回
(quick return)的效果,使用:scroll|enterAlways

exitUntilCollapsed view 将关闭滚动知道它被折叠起来(有minHeight)并且一直保持这样。
例如:

<android.support.v7.widget.Toolbar
     ...
     android:layout_height="192dp"
     app:layout_scrollFlags="scroll|exitUntilCollapsed"
     android:gravity="bottom"
     android:paddingBottom="12dp"
     android:minHeight="?attr/actionBarSize"

结果如下:

为了实现其他的功能,先移除TabLayout

在布局里面删掉:

<!--android.support.design.widget.TabLayout -->

在Java里面,删除:

//tabLayout = (TabLayout) findViewById(R.id.tabLayout);
//tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
//tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
//tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));
Make toobar collapsable 使toolbar可折叠

就像在exitUnitilCollapsed所示的例子,toolbar可以展开折叠,但是toolbar仍然
离开了屏幕,最好的体验是让这些icon应该留在屏幕内。

Design Support Library已经为这个准备好了,利用CollapsingToolbarLayout可以让toolbar
折叠起来,像其他组件一样,具体操作如下:

  • 用collapsingToolbarLayout 包裹toolbar,但仍然在AppBarLayout里面
  • 从toolbar中删除layout_scrollFlags
  • 为collapsingToolbarLayout声明layout_scrollFlags,并且将layout_scrollFlags设置成
    scroll|exitUntilCollapsed
  • 改变AppBarLayout扩张状态时的布局高度大小,这里使用256dp

最终代码:

<android.support.design.widget.AppBarLayout
     <android.support.design.widget.CollapsingToolbarLayout
          android:id="@+id/collapsingToolbarLayout"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          app:layout_scrollFlags="scroll|exitUntilCollapsed">
          <android.support.v7.widget.Toolbar
               android:id="@+id/toolbar"
               android:layout_width="match_parent"
               android:layout_height="?attr/actionBarSize"
               android:background="?attr/colorPrimary"
               android:minHeight="?attr/actionBarSize"
               app:layout_collapseMode="pin"
               app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
               app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
     </android.support.design.widget.CollapsingToolbarLayout>
 </android.support.design.widget.AppBarLayout>    

这里的结果是:

如果没有app:layout_collapseMode=”pin”,则toolbar icon仍然会滚出屏幕。
但是现在没有标题文字了,因为在用CollapsingToolbarLayout包裹tool后,它就没有了,
需要在.java里面手动设置:

collapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsingToolbarLayout);
collapsingToolbarLayout.setTitle("Design Library");

这时标题字体是黑色的,这是没有对AppBar设置主题,在xml里面的AppBarLayout里面加一句:

android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"

结果:

由于CollapsingToolbarLayout 的 特点,应用的标题文字在收缩和展开状态是会自动过渡的,
想要在展开状态改变标题文字的位置,则可以通过应用的 margin 的四个属性,
app:expandedTitleMargin, app:expandedTitleMarginBottom, app:expandedTitleMarginEnd
以及 app:expandedTitleMarginStart

想要在折叠和展开状态时改变文本的显示,可以这样来简单的实现:
设置TextAppence分别通过 app:collapsedTitleTextAppearance 和
app:expandedTitleTextAppearance 来设置。

设置margin为64dp

<android.support.design.widget.CollapsingToolBarLayout
     ...
     app:expandedTitleMarginStart="64dp"

结果:

为appBar添加图片

CollapsingToolbarLayout 是继承自 FrameLayout,可以添加一个ImageView作为
toolbar的背景图片:

<ImageView
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:scaleType="centerCrop"
     android:src="@drawable/header" />
<android.support.v7.widget.Toolbar
     ...

这里结果:

蓝色的导航条仍旧显示着,这是要解决的问题,有一个 Toolbar 的背景看起来不是酷炫,
从 Toolbar 移除它,只需要下面这行代码就行了:

android:background="?attr/colorPrimary"

结果:

基本是实现了,但是看起来太呆,采用视差模式可以更加优雅,
只需声明 collapse 就行了:

<ImageView
     ...
     app:layout_collapseMode="parallax" />

结果:

可自行设置视差系数,在0.0-1.0 之间。

app:layout_collapseParallaxMultiplier="0.7"

注意到 App Bar 的背景总显示一张图片, 可以让它在收缩的时候自动恢复到普通的颜色,
通过声明属性: app:contentScrim

<android.support.design.widget.CollapsingToolbarLayout
     ...
     app:contentScrim="?attr/colorPrimary">

结果:

首先,为Drawer Menu创建一个标题布局文件:

res/layout/nav_header.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="192dp"
     android:theme="@style/ThemeOverlay.AppCompat.Dark">
     <ImageView
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:src="@drawable/nav_header_bg"
          android:scaleType="centerCrop" />
     <ImageView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:src="@drawable/nuuneoi"
          android:layout_gravity="bottom"
          android:layout_marginBottom="36dp" />
     <TextView
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_gravity="bottom"
          android:layout_margin="16dp"
          android:text="nuuneoi"
          android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>
</FrameLayout>  

创建一个菜单资源文件:

res/menu/navigation_drawer_items.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="all">
     <item
        android:id="@+id/navItem1"
        android:icon="@drawable/ic_action_location_found_dark"
        android:title="Home"/>
    <item
        android:id="@+id/navItem2"
        android:icon="@drawable/ic_action_location_found_dark"
        android:title="Blog"/>
    <item
       android:id="@+id/navItem3"
       android:icon="@drawable/ic_action_location_found_dark"
       android:title="About"/>
    <item
       android:id="@+id/navItem4"
       android:icon="@drawable/ic_action_location_found_dark"
       android:title="Contact"/>
</group>
</menu>

naviagtion与两个资源文件绑定起来,作为 Drawer Menu的菜单区域
用下面的代码代替已经存在的白色的LinearLayout:

     ...
</android.support.design.widget.CoordinatorLayout>
<android.support.design.widget.NavigationView
   android:id="@+id/navigation"
   android:layout_width="wrap_content"
   android:layout_height="match_parent"
   android:layout_gravity="start"
   app:headerLayout="@layout/nav_header"
   app:itemIconTint="#333"
   app:itemTextColor="#333"
   app:menu="@menu/navigation_drawer_items" />
</android.support.v4.widget.DrawerLayout>

现在就可以利用Drawer Menu.
效果:

为了处理菜单项的点击事件,声明setNavigationItemSelectedListener来监听,

Navigation navigation;
private void initInstances() {
     ...
     navigation = (NavigationView) findViewById(R.id.navigation);
     navigation.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
          @Override
          public boolean onNavigationItemSelected(MenuItem menuItem) {
               int id = menuItem.getItemId();
               switch (id) {
                    case R.id.navItem1:
                         break;
                    case R.id.navItem2:
                         break;
                    case R.id.navItem3:
                         break;
               }
               return false;
          }
     });

}     

在实际使用中,可以随意声明你想要定义的header view和修改菜单项。

添加TextInputLayout

替换旧的editText,让风格升级,
需要用TextInputLayout包裹住一个editText,

<android.support.design.widget.TextInputLayout
     android:layout_width="match_parent"
     android:layout_height="wrap_content">
      <EditText
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:hint="Username" />
</android.support.design.widget.TextInputLayout>          

在里面再次添加一个password的editText,
然后把这两个控件放到NestedScrollView看结果:

后记

这【篇总结是我看着另外一个作者的博客,一点一点写下来的,我自己之前已经在
android studio上实现了这个效果,感觉这个真的很棒,很赞,
便自己记录下来,也学到了一些东西,希望在不久这些很优雅的APP更多。

注:用的到的地方

<android.support.design.widget.TabLayout
     android:id="@+id/tabs"
     <!--Tab被选中字体的颜色-->
     app:tabSelectedTextColor="@android:color/holo_blue_bright"
     <!--Tab未被选中字体的颜色-->
     app:tabTextColor="@android:color/black"
     <!--Tab指示器下标的颜色-->
     app:tabIndicatorColor="@android:color/holo_blue_bright"
     android:layout_width="match_parent"
     android:layout_height="wrap_content" />

chenzhao@hustunique.com