Android安卓下拉阻尼效果实现原理及简单实例

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

转载请注明出处:codog_main的博客

前言

  本文将通过代码讲解下拉阻尼效果的实现原理。
  实现灵感来源于这篇博客,但是这篇博客的代码并不能让我满意,或者说是糟糕的,不过还是非常感谢作者带给我的启发。
  现在大部分资讯类安卓APP都有一个下拉刷新的功能,又如微信联系人列表顶部的小程序入口,也使用了这种下拉阻尼的效果。
  我的代码主要是解释其实现原理,为方便读者理解,所以代码逻辑非常简单,但如果想要实现例如下拉刷新转动的进度圈,还需要修改代码中的MoveHeaderTask类中的onProgressUpdate方法;如果要实现滑动列表顶部加入这种下拉阻尼效果,则需要修改代码中的onTouch方法,通过判断是否到达列表顶部来决定是否触发下拉阻尼效果的逻辑代码。
  最新的微信版本还实现了一个具有惯性的滑动列表(不清楚这样表述是否正确),滑动的速度大小和小程序入口的下拉阻尼效果会形成互动,但这已不是本文讨论的重点,这需要感兴趣的读者自行对我的代码进行迭代。
  运行效果如下:

  如图,拉动"可见主体"到达一定高度,"隐藏头部"就会弹出,反之,向上滑动到一定高度,"隐藏头部"则会收回,如果未到达指定高度,则恢复原状。
  实际运行效果其实很流畅,也不会出现上图中,头部无法完全隐藏的情况,只是AS自带的录屏工具比较差劲。我不建议把这个自定义控件用在对话框类型的activity上,因为前一个activity处于可见状态,可能会占用大量算力,导致动画效果不流畅,亲测。

原理

  这种效果是通过自定义控件的方式来实现的,我自定义了一个控件类型,这个自定义控件(PullDownDumperLayout)继承自线性布局(LinearLayout)。
  用户可以下拉弹出的那个视图,例如微信的小程序列表,开发者只是将这个视图移出了父元素之外,所以不可见,我们暂且称之为隐藏头部,只有下拉到一定程度才会弹出,而主体,例如微信的联系人列表,则是可见的,布局见下图。

实现这个效果需要我们做三件工作:

  1. 隐藏作为头部的控件
  2. 监听用户对屏幕的操作事件
  3. 实现下拉回弹的动画效果

  我们这个自定义控件会自动获取内部第一个子元素充当头部,其余的元素则是充当可见的主体(详见代码中的注释)。
  基本的布局原理差不多就这样了,但是我们还需要让自定义控件监听用户的手势操作,例如上下滑动等。这里我和灵感来源的那篇博客一样,让自定义控件实现View.OnTouchListener接口,实现内部的onTouch方法可以监听来自屏幕的所有触摸操作。代码中我让头部和第二个子元素(可见的主体)注册了这个监听器,这是为了方便读者理解,读者可根据自己的需求进行修改。
  注意,对于不能监听屏幕触摸事件的控件需要添加:
    android:clickable="true"
  至此,我们已经可以进行布局和监听用户手势了,但是还需要实现一个头部展开和隐藏的动画效果。当用户将隐藏头部下拉或上滑到一定高度时,这个效果就会被触发,这需要依赖上面所述的onTouch方法。动画效果的实现需要另开一个线程进行操作,线程的启动方式我们可以采用继承AsyncTask类来实现。
  除此之外,我们可能会多次复用这个控件,所以在自定义控件类的最后还需要一些调整参数的set方法。
  这里提个醒,在接下来的代码中,我们的自定义控件因为继承自LinearLayout,里面需要重写onLayout方法,而onLayout方法顾名思义就是布局,这个方法在Activity中的onCreate方法执行之后才会被调用,所以我们可以在Activity的onCreate方法中利用findViewById获取实例,调用上面提到的set方法进行参数的初始化。
  LinearLayout中不止onLayout一个方法,详细解析请读者移步其他关于XML标签加载过程的文章,这里不做赘述。

代码

PullDownDumperLayout .java:

public class PullDownDumperLayout extends LinearLayout implements View.OnTouchListener {
   
   

    /**
     * 取布局中的第一个子元素为下拉隐藏头部
     */
    private View mHeadLayout;

    /**
     * 隐藏头部布局的高的负值
     */
    private int mHeadLayoutHeight;

    /**
     * 隐藏头部的布局参数
     */
    private MarginLayoutParams mHeadLayoutParams;

    /**
     * 判断是否为第一次初始化,第一次初始化需要把headView移出界面外
     */
    private boolean mOnLayoutIsInit=false;

    /**
     * 移动时,前一个坐标
     */
    private float mMoveY;

    /**
     * 如果为false,会退出头部展开或隐藏动画
     */
    private boolean mChangeHeadLayoutTopMargin;

    /**
     * 触发动画的分界线,由mRatio计算得到
     */
    private int mBoundary;

    /**
     * 头部布局的隐藏和展开速度,以及单次执行时间
     */
    private int mHeadLayoutHideSpeed;
    private int mHeadLayoutUnfoldSpeed;
    private long mSleepTime;

    /**
     * 触发动画的分界线,头部布局上半部分和整体高度的比例
     */
    private double mRatio;

    public PullDownDumperLayout(Context context, AttributeSet attrs) {
   
   
        super(context, attrs);
        //初始化参数,根据自己的需求调整
        mHeadLayoutHideSpeed=-20;
        mHeadLayoutUnfoldSpeed=20;
        mSleepTime=10;
        mRatio=0.5;
    }

    /**
     * 布局开始设置每一个控件
     * 在activity的onCreate执行之后才会执行
     * 因此可以在onCreate中调用set方法设置参数
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
   
   
        super.onLayout(changed, l, t, r, b);
        if(!mOnLayoutIsInit && changed) {
   
   
            //将第一个子元素作为头部移出界面外
            mHeadLayout = this.getChildAt(0);
            mHeadLayoutHeight=-mHeadLayout.getHeight();
            mBoundary=(int)(mRatio*mHeadLayoutHeight);//计算触发动画分界线
            mHeadLayoutParams=(MarginLayoutParams) mHeadLayout.getLayoutParams();
            mHeadLayoutParams.topMargin=mHeadLayoutHeight;
            mHeadLayout.setLayoutParams(mHeadLayoutParams);
            //TODO 设置手势监听器,不能触碰的控件需要添加android:clickable="true"
            getChildAt(1).setOnTouchListener(this);
            mHeadLayout.setOnTouchListener(this);
            //标记已被初始化
            mOnLayoutIsInit=true;
        }
    }

    /**
     * 屏幕触摸操作监听器
     * @return 返回false表示在执行onTouch后会继续执行onTouchEvent
     */
    @Override
    public boolean onTouch(View v, MotionEvent event) {
   
   
        switch (event.getAction()) {
   
   
            case MotionEvent.ACTION_DOWN:
                mMoveY=event.getRawY();//捕获按下时的坐标,初始化mMoveY
                mChangeHeadLayoutTopMargin=false;
                break;
            case MotionEvent.ACTION_MOVE:
                float currY=event.getRawY();
                int vector=(int)(currY-mMoveY);//向量,用于判断手势的上滑和下滑
                mMoveY=currY;
                //判断是否为滑动
                if(Math.abs(vector)==0){
   
   
                    return false;
                }
                //头部完全隐藏时不再向上滑动
                
带阻尼回弹效果的RecyclerView 一、前提 接到新需求,要求列表滑动过程增加阻尼回弹效果,且即使列表不能填充一整个屏幕的情况下也支持滑动。 有人说,给RecyclerView加上 android:overScrollMode="always" 就行了,事实证明,NO!这个东西只是在滑动到边缘是多了个水波阴影而已,没有阻尼回弹。 又有人说,给ListView加上 android:overScrollMode="always" ... 阅读详情

相关推荐

安卓实现垂直滚动阻尼效果,超级超级简单

【代码】安卓实现垂直滚动阻尼效果,超级超级简单。

那人独钓寒江雪 519

实现Android ListView带阻尼效果的下拉上拉刷新功能

ListView作为Android开发中极为重要的组件,其刷新机制对提升用户体验至关重要。本章将对ListView的刷新机制进行深入介绍,从基础的刷新流程开始,逐步分析其背后的原理和常见的优化手段。自定义Header/Footer是提升用户界面个性化和用户体验的重要手段。在ListView或RecyclerView中,Header和Footer区域提供了展示额外信息的空间,比如广告、重要通知、引导页等。通过自定义实现,开发者可以将更多的业务逻辑与界面风格融入应用,以满足产品的特定需求。

weixin_42575109的博客 960

android 有阻尼下拉刷新列表的实现方法

本文将会介绍有阻尼下拉刷新列表的实现,先来看看效果预览: 这是下拉状态: 这是下拉松开手指后listView回滚到刷新状态时的样子: 1. 如何调用 虽然效果图看起来样子不太好看,主要是因为那个蓝色的背景对不对,没关系,这只是一个背景而已,在了解了我们这个下拉刷新列表的实现之后,你就可以很轻松地修改这个背景,从而实现你想要的UI效果!话不多说,下面我们先来讲讲这个下拉刷新列表是如何使用的,这也是我们编写代码所要实现的目标。 final PullToRefreshListView eListView = (PullToRefreshListView) rootView.findVi

滑模控制实战:用Python从零实现SMC算法(附完整代码)

本文通过Python实战,从零详解滑模控制(SMC)算法的核心原理与代码实现。文章聚焦于一阶系统,使用NumPy和Matplotlib逐步构建控制器,并针对SMC固有的抖振问题,详细讲解了边界层法的原理与实现,提供了完整的代码和参数调优指南,帮助读者直观理解并掌握这一鲁棒性极强的非线性控制方法。

weixin_29285605的博客 465

Redis TLS 加密传输实战:从证书生成到安全配置

本文详细介绍了为Redis启用TLS加密传输的完整实战流程。从阐述TLS加密的必要性开始,逐步指导读者生成自签名CA和服务器证书,并深入解析Redis服务端的核心安全配置,包括端口设置、认证模式与性能优化。最后,提供了主流客户端连接示例及生产环境下的排错与运维最佳实践,帮助构建安全的Redis通信通道。

weixin_29274969的博客 515

ESP32脑机接口开发板:EEG信号采集与应用开发指南

脑机接口(BCI)技术通过采集脑电图(EEG)信号实现人机交互,其核心在于高精度生物电信号采集与实时处理。基于ESP32微控制器的开发平台结合专业级ADS1299 ADC芯片,可构建8通道EEG信号采集系统,支持250SPS采样率与可编程增益。这种嵌入式解决方案在科研教育领域具有显著优势,既能满足信号质量要求,又大幅降低开发门槛。典型应用包括实时脑电波可视化、注意力检测等BCI场景,通过Python生态工具链(如BrainFlow、LSL)可快速实现数据处理。开发时需注意电极阻抗控制(<10kΩ)和50/6

weixin_42518874的博客 343

抖音用户评论和ID的采集方法

随着5G时代的普及,各大运营商的套餐纷纷推出,使得流量成本进一步降低。同时5G线路高宽带、低延迟的技术特性和手机普及,让视频用户访问量快速增加。 由于大量的视频工具和拍摄APP的提供,让短视频的拍摄、制作包括发行都变得更加简单便捷,越来越多的普通人可以投入视频创作,创造了更丰富的互联网内容,视频作者从视频推广中能获取粉丝和推广收益。 目前抖音就是目前最重要的短视频平台之一,要及时获取上面的用户评论和ID,需要结合爬虫代理实现采集方案,下面的代码就能快速采集相关信息: package htmlunit; i

ssbt_2020的博客 6787

解密电商大促背后的智慧:LSTM与ARIMA模型的双剑合璧

举个例子,如果某东发现某款新推出的电子产品在促销前夕搜索量突然暴涨,虽然这个产品没有过去的销售记录,但LSTM模型可以结合用户对类似产品的行为模式、实时的市场反馈,迅速捕捉到这些变化,并预测这款产品在大促期间的需求会急剧上升。想象一下,如果预测不准确,备货不足,商品很快就卖光了,大量用户可能在促销期间买不到想要的商品,这不仅让用户失望,还会影响某东的品牌形象。这种情况就像我们看一部很长的电视剧,看得越久,前面的情节可能就越来越模糊,到最后有些关键的情节细节都忘了,影响了对整个剧情的理解。

小蒋聊技术 2090

SAP ERPsim生产拓展实验报告

ERP课的实验和实践,也是SAP竞赛玩的东西,除了界面不华丽外很像游戏而且是团队型的pvp大乱斗

m0_67262022的博客 2020

Synopsys PCIe IP Loopback功能深度解析与应用实践

本文深入解析了Synopsys PCIe IP的Loopback功能,包括本地数字环回和远程环回两种模式的应用实践。通过详细的寄存器配置步骤和实战案例,帮助开发者快速掌握PCIe芯片验证与调试技术,提升系统开发效率。特别适合PCIe协议相关的硬件工程师和验证工程师参考。

weixin_29198045的博客 530

从0到1构建数据分析AI Agent:数据采集、洞察生成与报告自动化

在企业数字化转型的今天,数据分析师80%的时间被消耗在重复取数、数据清洗、报表排版等低价值劳动上,真正用于深度业务洞察和决策支持的时间不足20%。本文将从核心概念、技术原理、代码实现、落地实践全维度,教你从0到1搭建一套完整的数据分析AI Agent系统,实现「用户用自然语言提需求→Agent自动采集多源数据→AI自动挖掘业务洞察→自动生成符合业务要求的多格式报告」的全链路自动化,平均可将需求响应速度从天级压缩到分钟级,释放70%以上的分析师人力成本。

AI Python 编程之道的博客 471

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

上一篇: 关于“代码远程线程注入”的核心代码分享
下一篇: Android安卓实现原生文件选择器(浏览器)多选文件功能
codig
博客等级 码龄9年 10粉丝 3原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值