掌握 Coordinator Layout
在今年的 Google I/O 15上Google 發布了 新的支持庫 ,其中有好幾個組件與Material Design設計密切相關,
在這些新組件中,你可以找到有幾個類似于ViewGroup 的控件,如 AppbarLayout,CollapsingToolbarLayout 和 CoordinatorLayout.
這些ViewGroups 控件提供了非常強大的功能,我決定寫一篇文章來介紹相關的配置和技巧。
CoordinatorLayout
顧名思義,這個控件的目的就是協調它里面View的行為。
請看下面的圖片:

</div>
在這個例子中我們可以看到View之間是如何相互配合的,View會根據其他View的變動做相應的變化。
以下是CoordinatorLayout的簡單使用例子:
<?xml version="1.0" encoding="utf-8"?><CoordinatorLayout> <AppbarLayout/> <scrollableView/> <FloatingActionButton/> </CoordinatorLayout>AppBarLayout
AppBarLayout 是繼承LinerLayout實現的一個ViewGroup容器組件,
默認的AppBarLayout是垂直方向的, 可以管理其中的控件在內容滾動時的行為。這聽起來可能有點令人困惑,我想一張圖片可以勝過千言萬語,特別時GIF圖片:
</div>AppBarLayout在這個例子中時藍色的View,在其下放置了一個可以縮放的圖片,其中包含一個Toolbar,
一個LinearLayout(包含標題和副標題),以及一個TabLayout。我們可以通過設置layout_scrollFlags參數,來控制AppBarLayout中的控件行為。
在我們的這個例子中,大部分View的layout_scrollFlags都設置為scroll,如果沒有設置的話,
當可滾動的View進行滾動時,這些沒設置為scroll的View位置會保持不變;layout_scrollFlags設置上snap值則可以避免進入動畫中間狀態( mid-animation-states),
這意味著動畫會一直持續到View完全顯示或完全隱藏為止。LinearLayout其中包含了一個標題和一個副標題,當用戶向上移動時LinearLayout是一直顯示的,直到移出屏幕(enterAlways);
TabLayout會一直是可見的,因為我們沒有在TabLayout上設置任何flag。
正如你所見,AppbarLayout的強大管理能力是通過在View上設置不同scroll flags實現的。
<AppBarLayout> <CollapsingToolbarLayout app:layout_scrollFlags="scroll|snap" /><Toolbar app:layout_scrollFlags="scroll|snap" /> <LinearLayout android:id="+id/title_container" app:layout_scrollFlags="scroll|enterAlways" /> <TabLayout /> <!-- no flags -->
</AppBarLayout></pre>
這些參數的設置請參考 Google Developers docs。
不過我建議還是通過代碼練習來掌握它。我在文章的末尾提供了幾個Github上的例子。AppbarLayout flags
SCROLL_FLAG_ENTER_ALWAYS: 當任何向下滾動事件發生時, View都會移入 , 不管scrolling view 是否正在滾動。
SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED: 'enterAlways'的附加標識,它使得returning view恢復到指定的最小高度后才開始顯示,然后再慢慢展開。
SCROLL_FLAG_EXIT_UNTIL_COLLAPSED: 但向上移出屏幕時,View會一直收縮到最小高度后,再移出屏幕。
SCROLL_FLAG_SCROLL: View 會根據滾動事件進行移動。
SCROLL_FLAG_SNAP: 但滾動結束時,如果View只有部分可見,它將會自動滑動到最近的邊界(完全可見或完全隱藏)
CoordinatorLayout Behaviors
讓我們做一些測試,打開Android Studio(>= 1.4),根據模板Scrolling Activity創建一個項目,
不需要修改任何代碼,以下就是運行后的界面:
</div>如果我們查看生成的代碼,不管layouts或java類中我們都不能找到Fab在滾動時變化的動畫,為什么呢?
答案在FloatingActionButton的源代碼里,自動 Android Studio v1.2 加入了java反編譯功能,
我們使用ctrl/cmd + click可以查看源碼,看看到底發生了什么:/*
- Copyright (C) 2015 The Android Open Source Project *
- Floating action buttons are used for a
- special type of promoted action.
- They are distinguished by a circled icon
- floating above the UI and have special motion behaviors
- related to morphing, launching, and the transferring anchor point.
blah.. blah.. */ @CoordinatorLayout.DefaultBehavior( FloatingActionButton.Behavior.class) public class FloatingActionButton extends ImageButton { ...
public static class Behavior
extends CoordinatorLayout.Behavior<FloatingActionButton> { private boolean updateFabVisibility( CoordinatorLayout parent, AppBarLayout appBarLayout, FloatingActionButton child { if (a long condition) { // If the anchor's bottom is below the seam, // we'll animate our FAB out child.hide(); } else { // Else, we'll animate our FAB back in child.show(); } }
}
... }</pre>
負責縮放動畫的是design library新引入的元素叫做Behavior, 在這里是CoordinatorLayout.Behavior<FloatingAcctionButton>, 它根據一些滾動條件,判斷是否顯示FAB。
SwipeDismissBehavior
深入design support library的代碼,我們會發現一個新的類:SwipeDismissBehavior,使用這個Behavior,
我們可以很容易的使用CoordinatorLayout實現滑動刪除功能:
</div>@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_swipe_behavior); mCardView = (CardView) findViewById(R.id.swype_card);final SwipeDismissBehavior<CardView> swipe
= new SwipeDismissBehavior(); swipe.setSwipeDirection( SwipeDismissBehavior.SWIPE_DIRECTION_ANY); swipe.setListener( new SwipeDismissBehavior.OnDismissListener() { @Override public void onDismiss(View view) { Toast.makeText(SwipeBehaviorExampleActivity.this, "Card swiped !!", Toast.LENGTH_SHORT).show(); } @Override public void onDragStateChanged(int state) {} }); LayoutParams coordinatorParams = (LayoutParams) mCardView.getLayoutParams(); coordinatorParams.setBehavior(swipe);
}</pre>
Custom Behaviors
創建自定義Behaviors,并沒有想象的那么難,首先我們得搞清楚兩個核心元素 child 和 dependency.
</div>Childs and dependencies
child 是指需要應用behavior的View ,dependency 擔任觸發behavior的角色,并與child進行互動。
在這個例子中, child 是ImageView, dependency 是Toolbar,如果Toolbar發生移動,ImageView也會做相應的移動。
</div>現在我們已經知道概念了,接著我們看看怎么實現,
第一步我們需要繼承CoordinatorLayout.Behavior<T>,T是指某一個View,
在我們的例子中是ImageView, 繼承后,我們必須實現以下2個方法:
- layoutDependsOn
- onDependentViewChanged
layoutDependsOn方法在每次layout發生變化時都會調用,我們需要在dependency控件發生變化時返回True,在我們的例子中是用戶在屏幕上滑動時(因為Toolbar發生了移動),然后我們需要讓child做出相應的反應。
@Override public boolean layoutDependsOn( CoordinatorLayout parent, CircleImageView, child, View dependency) {return dependency instanceof Toolbar; }</pre>
一旦layoutDependsOn返回了True,第二個方法onDependentViewChanged就會被調用,
在這個方法里我們需要實現動畫,轉場等效果。public boolean onDependentViewChanged( CoordinatorLayout parent, CircleImageView avatar, View dependency) {?modifyAvatarDependingDependencyState(avatar, dependency); }
private void modifyAvatarDependingDependencyState( CircleImageView avatar, View dependency) {
// avatar.setY(dependency.getY()); // avatar.setBlahBlat(dependency.blah / blah);
}</pre>
整合后的代碼:
public static class AvatarImageBehavior extends CoordinatorLayout.Behavior<CircleImageView> {@Override public boolean layoutDependsOn( CoordinatorLayout parent, CircleImageView, child, View dependency) {
return dependency instanceof Toolbar; }
public boolean onDependentViewChanged( CoordinatorLayout parent, CircleImageView avatar, View dependency) { modifyAvatarDependingDependencyState(avatar, dependency); }
private void modifyAvatarDependingDependencyState( CircleImageView avatar, View dependency) {
// avatar.setY(dependency.getY()); // avatar.setBlahBlah(dependency.blah / blah);
}
}</pre>Resources
- Coordinator Behavior Example - Github
- Coordinator Examples - Github
- Introduction to coordinator layout on Android - Grzesiek Gajewski
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!相關經驗
相關資訊
sesese色