參考資料
CoordinatorLayout簡(jiǎn)介(一)CoordinatorLayout的簡(jiǎn)單使用
CoordinatorLayout簡(jiǎn)介(二)幾種系統(tǒng)默認(rèn)Behavior的使用
CoordinatorLayout簡(jiǎn)介(三)手寫一個(gè)CoordinatorLayout怎么樣嗜诀?
Behavior的基類是CoordinatorLayout#Behavior,先看一下類繼承關(guān)系:
可以看出身隐,系統(tǒng)默認(rèn)實(shí)現(xiàn)的Behavior大致分類六類:
- 繼承于SwipeDismissBehavor的
- 繼承于ExpandableBehavior的
- 繼承于FloatingActionButton的
- 繼承于ViewOffsetBehavior的
- BottomSheetBehavior
我們逐個(gè)分析
SwipeDismissBehavor
看先文檔簡(jiǎn)介:
An interaction behavior plugin for child views of CoordinatorLayout to provide support for the 'swipe-to-dismiss' gesture.
翻譯:一個(gè)為CoordinatorLayout的子View提供"滑動(dòng)消失"支持的交互插件
OK 那我們大概就知道它的作用了
看下使用方式:
<?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:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context="com.dafasoft.MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="com.google.android.material.behavior.SwipeDismissBehavior">
<ImageView
android:layout_width="match_parent"
android:layout_height="230dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:scaleType="fitXY"
android:src="@drawable/yellow_zero"/>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="?attr/colorPrimary"
app:tabIndicatorColor="@color/teal_700"
app:tabIndicatorHeight="4dp"
app:tabSelectedTextColor="#000"
app:tabTextColor="#fff"/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout>
然后運(yùn)行代碼 啪一下閃退了货矮,很快把省!
發(fā)生甚么事了? 怎么回事压昼?
看下CoordinatorLayout#parseBehavior的部分代碼求冷,這個(gè)方法的作用是根據(jù)我們?cè)趚ml中設(shè)置的layout_behavior
來(lái)解析成真正的Behavior對(duì)象,我們看下其中的部分邏輯:
try {
Map<String, Constructor<Behavior>> constructors = sConstructors.get();
if (constructors == null) {
constructors = new HashMap<>();
sConstructors.set(constructors);
}
Constructor<Behavior> c = constructors.get(fullName);
if (c == null) {
final Class<Behavior> clazz =
(Class<Behavior>) Class.forName(fullName, false, context.getClassLoader());
c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
c.setAccessible(true);
constructors.put(fullName, c);
}
return c.newInstance(context, attrs);
} catch (Exception e) {
throw new RuntimeException("Could not inflate Behavior subclass " + fullName, e);
}
我們獲取Constructor的時(shí)候窍霞,是獲取的一個(gè)帶參數(shù)的Constructor方法匠题,CONSTRUCTOR_PARAMS
的值分別是Context和AttributeSet
因此,假如我們要使用SwipeDismissBehavor但金,需要自定義一下:
public class TestSwipeDismissBehavior extends SwipeDismissBehavior {
public TestSwipeDismissBehavior(Context context, AttributeSet attributes) {
super();
}
}
然后將app:layout_behavior="com.google.android.material.behavior.SwipeDismissBehavior"
這一行改為
app:layout_behavior=".behavior.TestSwipeDismissBehavior"
看下效果:
官方為我們提供的SwipeDismissBehavior的實(shí)現(xiàn)類韭山,有且只有一個(gè)BaseTransientBottomBar#Behavior, 它的使用是在SnackBar中
什么是SnackBar
Snackbar顯示在所有屏幕其它元素之上(屏幕最頂層),同一時(shí)間只能顯示一個(gè)snackbar冷溃。
Snackbar的基本使用很簡(jiǎn)單钱磅,與Toast類似。
Snackbar.make(view, message_text, duration)
.setAction(action_text, click_listener)
.show();
make()方法是生成Snackbar的秃诵。Snackbar需要一個(gè)控件容器view用來(lái)容納续搀,官方推薦使用CoordinatorLayout來(lái)確保Snackbar和其他組件的交互,比如滑動(dòng)取消Snackbar菠净、Snackbar出現(xiàn)時(shí)FloatingActionButton上移禁舷。顯示時(shí)間duration有三種類型LENGTH_SHORT、LENGTH_LONG和LENGTH_INDEFINITE毅往。
setAction()方法可設(shè)置Snackbar右側(cè)按鈕牵咙,增加進(jìn)行交互事件。如果不使用setAction()則只顯示左側(cè)message攀唯。
Snackbar.make(coordinatorLayout,"這是massage", Snackbar.LENGTH_LONG).setAction("這是action", new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"你點(diǎn)擊了action",Toast.LENGTH_SHORT).show();
}
}).show();
效果:
ExpandableBehavior
看下官方文檔
過(guò)期了...使用MaterialContainerTransform替代了
pass吧...
FloatingActionButton#Behavior
這個(gè)Behavior官方是給FloatingActionButton特供的
我們看一下FloatingActionButton吧
FloatingActionButton是5.0版本出現(xiàn)的控件洁桌,顯示一個(gè)圓形懸浮按鈕。需要添加Design依賴庫(kù)(現(xiàn)在已遷移至Androidx中)并且使用Theme.AppCompat主題
FloatingActionButton配置
屬性 | 說(shuō)明 |
---|---|
android:src | 顯示的圖標(biāo)侯嘀,最好是24dp的 |
app:backgroundTint | 正常的背景顏色 |
app:rippleColor | 按下時(shí)的背景顏色 |
app:elevation | 正常的陰影大辛砹琛(默認(rèn)6dp) |
app:pressedTranslationZ | 按下時(shí)的陰影大小(默認(rèn)12dp) |
app:borderWidth | 邊框?qū)挾?/td> |
app:layout_anchor | 設(shè)置FAB的錨點(diǎn)戒幔,即以哪個(gè)控件為參照設(shè)置位置 |
app:layout_anchorGravity | FAB相對(duì)于錨點(diǎn)的位置 |
app:fabSize | normal或mini(對(duì)應(yīng)56dp和40dp) |
布局:
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floatingActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|bottom"
android:layout_margin="20dp"
app:rippleColor="#ffe5e5e5"
app:backgroundTint="@color/black"
app:elevation="6dp"
app:pressedTranslationZ="6dp"
app:fabSize="mini"
app:borderWidth="0dp"
android:src="@drawable/ic_dragon_avatar_9"/>
效果如下
與SnackBar的聯(lián)動(dòng)效果:
FloatingActionButton天然支持與SnackBar的聯(lián)動(dòng)吠谢,我們只需要將FloatingActionButton設(shè)置為CoordinatorLayout的子View,在展示SnackBar時(shí),SnackBar#make方法的View參數(shù)傳入CoordinatorLayout的對(duì)象即可
Snackbar.make(coordinatorLayout,"這是massage", Snackbar.LENGTH_LONG).setAction("這是action", new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"你點(diǎn)擊了action",Toast.LENGTH_SHORT).show();
}
}).show();
效果圖:
通過(guò)繼承FloatingActionButton#Behavior還可以實(shí)現(xiàn)其他各種各樣的聯(lián)動(dòng)效果
繼承自ViewOffsetBehavior
這一部分Behavior是我們?cè)谌粘i_發(fā)中使用頻次最高的
其中兩個(gè)最重要的Behavior:
- ViewOffsetBehavior
- AppBarLayout#Behavior
我們?cè)谏掀┛椭蟹窒淼膸追N效果都是通過(guò)這兩種Behavior實(shí)現(xiàn)的
當(dāng)我們需要實(shí)現(xiàn)滾動(dòng)控件和AppBarLayout的折疊诗茎、懸浮等效果時(shí)工坊,滾動(dòng)控件必須制定Behavior為ViewOffsetBehavior或其子類,否則是不會(huì)有協(xié)同滑動(dòng)的效果的
AppBarLayout#Behavior是AppBarLayout中各種炫酷效果的基石敢订,AppBarLayout#Behavior負(fù)責(zé)收取和處理從CoordinatorLayout傳來(lái)的滑動(dòng)或者嵌套滑動(dòng)事件王污,并根據(jù)這些事件對(duì)AppBarLayout的子控件和其自身進(jìn)行變換
有同學(xué)會(huì)說(shuō)我們?cè)趚ml中定義AppBarLayout時(shí)并沒有指定Behavior啊,是的楚午,如果我們沒有手動(dòng)指定昭齐,系統(tǒng)會(huì)默認(rèn)指定AppBarLayout的Behavior為AppBarLayout#Behavior,這是通過(guò)AppBarLayout的類注解實(shí)現(xiàn)的:
@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)
public class AppBarLayout extends LinearLayout {
static final int PENDING_ACTION_NONE = 0x0;
static final int PENDING_ACTION_EXPANDED = 0x1;
static final int PENDING_ACTION_COLLAPSED = 0x2;
static final int PENDING_ACTION_ANIMATE_ENABLED = 0x4;
static final int PENDING_ACTION_FORCE = 0x8;
...
這兩種Behavior的代碼較為復(fù)雜且江湖地位顯赫矾柜,本篇不作過(guò)多介紹司浪,在接下來(lái)我們分析CoordinatorLayout源碼時(shí)會(huì)著重講解
BottomSheetBehavior
BottonSheetBehavior用來(lái)實(shí)現(xiàn)從底部滑出的抽屜效果泊业,我們經(jīng)常可以在音樂(lè)播放APP和地圖APP中見到這種效果
BottomSheetBehavior支持的屬性:
屬性 | 含義 |
---|---|
behavior_peekHeight | 當(dāng)控件隱藏時(shí)折疊的高度 |
behavior_hideable | 是否可以隱藏 |
app:behavior_skipCollapsed | 是否跳過(guò)折疊狀態(tài) |
使用方式;
xml的定義:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/bottomSheetLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
app:behavior_peekHeight="50dp"
app:behavior_hideable="true">
<androidx.constraintlayout.utils.widget.ImageFilterView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="fitXY"
android:src="@drawable/yellow_zero_two"/>
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<Button
android:id="@+id/clickBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="點(diǎn)擊"/>
</RelativeLayout>
Activity:
public class BottomSheetBehaviorActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bottom_sheet_behavior);
findViewById(R.id.clickBtn).setOnClickListener(v -> {
LinearLayout bottomSheetLayout = findViewById(R.id.bottomSheetLayout);
BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetLayout);
bottomSheetBehavior.setState(bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED ? BottomSheetBehavior.STATE_EXPANDED : BottomSheetBehavior.STATE_COLLAPSED);
});
findViewById(R.id.floatingActionButton).setOnClickListener(v -> {
LinearLayout bottomSheetLayout = findViewById(R.id.bottomSheetLayout);
BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetLayout);
bottomSheetBehavior.setState(bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED ? BottomSheetBehavior.STATE_EXPANDED : BottomSheetBehavior.STATE_COLLAPSED);
});
}
}
效果圖: