Android Toolbar头部及其扩展

ToolBar基础使用

Toolbar是Android 5.0推出的一个Material Design风格的导航控件 ,与之前的Actionbar 相比,Toolbar更灵活,它实质就是个ViewGroup。Toolbar可定制性很强:

这里写图片描述这里写图片描述

这是一个典型的ToolBar例子,示例代码网上一大堆了,此处就不赘述了,需要注意的是,上图的clock是个在ToolBar中的自定义控件。

在使用时,必须先隐藏系统原先的导航栏,建议用Theme.AppCompat.Light.NoActionBar做app的theme。从上图可以看出,其实自定义view的空间是比较小的,同时,一般我们使用的是android.support.v7.widget.Toolbar,因此在xml定义TooBar属性(如title)时,需要使用的是http://schemas.android.com/apk/res-auto的title。

还有一点就是,如果要修改更多按钮(上图最右侧的那个黑色的三小点按钮),可以先在xml中设置Toolbar的主题android:theme=”@style/Theme.ToolBar.Sample”:

<style name="Theme.ToolBar.Sample" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="actionOverflowButtonStyle">@style/ActionButton.Overflow.Sample</item>
</style>

<style name="ActionButton.Overflow.Sample" parent="android:style/Widget.Holo.Light.ActionButton.Overflow">
    <item name="android:src">@mipmap/menu</item>
</style>

如此就能修改图标为@mipmap/menu了,更详细的使用可参照官方文档,此外,部分属性设置可参照这里。实际使用中,ToolBar往往不能满足我们的需求(太丑),因而需要一些更酷炫,更强定制化的控件。

可伸缩头部标题栏

先上效果图

这里写图片描述

FloatingActionButton与Snackbar

这里写图片描述

不详述,直接上代码:

android.support.design.widget.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="20dp"
    android:clickable="true"
    android:layout_marginRight="50dp"

    app:layout_anchor="@+id/appbar"
    app:layout_anchorGravity="bottom|right"   

    app:elevation="6dp"                        // 默认状态下FAB的阴影大小
    app:pressedTranslationZ="12dp"             // 点击时候FAB的阴影大小
    app:backgroundTint="#0058f1"               // 设置FAB的背景颜色
    app:rippleColor="#ff0000"                  // 设置FAB点击时的背景颜色
    android:src="@mipmap/ic_launcher" //设置FAB的图标,大小应为24dp
    app:fabSize="normal"  //normal或mini,对应的FAB大小分别为56dp和40dp
    app:borderWidth="0dp"  //该属性尤为重要,如果不设置0dp,那么在4.1的sdk上FAB会显示为正方形,而且在5.0以后的sdk没有阴影效果 />
fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Snackbar.make(v,
                // 最多两行文字
                "最多两行文字最多两行文字最多两行文字最多两行文字最多两行文字最多两行文字最多两行文字", Snackbar.LENGTH_SHORT)
                // 文本内容右侧的可点击区域,会挤占左侧文本
                .setAction("可点击区域,会挤占左侧文本", new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Toast.makeText(AppBarActivity.this, "点击了可点击区域", Toast.LENGTH_SHORT).show();
                    }
                }).show();
    }
});

CoordinatorLayout

思前想后,还是先把最核心的东西提到前面来写了。在上例中,fab会保持在头部的底线上跟随头部运动,并最终消失,按照以往的实现方法,肯定是写一大堆代码,去监听头部高度的变化,再去设置fab的属性,这样的话,代码的耦合度一定很大,而使用CoordinatorLayout的话,只需在fab中加入:

<android.support.design.widget.CoordinatorLayout ...>
    <android.support.design.widget.AppBarLayout android:id="@+id/appbar" ...>
         <android.support.design.widget.CollapsingToolbarLayout ...>
         ...
         </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
...
    <android.support.design.widget.FloatingActionButton 
        app:layout_anchor="@+id/appbar"
        app:layout_anchorGravity="bottom|right"   
.../>

CoordinatorLayout主要用途就是协调子View的布局,简单来说就是让一个Child的View,根据另一个叫做Dependency的View的运动(如位置移动等),来改变自身的属性。上例中,通过使用app:layout_anchor和app:layout_anchorGravity,可以将FloatingActionButton 放在AppBarLayout 的右下角,并且AppBarLayout 变化时,FloatingActionButton 始终会跟随运动,一直保持在这个相对位置。
这个运动,本质上来说,是通过Behavior作用的。Behavior是CoordinatorLayout体系的核心,Child发生变化的具体执行的代码都是放在Behavior里面的。假设我们现在要实现一个效果:

这里写图片描述

随着头部收起,圆形图标从右下角运动到左上角,并逐渐缩小,我们先定义其Behavior:

// 继承CoordinatorLayout.Behavior类
public class IVBehavior extends CoordinatorLayout.Behavior<ImageView> {
    //    必须重写带双参的构造器,因为从xml反射需要调用。
    public IVBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 左右边距15
        MAX_LEFT_DETAL = ViewUtil.getScreenWidth() - ViewUtil.dp2px(130);
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, ImageView child, View dependency) {
        // 判断parent下被依赖的子view
        return dependency != null && dependency.getId() == R.id.tb;
    }

    int initTBY = 0;
    int MAX_LEFT_DETAL;

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, ImageView child, View dependency) {
        //记录开始的Y座标 也就是toolbar起始Y座标
        if (initTBY == 0) {
            initTBY = (int) dependency.getY();
        }

        //计算toolbar从开始移动到最后的百分比
        float percent = dependency.getY() / initTBY;

        // 大小变化
        int width = ViewUtil.dp2px(40 + 60 * percent);
        CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
        lp.width = width;
        lp.height = width;
        child.setLayoutParams(lp);

        // 位置变化
        child.setY(ViewUtil.dp2px(10 + 150 * percent));
        child.setX(ViewUtil.dp2px(15) + MAX_LEFT_DETAL * percent);

        return true;
    }
}

再在此ImageView中定义:

<ImageView 
    app:layout_behavior=".IVBehavior"
    ...
/>

解决了(-_-)…实现Behavior,主要就是步骤就是:
1. 继承CoordinatorLayout.Behavior,T为Child的View类型;
2. 实现双参构造方法(Context context, AttributeSet attrs),以在xml中能被调用;
3. 重写layoutDependsOn,判断CoordinatorLayout 中,Child依赖的View,即Dependency;
4. 重写onDependentViewChanged,根据dependency属性的值或变化,设定当前Child的各种需要变化的属性。
5. 在xml中定义app:layout_behavior。

可以看到,通过使用Behavior,将Child和Dependency之间的关系完全解耦了,不需要跟传统做法一样,需要往Dependency中注入各种监听了。

AppBarLayout

AppBarLayout继承LinearLayout,垂直方向,一般是绑定CoordinatorLayout(parent)使用的。它可以让你定制,当CoordinatorLayout内某个可滚动View的滚动手势发生变化时,AppBarLayout内部的子View实现各种动作(主要是伸缩)。简单来说,就是如上面例子中,随着CoordinatorLayout下部的列表上下推时,头部AppBarLayout的伸缩的效果。简化代码如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#f6f6f6">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#ffffff">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="150dp"
            android:minHeight="50dp"
            android:background="#ff0000"
            android:gravity="center"
            android:text="1"
            android:textColor="#fff"
            android:textSize="20sp"
            android:textStyle="bold"
            app:layout_scrollFlags="scroll"/>
    </android.support.design.widget.AppBarLayout>

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/sv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="ToolBar图标必须为......很长的文本"
            android:textSize="32sp"/>
    </android.support.v4.widget.NestedScrollView>

</android.support.design.widget.CoordinatorLayout>

可滚动View(上例中的NestedScrollView)通过app:layout_behavior=”@string/appbar_scrolling_view_behavior”与AppBarLayout进行关联,其实就是用了系统定义的ScrollingViewBehavior,从而在AppBarLayout伸缩时,NestedScrollView会跟随变化。

至于AppBarLayout的伸缩效果,则是其子View使用app:layout_scrollFlags来设置动作的:
1、scroll:以下的所有其他属性,想要生效的话,都必须连着这个值一起使用。发生向下滚动事件时,优先滚动下部滚动View。

app:layout_scrollFlags="scroll"

这里写图片描述

2、enterAlways:发生向下滚动事件时,优先滚动上部伸缩View。

app:layout_scrollFlags="scroll|enterAlways"

这里写图片描述

3、enterAlwaysCollapsed:配合enterAlways使用,向下滚动时,上部伸缩View先向下滚动最小高度值,然后下部滚动View开始滚动,到达边界时,上部伸缩View再向下滚动,直至显示完全。

android:layout_height="150dp"
android:minHeight="50dp"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"

这里写图片描述

4、exitUntilCollapsed:向上滚动时,上部伸缩View向上滚动退出直至最小高度,然后下部滚动View开始滚动,即上部伸缩View不会全部退出屏幕。

android:layout_height="150dp"
android:minHeight="50dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed"

这里写图片描述

5、snap:松开手指时,上部伸缩View会吸附到最相近的“完全效果”,类似ViewPager的效果。snap可以与上述的几个效果一起使用。

android:layout_height="150dp"
android:minHeight="50dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"

这里写图片描述

CollapsingToolbarLayout

说穿了就是对Toolbar的再包装,必须得是AppBarLayout的直接子View,主要用来做伸缩:

<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#f6f6f6">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:background="#ffffff">
        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:id="@+id/collapsingToolbarLayout"
            android:layout_height="match_parent"
            app:collapsedTitleGravity="left|center_vertical"
            app:collapsedTitleTextAppearance="@style/CollapsingText"
            app:contentScrim="#f15800"
            app:expandedTitleGravity="bottom|center_horizontal"
            app:expandedTitleMarginBottom="50dp"
            app:expandedTitleTextAppearance="@style/ExtendText"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:title="伸缩头示例">

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                android:src="@mipmap/timg"
                app:layout_collapseMode="parallax"
                app:layout_collapseParallaxMultiplier="0.7"/>

            <android.support.v7.widget.Toolbar
                android:layout_width="match_parent"
                android:id="@+id/toolbar"
                app:collapseIcon="@mipmap/menu"
                android:theme="@style/Theme.ToolBar.Sample"
                app:contentInsetStartWithNavigation="20sp"
                app:layout_collapseMode="pin"
                android:layout_height="56dp"
                app:navigationIcon="@mipmap/arrow_back">
                ...
            </android.support.v7.widget.Toolbar>

        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/sv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="ToolBar图标必须为......很长的文本"
            android:textSize="32sp"/>
    </android.support.v4.widget.NestedScrollView>
    ...
</android.support.design.widget.CoordinatorLayout>

再放一遍效果吧:

这里写图片描述

CollapsingToolbarLayout会自动对title进行放大和缩小,需要特别注意的是,此效果要求title必须设置到CollapsingToolbarLayout里,设置到Toolbar上是无效的。

通过设置:

app:layout_collapseMode="pin"

可以像ToolBar一样,将子View固定在特定位置上,当布局伸缩时,也不会被影响。

当设置成:

app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.7"

时,子View就会像上部伸缩View里的背景ImageView一样,在伸缩时以视差的方式伸缩,视差的幅度通过app:layout_collapseParallaxMultiplier设置,1为不动,0为跟CollapsingToolbarLayout一样的速度,一般取值(0,1)。

最后,国际惯例上代码:https://github.com/lk666/ViewExp.git

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章