Android自定义ScrollView实现阻尼回弹


Posted in Java/Android onApril 01, 2022

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

解决思路:

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

Android自定义ScrollView实现阻尼回弹

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

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;
    }
}

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

Java/Android 相关文章推荐
springboot @ConfigurationProperties和@PropertySource的区别
Jun 11 Java/Android
Spring Cache和EhCache实现缓存管理方式
Jun 15 Java/Android
分析并发编程之LongAdder原理
Jun 29 Java/Android
ObjectMapper 如何忽略字段大小写
Jun 29 Java/Android
Java Kafka 消费积压监控的示例代码
Jul 01 Java/Android
详解JAVA的控制语句
Nov 11 Java/Android
springboot 多数据源配置不生效遇到的坑及解决
Nov 17 Java/Android
解析mybatis-plus中的resultMap简单使用
Nov 23 Java/Android
Java实现简单小画板
Jun 10 Java/Android
Android Gradle 插件自定义Plugin实现注意事项
Jun 16 Java/Android
Spring Cloud OAuth2实现自定义token返回格式
Jun 25 Java/Android
SpringBoot深入分析讲解监听器模式下
Jul 15 Java/Android
Java实战之课程信息管理系统的实现
Android超详细讲解组件ScrollView的使用
Spring Boot DevTools 全局配置学习指南
Spring事务管理下synchronized锁失效问题的解决方法
Mar 31 #Java/Android
Spring依赖注入多种类型数据的示例代码
Mar 31 #Java/Android
springboot layui hutool Excel导入的实现
spring注解 @PropertySource配置数据源全流程
Mar 25 #Java/Android
You might like
js基于qrcode.js生成二维码的方法【附demo插件源码下载】
2016/12/28 PHP
php 与 nginx 的处理方式及nginx与php-fpm通信的两种方式
2018/09/28 PHP
PHP实现长轮询消息实时推送功能代码实例讲解
2021/02/26 PHP
使用原生js实现页面蒙灰(mask)效果示例代码
2014/06/20 Javascript
javascript实现单击和双击并存的方法
2014/12/13 Javascript
使用AngularJS来实现HTML页面嵌套的方法
2015/06/17 Javascript
js实现基于正则表达式的轻量提示插件
2015/08/29 Javascript
jQuery 1.9.1源码分析系列(十三)之位置大小操作
2015/12/02 Javascript
浅析使用BootStrap TreeView插件实现灵活配置快递模板
2016/11/28 Javascript
微信小程序 特效菜单抽屉效果实例代码
2017/01/11 Javascript
jQuery异步提交表单实例
2017/05/30 jQuery
Next.js项目实战踩坑指南(笔记)
2018/11/29 Javascript
Vue中util的工具函数实例详解
2019/07/08 Javascript
JS中的算法与数据结构之列表(List)实例详解
2019/08/16 Javascript
微信小程序模板消息限制实现无限制主动推送的示例代码
2019/08/27 Javascript
js回溯法计算最佳旅行线路代码实例
2019/09/11 Javascript
在vue项目中 实现定义全局变量 全局函数操作
2020/10/26 Javascript
[05:20]卡尔工作室_DOTA2新手教学_DOTA2超强新手功能
2013/04/22 DOTA
python实现颜色rgb和hex相互转换的函数
2015/03/19 Python
Python编程实现的简单Web服务器示例
2017/06/22 Python
matplotlib中legend位置调整解析
2017/12/19 Python
Ubuntu16.04/树莓派Python3+opencv配置教程(分享)
2018/04/02 Python
python 获取sqlite3数据库的表名和表字段名的实例
2019/07/17 Python
Django url,从一个页面调到另个页面的方法
2019/08/21 Python
Python操作Sqlite正确实现方法解析
2020/02/05 Python
完美解决TensorFlow和Keras大数据量内存溢出的问题
2020/07/03 Python
CSS3制作苹果风格键盘特效
2015/02/26 HTML / CSS
html5 制作地图当前定位箭头的方法示例
2020/01/10 HTML / CSS
精致的手工皮鞋:Shoe Embassy
2019/11/08 全球购物
企业统计员岗位职责
2013/12/13 职场文书
优秀小学生家长评语
2014/01/30 职场文书
超市开学活动方案
2014/03/01 职场文书
物业经理自我鉴定
2014/03/03 职场文书
旅游与环境专业求职信
2014/06/05 职场文书
标准单位租车协议书
2014/09/23 职场文书
2015年乡镇工作总结范文
2015/04/22 职场文书