Android进阶之路自定义控件

使用MotionLayout实现高德地图bottomSheets

2019-06-10  本文已影响3人  兔子吃过窝边草

高德效果

高德效果

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

image
demo.apk下载体验

缘由

MotionLayout

  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>  

拆解过程

1 2 3

拆解完毕

实现过程

    /**
     * 初始位置
     */
    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;
 @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);

    }
  1. 上拉超过顶部阀值PROGRESS_TOP之后标题出现在屏幕内,其余时候出现在屏幕外即可;
  2. 初始状态这里把scaleXscaleY设为0.9结束设为了1,仅仅是为了过渡好看,你可以不用设置随意修改即可
  3. 背景色过渡,最开始透明,结束为白色背景。中间过渡关键帧95变纯白背景

结果和改进

image
  1. 动画应该是匀速的,然而setProgress(pro);却是一步直达;
  2. 设置时间间隔匀速达到最后的进度即可,源码已详细注释。改进之后见最上面效果图;
 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是我会长期维护的一个库⬇️⬇️⬇️

🍯 Sugar 简单便捷 快速开发Android项目,集合流行框架封装

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.
   
上一篇下一篇

猜你喜欢

热点阅读