Android自定义控件Android开发经验谈Android开发

Android----仿qq顶部底部弹性ScrollView

2018-04-30  本文已影响289人  pgydbh

目录

无标题1.png

演示

gif5新文件.gif

思路

当接触到底部,顶部,滑动时记录滑动距离,然后设置内部孩子的layout,松开之后还原。

问题&核心代码

问题1:如何记录滑动距离
答案:定义move,表示当前滑动的距离,不用管理页面滑动距离,因为滑动距离与页面距离有函数关系。
private float move;//移动距离

/**
 * 计算滑动高度与实际要移动的高度的关系
 * @param move 滑动高度
 * @return
 */
private float calculateHeight(float move){
    return move / 2;
}
问题2:页面是怎么移动的
答案:这个问题有好几种思路,比如强行加头部,加底部,更改头部底部高度,或者给inner(scrollView的唯一孩子)设置padding,但是考虑应用场景,这里应该是示用改变inner的layout位置(相对于父亲scrollView的位置)是最恰当的方法。
//设置scrollView中唯一孩子的位置
private void setLayout(float move){
    inner.layout(normal.left, normal.top + (int) calculateHeight(move), normal.right, normal.bottom + (int) calculateHeight(move));
}
问题3:怎么回去
答案:一次滑动开始时记录位置,检测到抬起时,执行回弹动画。
private Rect normal;//用于存储开始滑动时的位置
else{
    lastPosition = ev.getY();
    normal.set(inner.getLeft(), inner.getTop(), inner.getRight(), inner.getBottom());
    animStop = true;
}
case MotionEvent.ACTION_UP:
                replyView(move, 0);
                animStop = false;
                break;
问题4.如何处理双指滑动
答案:双指滑动有两种情况,这里是第一种情况,一指未抬,另一指头已落下,其实在整个过程中只有一次down事件发生,所以较好处理,为了防止第二指落下距离远离第一指最后move位置,设置检测区间
if (lastPosition != 0){
    float temp = ev.getY() - lastPosition;
    if (temp > -60 && temp < 60) {
        move += temp;
    }
    lastPosition = ev.getY();
    if ((move > 0 && isScrolledToTop) || (move < 0 && isScrolledToBottom)) {
        setLayout(move);
    }else{
        setLayout(0);
        move = 0;
        lastPosition = 0;
    }
}
问题5:如何处理动画移动时出现事件
答案:设置bool值控制动画是不是继续有效,(因为没有想到好的取消动画的办法,cancel无效),动画期间发生事件,控制动画效果失效,接着上次move执行普通滑动。
/**
 * 回弹
 * @param distance 距离
 * @param origin 终点
 */
private void replyView(final float distance, final int origin) {
    // 设置动画
    anim = ObjectAnimator.ofFloat(distance - origin, 0.0F).setDuration(500);
    anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            if (!animStop) {
                if (distance > 0) {
                    move = (float) animation.getAnimatedValue();
                    setLayout(move);
                } else {
                    move = (float) animation.getAnimatedValue();
                    setLayout(move);
                }
            }
        }
    });
    anim.start();
}
case MotionEvent.ACTION_MOVE:
                if (!animStop){
                    lastPosition = ev.getY();
                    animStop = true;
                }
问题6:如何处理关于viewpager

答案:简单之处在于viewpager是横向滑动,而scrollView是竖直滑动,所以设置控制事件阻止数值,

//处理Viewpager
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mPrevX = event.getX();
                    break;
                case MotionEvent.ACTION_MOVE:
                    System.out.println(event.getX() - mPrevX);
                    if (Math.abs(event.getX() - mPrevX) > 60) {
                        return false;
                    }
        }
        return super.onInterceptTouchEvent(event);
    }

查看demo

https://github.com/pgyCode/PullUpAndPullDown

总结

Android事件分发机制到现在还是很模糊,没有一个清晰的认识,但是有一种模糊的认识,接下拉如果有时间一定认真研究,总结其中最核心的东西出来。
上一篇下一篇

猜你喜欢

热点阅读