Android 下拉刷新实践

2023-11-13

1. 手动实现一个下拉刷新功能。

2. 效果图:

3. view结构

 4.实现思路

<com.luocc.tim.recycler.RefreshLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/refresh"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginTop="-50dp"
        android:gravity="bottom|center" />

    <com.luocc.tim.recycler.ChatListRecycler
        android:id="@+id/chat_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</com.luocc.tim.recycler.RefreshLayout>

给refresh部分设置负的margin,让他显示在布局外面,平常时这个view是看不见的,当下拉的时候通过scrollBy/scrollTo方法滑动,松手后让他完全隐藏或者显示一个合适的高度。

5.实现要点

5.1何时滑动

本功能实现的基础是View的事件的分发、拦截、处理。

继承一个ViewGroup,按需要重写touch相关方法:

// 分发事件
public boolean dispatchTouchEvent(MotionEvent ev)

// 拦截事件
public boolean onInterceptTouchEvent(MotionEvent ev)

// 处理事件
public boolean onTouchEvent(MotionEvent event)

我对这几个方法的了解仅仅是看过一些博客,简单解释一下:

手指触摸手机屏幕,产生DOWN事件,由最外层(父View)ViewGroup的dispatchTouchEvent方法分发,到onInterceptTouchEvent,如果该方法返回true,说明本ViewGroup需要处理这个事件,下一步执行onTouchEvent;如果返回false,说明本ViewGroup不对这个事件做处理,让这个事件继续向下流转,然后重复父ViewGroup的操作,直到某个View.

一次事件一般是这样的:

ACTION_DOWN -> ACTION_MOVE .... 无数个ACTION_MOVE -> ACTION_UP

实现下拉功能需要做的就是,在满足下拉条件时,让RecyclerView的父View的onInterceptEvent方法返回true,拦截此事件,这样事件就走不到RecyclerView;不满足条件时就让事件走到RecyclerView,让其顺利滑动。

public boolean onInterceptTouchEvent(MotionEvent ev) {
    Log.d(TAG, "onInterceptTouchEvent: action = " + ev.getAction());
    switch (ev.getAction()) {
        ......
        case MotionEvent.ACTION_MOVE:
            // 是否是向下滑动
            boolean moveDown = ev.getY() - mLastY > 0;
            Log.d(TAG, "onInterceptTouchEvent: moveDown ? " + moveDown);
            // 该方法判断RecyclerView是否滑动到最顶部了
            if (moveDown && !chatList.canScrollVertically(-1)) {
                return true;
            }
            break;
        ......
    }
    return super.onInterceptTouchEvent(ev);
}

5.2 滑动冲突

当DOWN事件被某一层VIewGroup拦截后,后续的MOVE、UP事件都会直接走到它的onTouchEvent方法中,不会经过onInterceptTouchEvent,这就会产生一些问题。

当RefreshLayout(RecyclerView的父View)拦截了事件,正在处理下拉操作时,刷新区域出现,这很正常,但是这时候向上滑动会发现刷新区域已经完全闭合了,RecyclerView的滑动却不起作用。

当然,你也可以这种现象当做一个特性,但我很无聊,所以我决定把他修一下。

类似的还有,当RecyclerView在滑动的时候,滑动到最顶部继续下拉,刷新区域出不来。这是因为滑动事件被RecyclerView拦截后,后续的事件都走到他这了,导致RefreshLayout收不到滑动事件。

解决第一点的做法是,在明确知道RefreshLayout不需要处理后续MOVE事件后,手动发送一个DOWN事件。

public boolean dispatchTouchEvent(MotionEvent ev) {
    Log.d(TAG, "dispatchTouchEvent: action = " + ev.getAction());
    switch (ev.getAction()) {
        case MotionEvent.ACTION_MOVE:
            if (mChildMove) {
                dispatchTouchEvent(
                    // MetaState参数不清楚干啥用的,我就随便传了一个
                    MotionEvent.obtain(
                            SystemClock.uptimeMillis(),
                                SystemClock.uptimeMillis(),
                                MotionEvent.ACTION_DOWN,
                                ev.getX(),
                                ev.getY(),
                                ev.getMetaState()));
                return false;
            }
            break;
        case MotionEvent.ACTION_UP:
            break;
    }
    return super.dispatchTouchEvent(ev);
}

在某个View在处理事件时,后续的MOVE都往他这里跑,原因是ViewGroup里有个链表

private TouchTarget mFirstTouchTarget;

// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
    cancelAndClearTouchTargets(ev);
    resetTouchState();
}

他会记住当前正在处理事件的View,而重新发送一个DOWN事件会重置它(不知道有没有更好的办法),重置后onInterceptTouchEvent就能重新工作了。

解决第二点的做法是,子View不想处理事件了,想让父View继续拦截事件。

// 在onTouchEvent中判断
if (!canScrollVertically(-1)) {
    getParent().requestDisallowInterceptTouchEvent(false);
}

该方法让父View重新拦截。

6. 总结

实现下拉刷新最关键的地方在于理解View事件的流转机制,分发、拦截、处理。

最后附上代码:gitee地址

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 下拉刷新实践 的相关文章

随机推荐

  • Git 合并不同来源的分支 【rebase】

    Git 合并不同来源的分支 rebase 背景介绍 操作命令 背景介绍 我现在有2个流程 AAA BBB 想要把这2个不同来源的分支 合并到一起 操作命令 下载dev分支 lt 分支名 gt
  • 如何取得字符串最后一个字符

    方法一 include
  • 嵌入式基础知识-DMA

    本篇来介绍DMA的一些基础知识 1 DMA简介 DMA Direct Memory Access 中文名为直接内存访问 它是一些计算机总线架构提供的功能 能使数据从附加设备 如磁盘驱动器 直接发送到计算机主板的内存上 对应嵌入式处理器来说
  • 【研究开源机器臂】(3):低成本实现200多元实现,开源机械臂,使用Wemos D1 R32 ESP32 +Arduino CNC,可以控制机械臂往复运动了,有视频介绍和演示说明

    开源机械臂资料 文章分类 https blog csdn net freewebsys category 5782941 html 前面讨论了相关的技术调研 1 首先是设备购买机械臂 170 180 左右 大家可以去小黄鱼去找找 或者这样的
  • 问题 2020-6-10

    MySQL查考数据表中某一列是否有重复数据 解决 MySQL中 查询表 dat bill 2018 11 中字段 product id 值重复的记录 SELECT product id COUNT AS sumCount FROM dat
  • VUE中替换指定字符

    例子 我想要将变量a中的clazz替换成user 原本a的值为sys clazz 但是通过跳转到另一个页面时我需要把clazz替换成user 因此我可以这样做 this a this route query academicYearTabl
  • pip&conda 搜索包

    1 pip搜索包 pip search已经停用 需要安装pip search包 gt pip install pip search gt pip search numpy https pypi org search q numpy
  • 构造方法私有化

    1 单例设计模式 Singleton 在正常情况下 如果有一个类 那么只有通过产生对象之后才可以操作这个类范例 观察如下代码 class Singleton public void print System out println Hell
  • Web3的未来:7 个潜在的亿级机会

    Web3的未来将不仅仅是PFP的jpegs NFT市场 CEXs DEXs和DeFi协议 为了使Web3成为主流 有许多工具可以围绕一个繁荣的生态系统来构建 这里有7个隐藏的web3亿美元的机会 当下就开始建设 1 钱包整合生态系统 Web
  • 随机生User-Agent代理Ip

    import random import urllib request def url url p 49 235 246 24 8118 proxy support urllib request ProxyHandler http p op
  • opencv2 无法加载RTSP的问题

    最近通过OPENCV3 2 0加载海康摄像头 实现后续的摄像头画面拼接 但是发现拷贝了所有的opencv2的dll文件 仍然无法成功加载rtsp视频流 假设是视频流无法获取 通过VLC工具拉流 可以成功 效果如下 后来试了很多其他办法 感觉
  • linux卸载内核

    查看所安装的所有内核 ls boot 在删除旧内核之前 记住最好留有2个最近的内核 最新的和上一个版本 以防主要的版本出错 现在就让我们看看如何在Ubuntu上清理旧内核 在Ubuntu内核镜像包含了以下的包 linux image 内核镜
  • 【python实现华为OD机试真题】优雅子数组【2023 Q1

    题目描述 如果一个数组Q中出现次数最多的元素出现大于等于K次 被称为k 优雅数组 k也可以被称为优雅阈值只 例如 数组1 2 3 1 2 3 1 它是一个3 优雅数组 因为元素1出现次数大于等于3次 数组 1 2 3 1 2 就不是一一个3
  • UE4 跑酷游戏-得分机制

    得分机制 1 这次我们要做的是得分 根据人物跑过的地板来得取分数 首先进入我们的第三人称游戏模式里面 创建一些变量 2 退出第三人称游戏模式后 进入主地板蓝图后 在销毁地板之前写读取人物跑i过地板块数进行得分
  • Unity 使用Photon Server 联网开发(一)配置连接设置流程

    Photon官网首页 Photon官网 下载配置Pun与Server的教程 Photon Pun与Server的下载与配置教程 1 Pun导入项目后配置PhotonServerSettings Hosting 服务器托管方式 Not Set
  • Date 日期时间 浅层研究

    Date 日期时间 浅层研究 若需查看所有函数及属性常量 请直接翻至尾部 文章目录 Date 日期时间 浅层研究 获取当前时间 奇怪玩法 Date System Calendar 奇怪的问题探究区 查看此资料之前我们需要了解下 native
  • 论文阅读:FMCW雷达生命体征监测(心跳监测)

    论文 基于连续波雷达的非接触式生命体征监测系统设计与实现 1 雷达种类及特点 用于非接触式测量的雷达种类主要有脉冲雷达和连续波雷达两类 脉冲雷达根据发射脉冲与接收脉冲的时间差来计算所测量对象的实际距离 脉冲雷达需要将窄脉冲持续地发出 其优点
  • QVector 容器

    QVector 容器 QVector在相邻的内存中存储给定数据类型T的一组数据 在QVector前部或中间位置插入操作速度都很慢 因会导致内存中大量的数据移动 访问数据可使用下标 也可使用迭代器 继承自QVector类的子类有QPolygo
  • 如何运行后缀名为.ipynb的文件

    打开cmd 输入 pip install jupyter notebook 安装截图 下载之后 输入 jupyter notebook 之后浏览器会弹出一个页面 如图 然后就可以打开电脑里的文件 如图 也可以选择upload你的文件 打开i
  • Android 下拉刷新实践

    1 手动实现一个下拉刷新功能 2 效果图 3 view结构 4 实现思路