使用MotionLayout實(shí)現(xiàn)高德地圖bottomSheets效果

高德效果

高德效果

以下是我用motionlayout實(shí)現(xiàn)的效果桐筏,沒有達(dá)到絲滑流暢,優(yōu)化就看小伙伴你了

image

demo.apk下載體驗(yàn)

緣由

  • 使用高德地圖的時候看著這種體驗(yàn)很好拇砰,隨后就想試試怎么達(dá)到類似效果
  • 最近正在看MotionLayout的東西梅忌,正好就嘗試嘗試

MotionLayout

  • 「譯」 MotionLayout 介紹 (Part I - IV)系列教會你如何使用MotionLayout
  • 這里不做過多描述,總結(jié)一下在xml文件夾下創(chuàng)建xxscene.xml 主要用于描述場景動畫的關(guān)鍵幀和view狀態(tài)變化等
  • xxscene.xml內(nèi)容包括 主要為3個關(guān)鍵內(nèi)容:
  1. Transition 過渡

constraintSetStart:啟動約束場景

constraintSetEnd:結(jié)束約束場景

app:dragDirection="dragUp" 拽動(拖拉)

  1. KeyFrameSet關(guān)鍵幀集合

KeyAttribute關(guān)鍵幀

app:framePosition 位置,進(jìn)度

app:target="@id/xxx 被描述的view id

  1. ConstraintSet 約束集合
 <Transition
        app:constraintSetEnd="@id/slideup_end"
        app:constraintSetStart="@id/slideup_start"
        app:duration="600"
        app:interpolator="easeIn">
        <OnSwipe
            app:dragDirection="dragUp"
            app:maxAcceleration="600"
            app:touchAnchorSide="top"
            app:touchAnchorId="@id/content"
           />
        <KeyFrameSet>
            <KeyAttribute
                android:alpha="0"
                app:framePosition="45"
                app:target="@id/sugar_title" />

            <KeyAttribute
                android:alpha="1"
                app:framePosition="90"
                app:target="@id/sugar_title" />
        ...
        </KeyFrameSet>
 </Transition>   
 
 <ConstraintSet android:id="@+id/slideup_start">

        <Constraint
        ···
        />
    ...
  </ConstraintSet>  

拆解過程

  • 高德地圖是上拉之后是三段式的,如圖所示
1
2
3
  • MotionLayout就只有一個初始約束和結(jié)束約束涮总,沒有中間約束沟娱,如何實(shí)現(xiàn)這種三段式效果?
  • 答: 使用progress, MotionLayout自帶進(jìn)度
  • 有進(jìn)度捌锭,什么時候執(zhí)行下一步操作,什么時候又執(zhí)行上一步操作?
  • 答: 根據(jù)手勢尸诽,我們可以判斷用戶下一步是往上拉還是下拉,設(shè)定階段閥值盯另,超過進(jìn)入下一步性含,未超過回到之前一步
  • 高德地圖中,只有手觸碰到bottomview的時候手勢才有效果鸳惯,所以還需要判斷touch事件是否在view范圍內(nèi)

拆解完畢

實(shí)現(xiàn)過程

  • 設(shè)置閥值
    /**
     * 初始位置
     */
    public final static float PROGRESS_START = 0f;
    /**
     * 頂部閥值 
     */
    public final static float PROGRESS_TOP = 0.9f;
    /**
     * 低部閥值 
     */
    public final static float PROGRESS_BOTTOM = 0.1f;
    /**
     * 中間位置 
     */
    public final static float PROGRESS_MIDDLE = 0.6f;
    /**
     * 結(jié)束位置 
     */
    public final static float PROGRESS_END = 1.0f;
  • 重寫MotionLayoutonTouchEvent事件 ,使用hasMiddle布爾值判斷是否有中間狀態(tài)
 @Override
    public boolean onTouchEvent(MotionEvent event) {
        float progress = getProgress();
        View viewGroup = findViewById(R.id.content);
        Rect mRect = new Rect();
        if (!mTouchStared) {
            viewGroup.getHitRect(mRect);
            mTouchStared = mRect.contains((int) event.getX(), (int) event.getY());
        }
        float endY;
        if (hasMiddle) {
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_CANCEL:
                    mTouchStared = false;
                    break;
                case MotionEvent.ACTION_DOWN:
                    startY = event.getY();
                    break;
                case MotionEvent.ACTION_UP:
                    endY = event.getY();
                    //手勢向下
                    if ((endY - startY) > 0) {
                        if (progress >= PROGRESS_TOP) {
                            mTouchStared = false;
                            handleProgress(PROGRESS_END);

                        }
                        if (progress < PROGRESS_TOP && progress >= PROGRESS_MIDDLE) {
                            handleProgress(PROGRESS_MIDDLE);
                        }
                        if (progress < PROGRESS_MIDDLE) {
                            handleProgress(PROGRESS_START);
                        }
                        //手勢向上
                    } else {
                        if (progress <= PROGRESS_BOTTOM) {
                            handleProgress(PROGRESS_START);
                        }
                        if (progress > PROGRESS_BOTTOM && progress <= PROGRESS_MIDDLE) {
                            handleProgress(PROGRESS_MIDDLE);
                        }
                        if (progress > PROGRESS_MIDDLE) {
                            mTouchStared = false;
                            handleProgress(PROGRESS_END);
                        }
                    }
                    return mTouchStared;
            }
        } else {
            if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || event.getActionMasked() == MotionEvent.ACTION_UP) {
                mTouchStared = false;
                return super.onTouchEvent(event);
            }
        }
        return mTouchStared && super.onTouchEvent(event);

    }
  • bottom_scene.xml
  1. 上拉超過頂部閥值PROGRESS_TOP之后標(biāo)題出現(xiàn)在屏幕內(nèi)商蕴,其余時候出現(xiàn)在屏幕外即可;
  2. 初始狀態(tài)這里把scaleXscaleY設(shè)為0.9結(jié)束設(shè)為了1悲敷,僅僅是為了過渡好看究恤,你可以不用設(shè)置隨意修改即可
  3. 背景色過渡,最開始透明后德,結(jié)束為白色背景部宿。中間過渡關(guān)鍵幀95變純白背景

結(jié)果和改進(jìn)

  • 設(shè)置允許中間狀態(tài)后,之后進(jìn)入下一步的過程瓢湃,如圖理张,過于生硬
image
  • 改進(jìn)方向
  1. 動畫應(yīng)該是勻速的,然而setProgress(pro);卻是一步直達(dá)绵患;
  2. 設(shè)置時間間隔勻速達(dá)到最后的進(jìn)度即可雾叭,源碼已詳細(xì)注釋。改進(jìn)之后見最上面效果圖落蝙;
 private void handleProgress(float progress) {
        //如果需要設(shè)置的進(jìn)度和當(dāng)前進(jìn)度相同不做處理
        if (progress == getProgress()){
            return;
        }
        //動畫播放時間底值
        long time = 200;
        //進(jìn)度間隔 >0 說明上拉 < 0說明下滑
        float interval = progress - getProgress();
        long startTime, endTime;
        if (interval > 0) {
            startTime = (long) (getProgress() * time);
            endTime = (long) (progress * time);
        } else {
            endTime = (long) (getProgress() * time);
            startTime = (long) (progress * time);
        }
        if (timeDisposable != null){
            timeDisposable.dispose();
        }
        //startTime 初始時間 endTime - startTime為次數(shù) 0為延遲時間 3為間隔 單位TimeUnit.MILLISECONDS 毫秒
        timeDisposable = Observable.intervalRange(startTime, endTime - startTime, 0, 3, TimeUnit.MILLISECONDS)
                .observeOn(Schedulers.io())
                .compose(((BaseActivity) getContext()).getProvider().bindToLifecycle())
                .observeOn(AndroidSchedulers.mainThread())
                .map(new Function<Long, Long>() {
                    @Override
                    public Long apply(Long aLong) throws Exception {
                        //下滑需要反向
                        if (interval < 0) {
                            long interStart = aLong - startTime;
                            return endTime - interStart;
                        }
                        return aLong;
                    }
                })
                .subscribe(new Consumer<Long>() {
                    @Override
                    public void accept(Long aLong) throws Exception {
                        float pro = (Float.valueOf(aLong) / time);
                        setProgress(pro);
                    }
                });
    }

源碼已放入sugar demo中织狐,sugar是我會長期維護(hù)的一個庫??????

?? Sugar 簡單便捷 快速開發(fā)Android項(xiàng)目暂幼,集合流行框架封裝

About me

License

Copyright 2019, wobiancao       
  
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at 
 
       http://www.apache.org/licenses/LICENSE-2.0 

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
   
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市移迫,隨后出現(xiàn)的幾起案子旺嬉,更是在濱河造成了極大的恐慌,老刑警劉巖厨埋,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邪媳,死亡現(xiàn)場離奇詭異,居然都是意外死亡荡陷,警方通過查閱死者的電腦和手機(jī)雨效,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來废赞,“玉大人徽龟,你說我怎么就攤上這事∮纪罚” “怎么了顿肺?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長渣蜗。 經(jīng)常有香客問我屠尊,道長,這世上最難降的妖魔是什么耕拷? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任讼昆,我火速辦了婚禮,結(jié)果婚禮上骚烧,老公的妹妹穿的比我還像新娘浸赫。我一直安慰自己,他們只是感情好赃绊,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布既峡。 她就那樣靜靜地躺著,像睡著了一般碧查。 火紅的嫁衣襯著肌膚如雪运敢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天忠售,我揣著相機(jī)與錄音传惠,去河邊找鬼。 笑死稻扬,一個胖子當(dāng)著我的面吹牛卦方,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播泰佳,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼盼砍,長吁一口氣:“原來是場噩夢啊……” “哼尘吗!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起浇坐,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤摇予,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后吗跋,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宁昭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年跌宛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片积仗。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡疆拘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寂曹,到底是詐尸還是另有隱情哎迄,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布隆圆,位于F島的核電站漱挚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏渺氧。R本人自食惡果不足惜旨涝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望侣背。 院中可真熱鬧白华,春花似錦、人聲如沸贩耐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽潮太。三九已至管搪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間消别,已是汗流浹背抛蚤。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寻狂,地道東北人岁经。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像蛇券,于是被迫代替她去往敵國和親缀壤。 傳聞我的和親對象是個殘疾皇子樊拓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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