Flipboard bottomsheet源碼分析

Flipboard的bottomsheet源碼地址https://github.com/Flipboard/bottomsheet康嘉。

bottomsheet中最重要的是BottomSheetLayout段磨,其實它是個自定義的ViewGroup际歼,繼承于FrameLayout术羔。查看文檔和bottomsheet-sample里的例子,調(diào)用入口都是showWithSheetView方法寞焙,如PickerActivity中使用

bottomSheetLayout.showWithSheetView(intentPickerSheet);

intentPickerSheet就是從底部彈出的View,在Module bottomsheet-commons中储狭,F(xiàn)lipboard給了幾個封裝好的View,適用于相關(guān)場景捣郊,比如選擇圖片辽狈,選擇Menu等。本文重點是分析BottomSheetLayout模她。

在從入口方法showWithSheetView分析前,先說明兩個問題懂牧,一個是BottomSheetLayout的狀態(tài)侈净,另一個是BottomSheetLayout的布局結(jié)構(gòu)。
一僧凤、BottomSheetLayout狀態(tài)說明
BottomSheetLayout根據(jù)彈窗的狀態(tài)畜侦,分為四個狀態(tài):

public enum State {
        HIDDEN,\\隱藏
        PREPARING,\\準(zhǔn)備中,唯一出現(xiàn)時機(jī)就是調(diào)用showWithSheetView開始時
        PEEKED,\\彈出
        EXPANDED\\見下面的說明
    }

(在BottomSheetLayout中躯保,彈出的View定義為sheetView,BottomSheetLayout會定義一個默認(rèn)的彈出高度peekKeyline旋膳,如果彈出的sheetView高度大于peekKeyline,并且顯示的高度大于peekKeyline途事,則State為EXPANDED验懊。)
peekKeyline的定義如下:

peekKeyline = point.y - (screenWidth / (16.0f / 9.0f));\\point.y為屏幕的高度

二擅羞、BottomSheetLayout布局結(jié)構(gòu)

1.BottomSheetLayout是根布局,布局文件中BottomSheetLayout包含的View為第一個childView
2.dimView是BottomSheetLayout的第二個childView义图,dimView就是那個黑色半透明背景减俏,代碼定義就是 dimView = new View(getContext());定義透明而已
3.sheetView在最上面,是BottomSheetLayout的第三個childView,sheetView就是彈出的View碱工,上面說了

那么問題來了娃承,dimView和sheetView是怎么加入到BottomSheetLayout的?
從源頭看怕篷,在Activity的onCreate中調(diào)用setContentView历筝,然后inflate布局文件,BottomSheetLayout是個ViewGroup,inflate的時候調(diào)用

viewGroup.addView(view, params);

BottomSheetLayout重寫了addView方法廊谓,

 @Override
 public void addView(@NonNull View child) {
        if (getChildCount() > 0) {
            throw new IllegalArgumentException("You may not declare more 
             then one child of bottom sheet. The sheet view must be added 
              dynamically with showWithSheetView()");
        }
        setContentView(child);
}
public void setContentView(View contentView) {
     super.addView(contentView, -1, generateDefaultLayoutParams());\\第一個childView
     super.addView(dimView, -1, generateDefaultLayoutParams());\\第二個childView
 }

dimView被add了梳猪,那sheetView是什么時候add呢?答案在showWithSheetView里蹂析。showWithSheetView里有這樣一句

super.addView(sheetView, -1, params);

這樣之后的代碼中獲得sheetView的方法就容易看懂了

public View getSheetView() {
     return getChildCount() > 2 ? getChildAt(2) : null;\\直接取第三個childView
}

下面進(jìn)入正題舔示,開始分析showWithSheetView(與isTablet相關(guān)的代碼可以不用管,我們主要是針對手機(jī)平臺电抚,而不是平板電腦)惕稻。
三、showWithSheetView流程分析
代碼太長蝙叛,有些沒貼俺祠,最好對照源碼看。
showWithSheetView有幾個重載方法借帘,最終走到

 public void showWithSheetView(final View sheetView, final ViewTransformer viewTransformer) 蜘渣,

如果用戶沒有提供自定義的ViewTransformer,BottomSheetLayout就使用默認(rèn)的BaseViewTransformer肺然,功能很簡單蔫缸,就是根據(jù)彈出高度計算dimView的透明度,代碼不貼了际起,看看就知道了拾碌。
進(jìn)入showWithSheetView方法,首先走到

if(state!=State.HIDDEN){
    Runnable runAfterDismissThis=new Runnable(){
        @Override
        public void run(){
            showWithSheetView(sheetView,viewTransformer);
        }
    };
    dismissSheet(runAfterDismissThis);
    return;
}

在調(diào)用showWithSheetView前街望,BottomSheetLayout可能不是HIDDEN狀態(tài)校翔,這段代碼就是處理這種情況,看名字runAfterDismissThis就是知道,先調(diào)用dismissSheet隱藏sheetView,在dismissSheet動畫結(jié)束時灾前,再調(diào)用runAfterDismissThis防症,相關(guān)代碼在dismissSheet里監(jiān)聽動畫結(jié)束的地方onAnimationEnd

setState(State.HIDDEN);\\改變狀態(tài)為HIDDEN
setSheetLayerTypeIfEnabled(LAYER_TYPE_NONE);\\關(guān)閉硬件加速
removeView(sheetView);\\隱藏動畫結(jié)束sheetView被remove了
if (runAfterDismiss != null) {
        runAfterDismiss.run();
        runAfterDismiss = null;
}

然后是設(shè)置BottomSheetLayout狀態(tài)是PREPARING,接著一段是設(shè)置sheetView的LayoutParams,然后調(diào)用addView把sheetView加入到BottomSheetLayout,之前說BottomSheetLayout布局結(jié)構(gòu)時說過蔫敲,當(dāng)然最開始sheetView位置設(shè)置到了BottomSheetLayout的底部不可見饲嗽,通過initializeSheetValues方法中的

 getSheetView().setTranslationY(getHeight());

位置都設(shè)置好了,開始動畫燕偶,之所以post方式開始動畫喝噪,是要sheetView已經(jīng)畫過在執(zhí)行peekSheet。

getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                getViewTreeObserver().removeOnPreDrawListener(this);
                post(new Runnable() {
                    @Override
                    public void run() {
                        // Make sure sheet view is still here when first draw happens.
                        // In the case of a large lag it could be that the view is dismissed before it is drawn resulting in sheet view being null here.
                        if (getSheetView() != null) {
                            peekSheet();
                        }
                    }
                });
                return true;
            }

上面一段指么,主要是走到peekSheet方法酝惧,開始動畫。最下面的sheetViewOnLayoutChangeListener先不用管伯诬。
peekSheet代碼如下:

public void peekSheet() {
        cancelCurrentAnimation();
        setSheetLayerTypeIfEnabled(LAYER_TYPE_HARDWARE);
        ObjectAnimator anim = ObjectAnimator.ofFloat(this, SHEET_TRANSLATION, getPeekSheetTranslation());
        anim.setDuration(ANIMATION_DURATION);
        anim.setInterpolator(animationInterpolator);
        anim.addListener(new CancelDetectionAnimationListener() {
            @Override
            public void onAnimationEnd(@NonNull Animator animation) {
                if (!canceled) {
                    currentAnimator = null;
                }
            }
        });
        anim.start();
        currentAnimator = anim;
        setState(State.PEEKED);
    }

peekSheet的功能就是啟用硬件加速在sheetView上畫屬性動畫晚唇,設(shè)置BottomSheetLayout的狀態(tài)為PEEKED。當(dāng)然這里有個屬性動畫怎么畫的問題盗似,wrap了一層哩陕,可以參考代碼中的SHEET_TRANSLATION,這里不分析了赫舒。
總結(jié)一下悍及,showWithSheetView方法主要功能就是add sheetView到BottomSheetLayout后開始彈出動畫。

以后再分析BottomSheetLayout比較有亮點的功能接癌,可拖動sheetView心赶。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市缺猛,隨后出現(xiàn)的幾起案子缨叫,更是在濱河造成了極大的恐慌,老刑警劉巖荔燎,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耻姥,死亡現(xiàn)場離奇詭異,居然都是意外死亡有咨,警方通過查閱死者的電腦和手機(jī)琐簇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來座享,“玉大人婉商,你說我怎么就攤上這事≌鹘玻” “怎么了据某?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵橡娄,是天一觀的道長诗箍。 經(jīng)常有香客問我,道長挽唉,這世上最難降的妖魔是什么滤祖? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任筷狼,我火速辦了婚禮,結(jié)果婚禮上匠童,老公的妹妹穿的比我還像新娘埂材。我一直安慰自己,他們只是感情好汤求,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布俏险。 她就那樣靜靜地躺著,像睡著了一般扬绪。 火紅的嫁衣襯著肌膚如雪竖独。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天挤牛,我揣著相機(jī)與錄音莹痢,去河邊找鬼。 笑死墓赴,一個胖子當(dāng)著我的面吹牛竞膳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播诫硕,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼坦辟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了痘括?” 一聲冷哼從身側(cè)響起长窄,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎纲菌,沒想到半個月后挠日,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡翰舌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年嚣潜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片椅贱。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡懂算,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出庇麦,到底是詐尸還是另有隱情计技,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布山橄,位于F島的核電站垮媒,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜睡雇,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一萌衬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧它抱,春花似錦秕豫、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至侮穿,卻和暖如春沫屡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背撮珠。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工沮脖, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芯急。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓勺届,卻偏偏與公主長得像,于是被迫代替她去往敵國和親娶耍。 傳聞我的和親對象是個殘疾皇子免姿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,527評論 25 707
  • ¥開啟¥ 【iAPP實現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 6,358評論 0 17
  • 翻譯自“Collection View Programming Guide for iOS” 0 關(guān)于iOS集合視...
    lakerszhy閱讀 3,833評論 1 22
  • 入夜秋風(fēng)忽襲來 枯葉沙沙鳴不止 紛紛飄落壤中去 唯報樹母養(yǎng)育恩
    石二笙簫閱讀 187評論 0 0
  • 家 是一點一滴堆砌起來的靈感 童年的執(zhí)念 愛情的向往 生活的憧憬 都會在家的營造和布置中呈現(xiàn) 復(fù)古 是一種情結(jié) 更...
    洽總閱讀 292評論 0 0