『Material Design入門學(xué)習(xí)筆記』CollapsingToolbarLayout與AppBarLayout(附demo)

這篇文章中,介紹到的其它組件有TextInputLayout,F(xiàn)loatingActionButton,Snackbar,CoordinatorLayout。
該專題的其它文章:
『Material Design入門學(xué)習(xí)筆記』前言
『Material Design入門學(xué)習(xí)筆記』動(dòng)畫(huà)(含demo)
『Material Design 入門學(xué)習(xí)筆記』主題與 AppCompatActivity(附 demo)
『Material Design入門學(xué)習(xí)筆記』RecyclerView與CardView(附demo)
demo下載

組件介紹

CoordinatorLayout

這一篇講的組件有些多,所以要從頭說(shuō)起,頭是哪呢?就是布局文件,布局文件的頭是哪呢?就是CoordinatorLayout。
CoordinatorLayout被譽(yù)為超級(jí)FrameLayout。
主要是實(shí)現(xiàn)兩個(gè)功能:

  • 作為頂層布局
  • 調(diào)度協(xié)調(diào)子布局
    比如說(shuō),接下來(lái)會(huì)講到的Snackbar,就只有依靠CoordinatorLayout才能實(shí)現(xiàn)一些特殊操作,這個(gè)后面會(huì)講到。
    CoordinatorLayout功能強(qiáng)大,主要是依靠一個(gè)Behavior對(duì)象。CoordinatorLayout自己并不控制View,所有的控制權(quán)都在Behavior。
    這個(gè)Behavior可以通過(guò)自定義的方式來(lái)實(shí)現(xiàn)自己的邏輯。寫到這里,我突然發(fā)現(xiàn)后面沒(méi)法介紹了,因?yàn)榻榻BBehavior的功能需要依賴其它組件,那就放在后面再介紹Behavior吧。
    這里強(qiáng)調(diào)一下,接下來(lái)介紹的所有組件,都會(huì)用到這個(gè)庫(kù):
compile 'com.android.support:design:22.2.1'

AppBarLayout

AppBarLayout繼承自LinearLayout,布局方向?yàn)榇怪狈较?。但是它?nèi)部封裝了一些手勢(shì)變換的動(dòng)畫(huà)。
首先它需要依賴CoordinatorLayout作為父容器,同時(shí)也要求一個(gè)具有可以獨(dú)立滾動(dòng)的子View。
他的子View會(huì)有一個(gè)設(shè)置參數(shù) app:layout_scrollFlags有一下幾種設(shè)置:

  • scroll:設(shè)為scroll的View會(huì)跟隨滾動(dòng)事件一起發(fā)生移動(dòng), 所有想滾動(dòng)出屏幕的view都需要設(shè)置這個(gè)flag,沒(méi)有設(shè)置這個(gè)flag的view將被固定在屏幕頂部。
  • enterAlways:設(shè)為enterAlways的View,當(dāng)ScrollView往下滾動(dòng)時(shí),該View會(huì)直接往下滾動(dòng),而不用考慮ScrollView是否在滾動(dòng),這個(gè)flag讓任意向下的滾動(dòng)都會(huì)導(dǎo)致該view變?yōu)榭梢?jiàn),如啟用快速“返回模式”。
  • exitUntilCollapsed:值設(shè)為exitUntilCollapsed的View,當(dāng)這個(gè)View要往上逐漸“消逝”時(shí),會(huì)一直往上滑動(dòng),直到剩下的的高度達(dá)到它的最小高度后,再響應(yīng)ScrollView的內(nèi)部滑動(dòng)事件。簡(jiǎn)而言之,滾動(dòng)退出屏幕,最后折疊在頂端。
  • enterAlwaysCollapsed:是enterAlways的附加選項(xiàng),一般跟enterAlways一起使用,它是指,View在往下“出現(xiàn)”的時(shí)候,首先是enterAlways效果,當(dāng)View的高度達(dá)到最小高度時(shí)(注意你的view需要設(shè)置minHeight屬性),View就暫時(shí)不去往下滾動(dòng),直到ScrollView滑動(dòng)到頂部不再滑動(dòng)時(shí),View再繼續(xù)往下滑動(dòng),直到滑到View的頂部結(jié)束。
    需要注意的是,后面兩種模式基本只有在CollapsingToolbarLayout才有用。

CollapsingToolbarLayout

CollapsingToolbarLayout繼承自FrameLayout,作用是為Toolbar提供了折疊功能。
下面介紹一些參數(shù):
app:contentScrim這個(gè)參數(shù)可以讓CollapsingToolbarLayout在收縮的時(shí)候,背景圖片消失的時(shí)候,指定一個(gè)顏色。
app:collapsedTitleGravity 指定折疊狀態(tài)的標(biāo)題如何放置,可選值:top、bottom等
app:collapsedTitleTextAppearance指定折疊狀態(tài)標(biāo)題文字的樣貌
app:expandedTitleTextAppearance指定展開(kāi)狀態(tài)標(biāo)題文字的樣貌
app:expandedTitleGravity 展開(kāi)狀態(tài)的標(biāo)題如何放置
app:titleEnabled指定是否顯示標(biāo)題文本
app:toolbarId指定與之關(guān)聯(lián)的ToolBar,如果未指定則默認(rèn)使用第一個(gè)被發(fā)現(xiàn)的ToolBar子View
app:expandedTitleMarginStart指定展開(kāi)狀態(tài)標(biāo)題距離開(kāi)始位置的高度
app:expandedTitleMarginBottom指定展開(kāi)狀態(tài)標(biāo)題距離底部的距離
app:expandedTitleMarginEnd指定展開(kāi)狀態(tài)標(biāo)題距離結(jié)束位置的高度
app:layout_collapseParallaxMultiplier="0.7"設(shè)置視差的系數(shù),介于0.0-1.0之間。
app:layout_collapseMode“pin”:固定模式,在折疊的時(shí)候最后固定在頂端;“parallax”:視差模式,在折疊的時(shí)候會(huì)有個(gè)視差折疊的效果。
app:layout_anchor``app:layout_anchorGravity兩個(gè)屬性連同一起,與某一個(gè)AppBarLayout控件相關(guān)聯(lián),確定位置。

TextInputLayout

TextInputLayout控件和LinearLayout完全一樣,它只是一個(gè)容器。跟ScrollView一樣,TextInputLayout只接受一個(gè)子元素。子元素需要是一個(gè)EditText元素。
EditText中有一個(gè)參數(shù)hint,這個(gè)大家都不陌生,但是如果EditText放在TextInputLayout中,則會(huì)讓hint變成一個(gè)在EditText上方的浮動(dòng)標(biāo)簽,同時(shí)還包括一個(gè)material動(dòng)畫(huà)。

FloatingActionButton

FloatingActionButton是一個(gè)圓形的按鈕,跟Button一樣會(huì)有點(diǎn)擊事件。不同的是,可以設(shè)置陰影,可以設(shè)置反饋動(dòng)畫(huà)。而且如果你設(shè)置的背景圖片是一個(gè)方形的,會(huì)在圓形按鈕中間顯示。

布局文件

上面介紹了一些基本屬性,現(xiàn)在看一下整體的布局文件

<?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:id="@+id/main_content"
                                                 android:layout_width="match_parent"
                                                 android:layout_height="match_parent"
                                                 android:fitsSystemWindows="true">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="256dp"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        android:fitsSystemWindows="true">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:expandedTitleMarginStart="48dp"
            app:expandedTitleMarginEnd="64dp">

            <ImageView
                android:id="@+id/backdrop"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                android:fitsSystemWindows="true"
                android:src="@drawable/logo"
                app:layout_collapseMode="parallax"
                />

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                app:layout_collapseMode="pin" />

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

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



    <android.support.design.widget.TextInputLayout
        android:id="@+id/inputwrapper"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <EditText
            android:id="@+id/input"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="輸入內(nèi)容"/>

    </android.support.design.widget.TextInputLayout>
    <android.support.design.widget.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_anchor="@id/appbar"
        app:layout_anchorGravity="bottom|right|end"
        android:src="@mipmap/ic_launcher"
        android:id="@+id/btn_ok"
        />

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

在該布局文件中使用了Toolbar,關(guān)于它的相關(guān)屬性,可以參考我之前的文章,這里不再重復(fù)介紹。
TextInputLayout有人會(huì)問(wèn)為什么TextInputLayout使用app:layout_behavior="@string/appbar_scrolling_view_behavior",其實(shí)為了適配好位置,最好在外面加一層NestedScrollView,因?yàn)樯婕暗交瑒?dòng)嘛,但是這里暫時(shí)不用,因?yàn)槟鞘窍乱黄恼碌膬?nèi)容。

代碼

下面貼出相關(guān)的代碼:

public class CollapsingActivity extends AppCompatActivity {
    private FloatingActionButton okBtn;
    private EditText editText;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_collapsing);
        initToolBar();
        TextInputLayout inputWrapper = (TextInputLayout) findViewById(R.id.inputwrapper);
        editText = (EditText)findViewById(R.id.input);
        okBtn = (FloatingActionButton)findViewById(R.id.btn_ok);
        okBtn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                hideKeyboard();
                Snackbar mySnackbar = Snackbar.make(findViewById(R.id.main_content),editText.getText().toString(),Snackbar.LENGTH_SHORT);
                mySnackbar.setAction(editText.getText().toString(), new OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        Toast.makeText(CollapsingActivity.this,"click Snackbar",Toast.LENGTH_LONG).show();
                    }
                });
                mySnackbar.show();

            }
        });
        okBtn.setCompatElevation(0);
        inputWrapper.setHintAnimationEnabled(true);
        inputWrapper.setHint("請(qǐng)輸入內(nèi)容");
    }
    private void hideKeyboard() {
        View view = getCurrentFocus();
        if (view != null) {
            ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).
                hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
        }
    }
    private void initToolBar(){
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        setTitle("CollapsingActivity");//設(shè)置標(biāo)題
        toolbar.setNavigationIcon(R.mipmap.ic_launcher_round);//設(shè)置返回鍵,我這里沒(méi)有,就有icon代替吧
        toolbar.setNavigationOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });//返回監(jiān)聽(tīng)
        toolbar.setSubtitle("by deep");//設(shè)置副標(biāo)題
    }
}

簡(jiǎn)單說(shuō)一下代碼的邏輯,在界面上有個(gè)輸入框,輸入內(nèi)容,點(diǎn)擊FloatingActionButton,會(huì)將內(nèi)容做一個(gè)Snackbar的展示,這是在底部會(huì)有一個(gè)Snackbar,Snackbar可以通過(guò)setAction方法設(shè)置點(diǎn)擊事件,它的第一個(gè)參數(shù)是按鈕顯示的文字,第二個(gè)參數(shù)是點(diǎn)擊事件,我這里是點(diǎn)擊Snackbar會(huì)再?gòu)棾鲆粋€(gè)Toast。
關(guān)于Snackbar的初始化,可以使用make方法,這里需要注意它的參數(shù):

  • 第一個(gè)參數(shù)是一個(gè)view,我試過(guò),可以設(shè)置界面任何一個(gè)View,但是最好設(shè)置為父容器CoordinatorLayout,這樣的話,會(huì)有一個(gè)滑動(dòng)消除的手勢(shì)。
  • 第二個(gè)參數(shù)是顯示的內(nèi)容,第三個(gè)參數(shù)是顯示時(shí)長(zhǎng)。

自定義behavior

在上文中提到過(guò)CoordinatorLayout功能如此強(qiáng)大,全依賴于Behavior對(duì)象。
我們先看一下源碼,Behavior是一個(gè)抽象類,public static abstract class Behavior<V extends View>,我們可以根據(jù)View隨意去實(shí)現(xiàn)。
源碼中的實(shí)現(xiàn)這個(gè)抽象類的有以下幾個(gè):

  1. AppBarLayout.Behavior;
  2. AppBarLayout.ScrollingViewBehavior;
  3. FloatingActionButton.Behavior;
  4. Snackbar.Behavior;
  5. BottomSheetBehaviro;
  6. SwipeDismissBehavior;
  7. HeaderBehavior;
  8. ViewOffsetBehavior;
  9. HeaderScrollingViewBehavior;
    其中也有抽象類。
    上面我們用到的就有ScrollingViewBehavior和FloatingActionButton.Behavior,由于FloatingActionButton.Behavior默認(rèn)就是設(shè)置好的,所以我們沒(méi)有在布局文件中寫。
    那么我們現(xiàn)在可以重新寫一個(gè)FloatingActionButton的Behavior。
    在寫之前最好去讀一下Behavior這個(gè)抽象類,其中很多概念和關(guān)聯(lián)需要弄明白。
    其中重要的兩個(gè)概念
  • child 它是一個(gè)View, 是該Behavior的關(guān)聯(lián)對(duì)象,也即Behavior所要操作的對(duì)象
  • dependency ,也是個(gè)View,是 child的依賴對(duì)象,同時(shí)也是Behavior對(duì)child進(jìn)行操作的根據(jù)
    例如我們上面那個(gè)布局文件中child就是FloatingActionButton,dependency就是AppBarLayout。
    還有一些方法:
    layoutDependsOn用來(lái)確定依賴關(guān)系,原文是:

Determine whether the supplied child view has another specific sibling view as a layout dependency.
意思是,這個(gè)view有沒(méi)有兄弟view。

onDependentViewChanged

Respond to a change in a child's dependent view
當(dāng)我們的 dependency 發(fā)生改變的時(shí)候,這個(gè)方法會(huì)調(diào)用,而我們?cè)?onDependentViewChanged 方法里根據(jù)需求做相應(yīng)的界面處理即可。
我們簡(jiǎn)單寫一個(gè)FloatingActionButton跟隨AppBarLayout移動(dòng)的例子:

public class CustomBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {

    public CustomBehavior() {
    }

    public CustomBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
       return dependency instanceof AppBarLayout;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
        float translationY = Math.abs(dependency.getTop());
        Log.e("xxxxxx ","translationY="+translationY);
        child.setY(800-translationY);
        return true;
    }

}

然后設(shè)置到布局文件的對(duì)應(yīng)位置:

  <android.support.design.widget.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_anchor="@id/appbar"
        app:layout_anchorGravity="bottom|right|end"
        android:src="@mipmap/ic_launcher"
        android:id="@+id/btn_ok"
        app:layout_behavior="deep.testmaterial.coordinator.RotateBehavior"
        />

自定義Behavior的方式就介紹到這。

總結(jié)

本來(lái)想在文章中插圖,但是由于動(dòng)圖過(guò)大,上傳總是失敗,所以,還是請(qǐng)感興趣的朋友運(yùn)行demo看一下效果即可。后面的文章還是對(duì)本文的延伸,增添了一些新的組件,敬請(qǐng)關(guān)注。
有問(wèn)題可以給我留言,或者關(guān)注我的公眾號(hào)留言。


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

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

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