重要的ui元件——Behavior
v7包下的元件類似CoordinatorLayout推出也有一段時間了,大家使用的時候應該會體會到其中很多的便利,今天這篇文章帶大家來了解一個比較重要的ui元件——Behavior。從字面意思上就可以看出它的作用,就是用來規定某些元件的行為的,那它到底是什麼,又該怎麼用呢?看完這篇文章希望大家會有自己的收穫~
前言
寫這篇文章的起因是因為我無意中在GitHub上發現了大神新建了一個Repo,內容是。有興趣的同學可以去看看,其實就是透過Behavior去構造一個類似於DrawerLayout的佈局。想了想已經挺長時間沒有搞ui方面的程式碼了,所以趁著這個機會複習了一下,順便寫一篇文章鞏固,也給想要了解這方面內容的同學一個平臺吧。
Behavior是什麼
在文章的開始,我們先要了解什麼是Behavior。
1 |
/** * Interaction behavior plugin for child views of {@link CoordinatorLayout}. * * |
它是CoordinatorLayout的內部類,從它的註釋和其中的方法可以看出來,它其實就是給CoordinatorLayout的子View提供了一些互動的方法,用來規範它們的互動行為,比如上面出現的onTouchEvent可以用來規範子View的觸控事件,onLayoutChild可以用來規範子View的佈局。
說到這裡,大家可能會有一個問題,CoordinatorLayout又是個什麼東西?
1 |
public class extends implements { } |
可以看出,它其實就是一個ViewGroup,實現了NestedScrollingParent用來執行巢狀滑動。至於巢狀滑動的機制大家可以看我部落格的第一篇文章,這不是我們這篇文章的重點。
既然CoordinatorLayout僅僅只是一個ViewGroup,它又為什麼能展示出它在xml佈局中展示的威力呢?其中的秘密就是在Behavior中。我們可以這麼說,CoordinatorLayout利用了Behavior作為一個代理,去控制管理其下的子View做到各種佈局和動畫效果。那為什麼要使用Behavior呢?我想原因大概就是解耦吧,如果把所有的邏輯都寫死在CoordinatorLayout中,一來不利於維護,二來我們就沒有做一些自定義的事情,會顯得非常的笨重。
為什麼要用Behavior
這裡我們舉一個非常簡單的例子。首先來看看我們的佈局檔案。
1 |
非常簡單有木有,CoordinatorLayout作為根佈局,裡面一個AppBarLayout一個RecyclerView。讓我們看看介面是怎麼樣的。
可以看到顯示是正確的。但是如果我把xml裡RecyclerView的那行layout_behavior刪掉呢?就像這樣。
1 |
最終介面的展示就像這樣,RecyclerView把AppBarLayout給覆蓋了。這裡其實很好理解,如剛才的程式碼所示,CoordinatorLayout其實只是一個ViewGroup,它不像LinearLayout那樣具有特定的佈局特點,甚至可以說它內部的邏輯和FrameLayout是沒什麼差別的,所以如果你不設定對應的Behavior的話,佈局就會有問題。從這裡也可以反映出Behavior的作用,就是規範子View的顯示和互動。
原理&系統是怎麼用Behavior的
說完了Behavior的作用,那該怎麼用它呢?這一小節讓我們來講講Behavior的原理以及系統是如何使用它的。
首先先看原理。我們知道Behavior是用來幫助CoordinatorLayout的,所以我們要從CoordinatorLayout中尋找答案。首先,我們可以看到CoordinatorLayout中有一個LayoutParams,它的子View的LayoutParams都是這個,其中它的建構函式如下。
1 |
LayoutParams(Context context, AttributeSet attrs) { super(context, attrs); ......... if (mBehaviorResolved) { mBehavior = parseBehavior(context, attrs, a.getString( R.styleable.CoordinatorLayout_LayoutParams_layout_behavior)); } a.recycle(); } |
可以看到它透過parseBehavior去得到了對應子View的Behavior。大家可以試試用RecyclerView的getLayoutParams方法去獲取LayoutParams並且呼叫getBehavior方法,可以得到的就是我們在xml檔案中設定的那個Behavior。
知道了如何將Behavior設定進去,那它是如何發揮作用的呢?讓我們來看看onLayout函式。
1 |
@Override protected void (boolean changed, int l, int t, int r, int b) { final int layoutDirection = ViewCompat.getLayoutDirection(this); final int childCount = mDependencySortedChildren.size(); for (int i = 0; i |
可以看到的是其中會先呼叫behavior.onLayoutChild(this, child, layoutDirection)。也就是說,Behavior的邏輯要優先於CoordinatorLayout自己的邏輯。其實不止是onLayout,我們還可以看看onTouchEvent這個函式。
1 |
public boolean (MotionEvent ev) { boolean handled = false; boolean cancelSuper = false; MotionEvent cancelEvent = null; final int action = MotionEventCompat.getActionMasked(ev); if (mBehaviorTouchView != null || (cancelSuper = performIntercept(ev, TYPE_ON_TOUCH))) { // Safe since performIntercept guarantees that // mBehaviorTouchView != null if it returns true final LayoutParams lp = (LayoutParams) mBehaviorTouchView.getLayoutParams(); final Behavior b = lp.getBehavior(); if (b != null) { handled = b.onTouchEvent(this, mBehaviorTouchView, ev); } } ......... return handled; } |
可以看到也是呼叫了Behavior的onTouchEvent,我們可以下判斷說Behavior中的那些方法在CoordinatorLayout中都會在合適的時機去呼叫。這也證明了我們剛才的那句話:[Behavior就是CoordinatorLayout的代理,幫助它去管理子View]。
我們做一個總結,Behavior可以代理哪些行為呢?
1.Measure和Layout的佈局行為。
2.onTouchEvent和onInterceptTouchEvent的觸控行為。比如design包中的SwipeDismissBehavior就是透過這樣的方式完成的。
3.巢狀滑動行為(NestedScrollingParent和NestedScrollingChild中的邏輯)。
4.子View間的依賴行為。
對於第四點我們這裡可以細說一下,什麼叫子View的依賴行為呢?這裡我們舉個例子,我們都知道如果在CoordinatorLayout中使用了FAB並且點選展示SnackbarLayout的話,FAB會在Snackbar顯示的時候對應的上移,這是因為FAB依賴了SnackbarLayout。
1 |
public static class extends . { ........ @Override public boolean (CoordinatorLayout parent, FloatingActionButton child, View dependency) { // We're dependent on all SnackbarLayouts (if enabled) return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout; } @Override public boolean (CoordinatorLayout parent, FloatingActionButton child,View dependency) { if (dependency instanceof Snackbar.SnackbarLayout) { updateFabTranslationForSnackbar(parent, child, dependency); } else if (dependency instanceof AppBarLayout) { // If we're depending on an AppBarLayout we will show/hide it automatically // if the FAB is anchored to the AppBarLayout updateFabVisibility(parent, (AppBarLayout) dependency, child); } return false; } ........ } |
這是FAB中的Behavior,可以看到它重寫了layoutDependsOn和onDependentViewChanged,裡面的邏輯很簡單的就可以看明白。這裡我們[將程式碼翻譯成語言]就是說FAB要依賴的元件是SnackbarLayout,所以在之後的操作裡當DependentView(SnackbarLayout)發生了改變,自己(FAB)也會相應的做出改變。
值得一提的是,onDependentViewChanged這個函式的呼叫時機並不是在onLayout之前,而是在onPreDraw中,具體程式碼如下:
1 |
class implements . { @Override public boolean () { dispatchOnDependentViewChanged(false); return true; } } |
如此簡單的處理View間的依賴,可見Behavior配合CoordinatorLayout是有多強大。下面我們可以再舉一個例子來講講Behavior的作用。還記得我們上面說的嗎?RecyclerView設定了一個Behavior它就可以和AppBarLayout很好的展示出來。這個Behavior的名字是:
1 |
app:layout_behavior="@string/appbar_scrolling_view_behavior" |
可以看到它是AppBarLayout裡的一個內部類,讓我們看看它做了什麼。
1 |
@Override public boolean (CoordinatorLayout parent, View child, View dependency) { // We depend on any AppBarLayouts return dependency instanceof AppBarLayout; } @Override public boolean (CoordinatorLayout parent, View child, View dependency) { offsetChildAsNeeded(parent, child, dependency); return false; } private void (CoordinatorLayout parent, View child, View dependency) { final CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior(); if (behavior instanceof Behavior) { // Offset the child, pinning it to the bottom the header-dependency, maintaining // any vertical gap, and overlap final Behavior ablBehavior = (Behavior) behavior; final int offset = ablBehavior.getTopBottomOffsetForScrollingSibling(); child.offsetTopAndBottom((dependency.getBottom() - child.getTop()) + ablBehavior.mOffsetDelta + getVerticalLayoutGap() - getOverlapPixelsForOffset(dependency)); } } |
我們知道,如果不設定這個Behavior的話,RecyclerView會覆蓋AppBarLayout。而上面這段程式碼裡的邏輯就可以很好的解釋這個原因了。值得一提的是,在offsetChildAsNeeded方法中有這麼一段:
1 |
final CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) dependency.getLayoutParams()).getBehavior(); if (behavior instanceof Behavior) { // Offset the child, pinning it to the bottom the header-dependency, maintaining // any vertical gap, and overlap final Behavior ablBehavior = (Behavior) behavior; |
這裡dependency就是AppBarLayout,所以我們可以知道,AppBarLayout中有兩個Behavior,一個是我們前面提到的ScrollingViewBehavior,用來處理它和其他滑動View的關係,另外一個就是Behavior,用來處理自己的邏輯,比如Layout。透過這種巧妙的方式,我們就可以做到非常簡便的控制View本身和View之間的邏輯。
如何自定義Behavior
本來想寫個demo給大家看一看的,不過感覺還是不要重複造輪子了,還是沒用的輪子。推薦大家看SwipeDismissBehavior用法及實現原理這篇文章和一開始提到的Jake大神的新作。如果你把這兩個東西搞懂,那麼Behavior你可以說已經完全沒問題了~
後記
最近一段時間都在搞hotPatch和外掛化相關的東西,看了很多Framework層的原始碼,要做的東西也做的七七八八,希望快點解決最後的幾個bug並且之後能開源和大家見面吧~
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2768/viewspace-2814684/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Avalonia的UI元件UI元件
- UI設計中最重要的顏色UI
- angular自定義元件-UI元件篇-switch元件Angular元件UI
- 聊聊前端 UI 元件:元件設計前端UI元件
- 聊聊前端 UI 元件:元件體系前端UI元件
- vue相關的UI元件庫VueUI元件
- 最近封裝的table ui元件封裝UI元件
- 必不可少的UI元件一——元件的基礎知識UI元件
- Istio Mixer元件和服務的重要說明元件
- 遊戲UI設計的3條重要原則遊戲UI
- jQuery UI 支援的時間元件timepickerjQueryUI元件
- ThinkPHP3.2 中 behavior 的使用PHP
- 封裝UI元件庫封裝UI元件
- Kendo UI:Grid 表格元件UI元件
- Android 自定義UI元件AndroidUI元件
- [Vue Router] Scroll BehaviorVue
- WPF MenuItem behavior MVVMUIMVVM
- Use IE userdata behavior
- Mint-UI 自定義元件UI元件
- Android通用UI元件之DialogAndroidUI元件
- WPF - 整合HandyControl UI元件庫UI元件
- React UI元件開發心得ReactUI元件
- WPF KeyDown MVVM Via BehaviorMVVM
- WPF Behavior InvokeCommandAction Command CommandParameter
- fish-ui 一套基於vue2的ui元件庫UIVue元件
- JAXenter調查:2018年最重要的UI技術趨勢!UI
- UI/UX設計對app開發公司的重要性UIUXAPP
- 修改Element-ui元件的樣式無效?UI元件
- 面向未來的原生 Web Components UI元件庫WebUI元件
- Shadcn UI:現代前端的靈活元件庫UI前端元件
- [譯]App UI設計:“看得見”有多重要APPUI
- 手寫(radio)element-ui元件UI元件
- 快速構建vue ui元件庫VueUI元件
- element-ui滾動條元件UI元件
- 如何開發React UI元件庫ReactUI元件
- WPF ListBox scrollintoview in ViewModel via behaviorView
- 如何優雅的設定UI庫元件的屬性?UI元件
- iOS 使用UI控制元件的外觀協議UIAppearance進行設定預設UI控制元件樣式iOSUI控制元件協議APP