android开发中,当一个页面存放的控件超出屏幕时,通常需要使用scrollview来包裹布局。这样用户可以通过手指的滑动来查看超出屏幕的部分。然而当scrollview滑动到边界时,继续滑动只会显示一个阴影效果。ios自带的控件却可以实现边界的阻尼回弹效果,这种阻尼回弹效果会让用户有更好的使用体验。这里给出一个android上的实现方案

解决思路:

scrollview使用时要求内部有且仅一个子view。当scrollview滑动到边界时,让子view在scrollview中随着手指按一定的规则进行平移,模拟出拉伸效果。当手指松开时,再让子view恢复拉伸前的位置,模拟出回弹效果。

完整的代码如下,详细的原理见注释即可

public class stretchscrollview extends nestedscrollview {

    // 子view
    private view innerview;
    // 上次手势事件的y坐标
    private float mlasty;
    // 记录子view的正常位置
    private rect normal = new rect();

    public stretchscrollview(context context, attributeset attrs) {
        super(context, attrs);
    }

    @override
    protected void onfinishinflate() {
        initview();
        super.onfinishinflate();
    }

    /**
     * 获取scrollview的子布局
     */
    private void initview() {
        // 去除原本scrollview滚动到边界时的阴影效果
        setoverscrollmode(over_scroll_never);
        if (getchildat(0) != null) {
            innerview = getchildat(0);
        }
    }

    @override
    public boolean ontouchevent(motionevent ev) {
        switch (ev.getaction()) {
            case motionevent.action_up:
                // 手指松开恢复
                if (!normal.isempty()) {
                    plananimation();
                    normal.setempty();
                    mlasty = 0;
                }
                break;
            case motionevent.action_move:
                float currenty = ev.gety();
                // 滑动距离
                int distancey = (int) (mlasty - currenty);

                // 处理y轴的滚动事件,当滚动到最上或者最下时需要移动布局
                // 手指刚触及屏幕时,也会触发此事件,此时mlasty的值还是0,会立即触发一个比较大的移动。这里过滤掉这种情况
                if (isneedtranslate() && mlasty != 0) {
                    if (normal.isempty()) {
                        // 保存正常的布局位置
                        normal.set(innerview.getleft(), innerview.gettop(), innerview.getright(), innerview.getbottom());
                    }
                    // 移动布局, 使distance / 2 防止平移过快
                    innerview.layout(innerview.getleft(), innerview.gettop() - distancey / 2,
                            innerview.getright(), innerview.getbottom() - distancey / 2);
                }
                mlasty = currenty;
                break;
        }
        return super.ontouchevent(ev);
    }

    /**
     * 回缩动画
     */
    public void plananimation() {
        // 开启移动动画
        translateanimation animation = new translateanimation(0, 0, innerview.gettop(), normal.top);
        animation.setduration(200);
        innerview.startanimation(animation);
        // 补间动画并不会真正修改innerview的位置,这里需要设置使得innerview回到正常的布局位置
        innerview.layout(normal.left, normal.top, normal.right, normal.bottom);
    }

    /**
     * 是否需要y移动布局
     */
    public boolean isneedtranslate() {
        int offset = innerview.getmeasuredheight() - getheight();
        int scrolly = getscrolly();
        // 顶部或者底部
        return scrolly == 0 || scrolly == offset;
    }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。