使用MotionLayout实现高德地图bottomSheets效果

栏目: Android · 发布时间: 6年前

高德效果

使用MotionLayout实现高德地图bottomSheets效果

以下是我用motionlayout实现的效果,没有达到丝滑流畅,优化就看小伙伴你了

使用MotionLayout实现高德地图bottomSheets效果
demo.apk下载体验

缘由

  • 使用高德地图的时候看着这种体验很好,随后就想试试怎么达到类似效果
  • 最近正在看MotionLayout的东西,正好就尝试尝试

MotionLayout

  • 「译」 MotionLayout 介绍 (Part I - IV) 系列教会你如何使用MotionLayout
  • 这里不做过多描述,总结一下在xml文件夹下创建 xxscene.xml 主要用于描述场景动画的关键帧和view状态变化等
  • xxscene.xml 内容包括 主要为3个关键内容:
  1. Transition 过渡

constraintSetStart :启动约束场景

constraintSetEnd :结束约束场景

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

  1. KeyFrameSet 关键帧集合

KeyAttribute 关键帧

app:framePosition 位置,进度

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>  
复制代码

拆解过程

  • 高德地图是上拉之后是三段式的,如图所示
使用MotionLayout实现高德地图bottomSheets效果
使用MotionLayout实现高德地图bottomSheets效果
使用MotionLayout实现高德地图bottomSheets效果
MotionLayout
MotionLayout

拆解完毕

实现过程

  • 设置阀值
/**
     * 初始位置
     */
    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;
    /**
     * 结束位置 
     */
    public final static float PROGRESS_END = 1.0f;
复制代码
  • 重写 MotionLayoutonTouchEvent 事件 ,使用 hasMiddle 布尔值判断是否有中间状态
@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 之后标题出现在屏幕内,其余时候出现在屏幕外即可;
  2. 初始状态这里把 scaleXscaleY 设为0.9结束设为了1,仅仅是为了过渡好看,你可以不用设置随意修改即可
  3. 背景色过渡,最开始透明,结束为白色背景。中间过渡关键帧95变纯白背景

结果和改进

  • 设置允许中间状态后,之后进入下一步的过程,如图,过于生硬
使用MotionLayout实现高德地图bottomSheets效果
  • 改进方向
setProgress(pro);
private void handleProgress(float progress) {
        //如果需要设置的进度和当前进度相同不做处理
        if (progress == getProgress()){
            return;
        }
        //动画播放时间底值
        long time = 200;
        //进度间隔 >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为次数 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是我会长期维护的一个库:arrow_down::arrow_down::arrow_down:

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.
   
复制代码

以上所述就是小编给大家介绍的《使用MotionLayout实现高德地图bottomSheets效果》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Perl入门经典

Perl入门经典

[美]Curtis "Ovid" Poe / 朱允刚、韩雷、叶斌 / 清华大学出版社 / 2013-9-20 / 78.00

作为最有影响力的编程语言之一,Perl被广泛用在Web开发、数据处理和系统管理中。无论是Perl新手,还是想要加强自己实战技能的Perl程序员,《Perl入门经典》都提供了处理日常情况所需的各种技术。凭借十多年的Perl经验,作者Curtis“Ovid”Poe一开始先简单回顾了Perl的基础知识,然后以此为出发点,举例说明了Perl在工作场所中的各种真实用法。此外,书中还包含了一些动手练习、宝贵建......一起来看看 《Perl入门经典》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器