双层布局 DoubleLayerLayout

来源:互联网 发布:淘宝店铺白色导航制作 编辑:程序博客网 时间:2024/04/30 07:58

前言

之前用过赶集生活 App,主界面向下滑动可以露出天气信息。效果不错。不仅充分的利用了空间,也给用户以发现的乐趣。所以,我也做了一个这样的布局。

效果图

这里写图片描述

思路

DoubleLayerLayout 继承自 RelativeLayout。正常情况下,foreground view 挡住 background view。滑动时,让 foreground 跟随手指移动,露出 background view。同时,关注手指移动的加速度,实现“猛地”一滑,foreground view 顺势滑开的效果。

参考链接

链接1

知识点

  1. View 中几个比较重要的 hook 的调用顺序:
    onFinishInflate();
    onAttachedToWindow();
    onWindowVisiblityChanged();
    onMeasure();
    onSizeChanged();
    onLayout();
    onDraw();
  2. 打算处理手势之前,触发一个cancelEvent
MotionEvent cancelEvent = MotionEvent.obtain(ev);  cancelEvent.setAction(MotionEvent.ACTION_CANCEL |                       (ev.getActionIndex()<< MotionEvent.ACTION_POINTER_INDEX_SHIFT));onTouchEvent(cancelEvent);

源码

package com.example.com.example.widget;import android.content.Context;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.animation.AccelerateDecelerateInterpolator;import android.view.animation.Animation;import android.view.animation.Transformation;import android.widget.RelativeLayout;import android.widget.Scroller;public class DoubleLayerLayout extends RelativeLayout {    private static final int SNAP_VELOCITY_THRESHOLD = 600;    private static final int DURATION = 200;    private View bgView;    private View fgView;    private Scroller scroller;    private VelocityTracker velocityTracker;    private float lastY;    private int originalTop;    /**     * if totalDy > dyThreshold, show {@link #bgView}     */    private float dyThreshold;    public DoubleLayerLayout(Context context) {        super(context);        init();    }    public DoubleLayerLayout(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public DoubleLayerLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    private void init(){        scroller = new Scroller(getContext());    }    @Override    protected void onFinishInflate() {        super.onFinishInflate();        bgView = getChildAt(0);        fgView = getChildAt(1);    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        super.onLayout(changed, l, t, r, b);        originalTop = getTop();        dyThreshold = getHeight()*0.25f;    }    @Override    public boolean onTouchEvent(MotionEvent event) {//        return super.onTouchEvent(event);        if (super.onTouchEvent(event)) {            return true;        }        addVelocityTracker(event);        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                handleDownEvent(event);                break;            case MotionEvent.ACTION_MOVE:                handleMoveEvent(event);                break;            case MotionEvent.ACTION_UP:            case MotionEvent.ACTION_CANCEL:                handleUpEvent(event);                break;        }        return true;    }    private void handleDownEvent(MotionEvent ev) {        lastY = ev.getY();    }    private void handleMoveEvent(MotionEvent ev) {        // update the position of fgView        float currY = ev.getY();        float dy = currY - lastY;        lastY = currY;        fgView.layout(fgView.getLeft(), ((int) (fgView.getTop() + dy)), fgView.getRight(), ((int) (fgView.getTop() + getMeasuredHeight() + dy)));//        fgView.scrollBy(0, (int) -dy);    }    private void handleUpEvent(MotionEvent ev) {        float totalDy = fgView.getTop() - originalTop;        float snapVelocity = getYVelocity();        if ((totalDy < dyThreshold && snapVelocity < SNAP_VELOCITY_THRESHOLD) || snapVelocity < -SNAP_VELOCITY_THRESHOLD){            // reset//            fgView.layout(fgView.getLeft(), originalTop, fgView.getRight(), originalTop+getMeasuredHeight());            animateLayout(fgView, originalTop-fgView.getTop());        } else {            // show bgView//            fgView.layout(fgView.getLeft(), ((int) (originalTop + getMeasuredHeight() * 0.5f)), getRight(), ((int) (originalTop + getMeasuredHeight() * 1.5f)));            animateLayout(fgView, ((int) (originalTop + getMeasuredHeight() * 0.5f - fgView.getTop())));        }        recycleVelocityTracker();    }    private void animateLayout(View view, int dy){        Animation layoutAnimation = new LayoutAnimation(view, dy);        layoutAnimation.setDuration(DURATION);        layoutAnimation.setInterpolator(new AccelerateDecelerateInterpolator());        startAnimation(layoutAnimation);    }    private void addVelocityTracker(MotionEvent event) {        if (velocityTracker == null) {            velocityTracker = VelocityTracker.obtain();        }        velocityTracker.addMovement(event);    }    private void recycleVelocityTracker() {        if (velocityTracker != null) {            velocityTracker.recycle();            velocityTracker = null;        }    }    private int getYVelocity() {        velocityTracker.computeCurrentVelocity(1000);        int velocity = (int) velocityTracker.getYVelocity();        return velocity;    }    class LayoutAnimation extends Animation {        private View view;        private int dy;        private int t;        private int h;        private int l;        private int r;        public LayoutAnimation(View view, int dy) {            super();            this.view = view;            this.dy = dy;            t = view.getTop();            l = view.getLeft();            r = view.getRight();            h = view.getMeasuredHeight();        }        @Override        protected void applyTransformation(float interpolatedTime,                                           Transformation transformation) {            int dy = (int) (interpolatedTime * this.dy);            view.layout(l, t + dy, r, t + h + dy);        }    }}
0 0
原创粉丝点击