Kotlin--›Android RecyclerView滚动处理(滚动到底部/顶部/居中/偏移/动画等特性)

2023-05-16

之前写过一篇Android–>RecyclerView 显示底部,滚动底部(无动画)文章.

当时是为了满足需求, 没想太多顺手写的. 虽然功能上能满足, 但是代码上还是有点low.

这几天, 我的徒弟傻豆 在写一个IM项目, 需要滚动到底部. 于是我重写了一个ScrollHelper滚动操作类.

文章目录

  • 特性
  • 需求分析
    • 1.滚动, 滚动偏移, 滚动动画
    • 2. 滚动到顶部, 底部, 居中
    • 3.锁定滚动
  • 使用方法
    • 1.初始化
    • 2.操作方法
    • 3.锁定滚动
  • 联系作者


特性

  • 1.支持滚动时的动画控制
  • 2.支持滚动到任意position
  • 3.支持滚动offset控制
  • 4.支持滚动到顶部or底部or居中
  • 5.支持锁定滚动, 短时间之内强制滚动到目标position
  • 6.支持智能锁定滚动(达到某个条件, 自动滚动到设置的目标position)

需求分析

1.滚动, 滚动偏移, 滚动动画

需要动画使用:

//带偏移, 带动画
androidx.recyclerview.widget.RecyclerView#smoothScrollBy
//滚动, 带动画
androidx.recyclerview.widget.RecyclerView#smoothScrollToPosition

不需要动画使用:

//带偏移, 不带动画
androidx.recyclerview.widget.RecyclerView#scrollBy
//滚动, 不带动画
androidx.recyclerview.widget.RecyclerView#scrollToPosition

注意:
如果触发了androidx.recyclerview.widget.RecyclerView.ItemAnimator动画,
那么androidx.recyclerview.widget.RecyclerView#scrollToPositionandroidx.recyclerview.widget.RecyclerView#smoothScrollToPosition
都会在一定程度上产生滚动动画.

提示
为什么需要使用scrollToPositionscrollBy呢?
这里给大家推荐一套滚动方案:
如果需要滚动的目标已经出现在屏幕内, 那么直接使用scrollByorsmoothScrollBy.
如果需要滚动的目标没有出现在屏幕内, 那么先使用scrollToPositionorsmoothScrollToPosition,再使用scrollByorsmoothScrollBy.

如果调用了androidx.recyclerview.widget.RecyclerView.Adapter#notifyItemInserted, 那么scrollToPositionorsmoothScrollToPosition方法可能会无效果.通常此时都需要使用post, 文章后面会给出我的方法.

2. 滚动到顶部, 底部, 居中

需要细粒度的控制滚动, 必须要保证目标已经出现的屏幕内, 才看完美控制.

控制方法就是scrollByorsmoothScrollBy.

/**当需要滚动的目标位置已经在屏幕上可见*/
internal fun scrollWithVisible(scrollParams: ScrollParams) {
    when (scrollType) {
        SCROLL_TYPE_NORMAL -> {//不处理
            //nothing
        }
        SCROLL_TYPE_TOP -> {//滚动到顶部
            viewByPosition(scrollParams.scrollPosition)?.also { child ->
                recyclerView?.apply {
                    val dx = layoutManager!!.getDecoratedLeft(child) -
                            paddingLeft - scrollParams.scrollOffset

                    val dy = layoutManager!!.getDecoratedTop(child) -
                            paddingTop - scrollParams.scrollOffset

                    if (scrollParams.scrollAnim) {
                        smoothScrollBy(dx, dy)
                    } else {
                        scrollBy(dx, dy)
                    }
                }
            }
        }
        SCROLL_TYPE_BOTTOM -> {//滚动到底部
            viewByPosition(scrollParams.scrollPosition)?.also { child ->
                recyclerView?.apply {
                    val dx =
                        layoutManager!!.getDecoratedRight(child) -
                                measuredWidth + paddingRight + scrollParams.scrollOffset
                    val dy =
                        layoutManager!!.getDecoratedBottom(child) -
                                measuredHeight + paddingBottom + scrollParams.scrollOffset

                    if (scrollParams.scrollAnim) {
                        smoothScrollBy(dx, dy)
                    } else {
                        scrollBy(dx, dy)
                    }
                }
            }
        }
        SCROLL_TYPE_CENTER -> {//滚动到居中
            viewByPosition(scrollParams.scrollPosition)?.also { child ->

                recyclerView?.apply {
                    val recyclerCenterX =
                        (measuredWidth - paddingLeft - paddingRight) / 2 + paddingLeft

                    val recyclerCenterY =
                        (measuredHeight - paddingTop - paddingBottom) / 2 + paddingTop

                    val dx = layoutManager!!.getDecoratedLeft(child) - recyclerCenterX +
                            layoutManager!!.getDecoratedMeasuredWidth(child) / 2 + scrollParams.scrollOffset

                    val dy = layoutManager!!.getDecoratedTop(child) - recyclerCenterY +
                            layoutManager!!.getDecoratedMeasuredHeight(child) / 2 + scrollParams.scrollOffset

                    if (scrollParams.scrollAnim) {
                        smoothScrollBy(dx, dy)
                    } else {
                        scrollBy(dx, dy)
                    }
                }
            }
        }
    }
}

private fun viewByPosition(position: Int): View? {
    return recyclerView?.layoutManager?.findViewByPosition(position)
}

3.锁定滚动

锁定滚动我这里使用了ViewTreeObserver.OnGlobalLayoutListenerorViewTreeObserver.OnDrawListener当做触发时机, 这样就不用自己写handle post了, 而且触发更及时.

inner abstract class LockScrollListener : ViewTreeObserver.OnGlobalLayoutListener,
   ViewTreeObserver.OnDrawListener,
   IAttachListener, Runnable {

   /**激活滚动动画*/
   var scrollAnim: Boolean = true
   /**激活第一个滚动的动画*/
   var firstScrollAnim: Boolean = false

   /**不检查界面 情况, 强制滚动到最后的位置. 关闭后. 会智能判断*/
   var force: Boolean = false

   /**第一次时, 是否强制滚动*/
   var firstForce: Boolean = true

   /**滚动阈值, 倒数第几个可见时, 就允许滚动*/
   var scrollThreshold = 2

   /**锁定需要滚动的position, -1就是最后一个*/
   var lockPosition = RecyclerView.NO_POSITION

   /**是否激活功能*/
   var enableLock = true

   /**锁定时长, 毫秒*/
   var lockDuration: Long = -1

   //记录开始的统计时间
   var _lockStartTime = 0L

   override fun run() {
       if (!enableLock || recyclerView?.layoutManager?.itemCount ?: 0 <= 0) {
           return
       }

       isScrollAnim = if (firstForce) firstScrollAnim else scrollAnim
       scrollType = SCROLL_TYPE_BOTTOM

       val position =
           if (lockPosition == RecyclerView.NO_POSITION) lastItemPosition() else lockPosition

       if (force || firstForce) {
           scroll(position)
           onScrollTrigger()
           L.i("锁定滚动至->$position $force $firstForce")
       } else {
           val lastItemPosition = lastItemPosition()
           if (lastItemPosition != RecyclerView.NO_POSITION) {
               //智能判断是否可以锁定
               if (position == 0) {
                   //滚动到顶部
                   val findFirstVisibleItemPosition =
                       recyclerView?.layoutManager.findFirstVisibleItemPosition()

                   if (findFirstVisibleItemPosition <= scrollThreshold) {
                       scroll(position)
                       onScrollTrigger()
                       L.i("锁定滚动至->$position")
                   }
               } else {
                   val findLastVisibleItemPosition =
                       recyclerView?.layoutManager.findLastVisibleItemPosition()

                   if (lastItemPosition - findLastVisibleItemPosition <= scrollThreshold) {
                       //最后第一个或者最后第2个可见, 智能判断为可以滚动到尾部
                       scroll(position)
                       onScrollTrigger()
                       L.i("锁定滚动至->$position")
                   }
               }
           }
       }

       firstForce = false
   }

   var attachView: View? = null

   override fun attach(view: View) {
       detach()
       attachView = view
   }

   override fun detach() {
       attachView?.removeCallbacks(this)
   }

   /**[ViewTreeObserver.OnDrawListener]*/
   override fun onDraw() {
       initLockStartTime()
       onLockScroll()
   }

   /**[ViewTreeObserver.OnGlobalLayoutListener]*/
   override fun onGlobalLayout() {
       initLockStartTime()
       onLockScroll()
   }

   open fun initLockStartTime() {
       if (_lockStartTime <= 0) {
           _lockStartTime = nowTime()
       }
   }

   open fun isLockTimeout(): Boolean {
       return if (lockDuration > 0) {
           val nowTime = nowTime()
           nowTime - _lockStartTime > lockDuration
       } else {
           false
       }
   }

   open fun onLockScroll() {
       attachView?.removeCallbacks(this)
       if (enableLock) {
           if (isLockTimeout()) {
               //锁定超时, 放弃操作
           } else {
               attachView?.post(this)
           }
       }
   }

   open fun onScrollTrigger() {

   }
}

/**锁定滚动到最后一个位置*/
inner class LockScrollLayoutListener : LockScrollListener() {

   override fun attach(view: View) {
       super.attach(view)
       view.viewTreeObserver.addOnGlobalLayoutListener(this)
   }

   override fun detach() {
       super.detach()
       attachView?.viewTreeObserver?.removeOnGlobalLayoutListener(this)
   }
}

/**滚动到0*/
inner class FirstPositionListener : LockScrollListener() {

   init {
       lockPosition = 0
       firstScrollAnim = true
       scrollAnim = true
       force = true
       firstForce = true
   }

   override fun attach(view: View) {
       super.attach(view)
       view.viewTreeObserver.addOnDrawListener(this)
   }

   override fun detach() {
       super.detach()
       attachView?.viewTreeObserver?.removeOnDrawListener(this)
   }

   override fun onScrollTrigger() {
       super.onScrollTrigger()
       if (isLockTimeout() || lockDuration == -1L) {
           detach()
       }
   }
}

private interface IAttachListener {
   fun attach(view: View)

   fun detach()
}

//滚动参数
internal data class ScrollParams(
   var scrollPosition: Int = RecyclerView.NO_POSITION,
   var scrollType: Int = SCROLL_TYPE_NORMAL,
   var scrollAnim: Boolean = true,
   var scrollOffset: Int = 0
)

使用方法

复制源码到工程即可, 就一个类文件.

1.初始化

val scrollHelper = ScrollHelper()
scrollHelper.attach(recyclerView)

2.操作方法

每次触发滚动时, 可配置的参数:

/**触发滚动是否伴随了adapter的addItem*/
var isFromAddItem = false
/**滚动是否需要动画*/
var isScrollAnim = false
/**滚动类别*/
var scrollType = SCROLL_TYPE_NORMAL
/**额外的偏移距离*/
var scrollOffset: Int = 0


/**滚动类别: 默认不特殊处理. 滚动到item显示了就完事*/
const val SCROLL_TYPE_NORMAL = 0
/**滚动类别: 将item滚动到第一个位置*/
const val SCROLL_TYPE_TOP = 1
/**滚动类别: 将item滚动到最后一个位置*/
const val SCROLL_TYPE_BOTTOM = 2
/**滚动类别: 将item滚动到居中位置*/
const val SCROLL_TYPE_CENTER = 3
//滚动到指定位置
ScrollHelper#scroll(position)

//滚动到底部
ScrollHelper#scrollToLast()

//滚动到顶部
ScrollHelper#scrollToFirst()

3.锁定滚动

锁定滚动配置参数:

/**激活滚动动画*/
var scrollAnim: Boolean = true
/**激活第一个滚动的动画*/
var firstScrollAnim: Boolean = false

/**不检查界面 情况, 强制滚动到最后的位置. 关闭后. 会智能判断*/
var force: Boolean = false

/**第一次时, 是否强制滚动*/
var firstForce: Boolean = true

/**滚动阈值, 倒数第几个可见时, 就允许滚动*/
var scrollThreshold = 2

/**锁定需要滚动的position, -1就是最后一个*/
var lockPosition = RecyclerView.NO_POSITION

/**是否激活功能*/
var enableLock = true

/**锁定时长, 毫秒*/
var lockDuration: Long = -1
//锁定滚动
ScrollHelper#lockPosition()

砖厂地址


群内有各(pian)种(ni)各(jin)样(qun)的大佬,等你来撩.

联系作者

点此快速加群

请使用QQ扫码加群, 小伙伴们都在等着你哦!

关注我的公众号, 每天都能一起玩耍哦!

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

Kotlin--›Android RecyclerView滚动处理(滚动到底部/顶部/居中/偏移/动画等特性) 的相关文章

  • Nginx负载均衡:分布式/热备Web Server的搭建

    Nginx是一款轻量级的Web 服务器 反向代理服务器 及电子邮件 xff08 IMAP POP3 xff09 代理服务器 xff0c 并在一个BSD like 协议下发行 由俄罗斯的程序设计师Igor Sysoev所开发 xff0c 供俄
  • 数据切分——Atlas读写分离Mysql集群的搭建

    关于数据切分的原理可以参见博客 xff1a http blog csdn net jhq0113 article details 44226789 关于Atlas的介绍可以参见博客 xff1a http blog csdn net jhq0
  • 数据切分——Mysql分区表的建立及性能分析

    Mysql的安装方法可以参考 xff1a http blog csdn net jhq0113 article details 43812895 Mysql分区表的介绍可以参考 xff1a http blog csdn net jhq011
  • 利用C++求解一元二次方程

    题目 xff1a 求解一元二次方程 xff1a ax 43 bx 43 c 61 0 的解 xff0c 其中a 61 1 b 61 3 c 61 2 分析 xff1a 大家都知道一元二次方程的解有三种情况 xff0c 即考虑 61 xff0
  • windows server 2016 中users组用户权限实探

    users 组用户不可删除他人创的文件和文件夹 但可在他人创建的文件夹中创文件夹 xff08 系统文件夹除外 xff09 并在创建的文件夹中创建文件 users 组用户不可以在磁盘根目前下创建文件 users 组用户不可在c盘windows
  • FFMPEG通过管道将图片推送流媒体

    最近遇到个需求 xff0c 将私有协议的码流 xff0c 就是比较老的视频设备啦 xff0c 新设备都支持标准H264 H265了 xff0c 或者私有平台协议的视频 xff0c 将这些私有协议视频通过转码推送到标准的流媒体服务器 xff0
  • 【VxWorks 6.x之FTP服务器】

    提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 文章目录 前言一 FTP是什么 xff1f 二 使用步骤使用FTP需要添加的组件 最后就可以通过FTP软件去访问VxWorks FTP服务
  • spring详解(IDEA版)

    在这个世界上取得成就的人 xff0c 都努力去寻找他们想要的机会 xff0c 如果找不到机会 xff0c 他们便自己创造机会 你好 xff0c 我是梦阳辰 期待与你相遇 xff01 文章目录 01 spring简介02 IOC理论03 快速
  • PPTV面试题——括号消除

    题目 xff1a 给定一个字符串 xff0c 设计一个算法消除其中承兑的括号 xff0c 如果括号不成对 xff0c 提示异常 xff08 error xff09 如 xff08 1 xff0c xff08 2 3 xff09 xff0c
  • java实现下载网络图片到本地

    文章目录 前言一 示例二 代码 1 代码示例2 运行结果总结 前言 当我们在网络上看到自己想要保存的照片 xff0c 有的网站设置了权限 xff0c 不能保存情况下 xff0c 我们可以借助Java的文件流读取网络上的图片 xff0c 并保
  • Unable to get package info for [包路径]; is package not installed

    这个问题引起原因是 简单的卸载app 没有卸载干净 xff0c 然后再次运行 当dalvik重新安装 apk文件并试图重用以前的活动从同一个包
  • 算法导论->算法基础->2.1插入排序 (从小到大)

    1 伪代码 2 执行过程图 3 c语言实现完整代码 include lt stdio h gt include lt malloc h gt typedef struct MyArray int pbase int length MyArr
  • 【实习面试】阿里&腾讯offer的点点滴滴(内附干货)

    前言 4月8号下午6点 xff0c 突然接到腾讯hr的电话 xff0c 本来已经不抱希望的我一脸懵逼 xff0c 差点连自我介绍都不会说了 之所以不抱希望 xff0c 是因为距离上次面试已经9天了 xff0c 然而正式的实习生面试将近 xf
  • PE 自己做

    windows11快正式发布了 xff0c 很多不达标的老旧电脑是不能直接装的 事先准备好一个纯净PE吧 以下文字全部粘贴到 cmd 文件 xff0c 运行就可以生成一个 boot wim 文件 xff0c 放到任意一个可以启动的 win1
  • 找回忘记的Ubuntu账号密码

    前端时间使用VMWare安装了个Ubuntn的虚拟机 xff0c 但是烦于安装后显卡驱动的问题 xff0c 看着操作界面就有点厌烦 xff0c 就扔下了 今天打开虚拟机登陆的时候忘了密码 xff0c 寻思着难道要重装不行 xff1f 现在记
  • KVM虚拟化解决方案系列之KVM部署篇(1-4)

    通过 KVM虚拟化解决方案系列之KVM架构篇 我们了解了KVM的基本架构之后 xff0c 那么接下来继续介绍如何使用KVM来搭建自己的虚拟化环境 xff0c 搭建环境如表1所示 表1 KVM搭建环境 主机名角色操作系统IP地址备注kvm01
  • ArduinoLoRa 休眠极限 1.4uA

    提示1 xff1a 锐米所有 LoRa 产品严格遵循国际标准的 LoRaWAN 协议 提示2 xff1a 您可以免费复制 xff0c 修改和商用本项目 xff0c 请注明锐米原创 提示3 xff1a 如果您有其他 LoRa 需求或建议 xf
  • SElinux 读懂.te 定义自己的 .te【转】

    本文转载自 https blog csdn net kongbaidepao article details 61417291 一 te 文件定义中的一些宏 1 1 unix socket connect 1 1 2 3 这个其实是一个宏
  • ArduinoLoRa 休眠定时器唤醒 5.5uA

    提示1 xff1a 锐米所有 LoRa 产品严格遵循国际标准的 LoRaWAN 协议 提示2 xff1a 您可以免费复制 xff0c 修改和商用本项目 xff0c 请注明锐米原创 提示3 xff1a 如果您有其他 LoRa 需求或建议 xf
  • 花 1 小时,开源设计 LoRa 按钮

    提示1 xff1a 锐米所有 LoRa 产品严格遵循国际标准的 LoRaWAN 协议 提示2 xff1a 您可以免费复制 xff0c 修改和商用本项目 xff0c 请注明锐米原创 提示3 xff1a 如果您有其他 LoRa 需求或建议 xf

随机推荐

  • 花 1 小时,开源设计 LoRa 红外感应终端

    提示1 xff1a 锐米所有 LoRa 产品严格遵循国际标准的 LoRaWAN 协议 提示2 xff1a 您可以免费复制 xff0c 修改和商用本项目 xff0c 请注明锐米原创 提示3 xff1a 如果您有其他 LoRa 需求或建议 xf
  • ArduinoLoRa 休眠中断唤醒 1.4uA

    提示1 xff1a 锐米所有 LoRa 产品严格遵循国际标准的 LoRaWAN 协议 提示2 xff1a 您可以免费复制 xff0c 修改和商用本项目 xff0c 请注明锐米原创 提示3 xff1a 如果您有其他 LoRa 需求或建议 xf
  • 花 1 小时,开源设计 LoRa 检测电池容量

    提示1 xff1a 锐米所有 LoRa 产品严格遵循国际标准的 LoRaWAN 协议 提示2 xff1a 您可以免费复制 xff0c 修改和商用本项目 xff0c 请注明锐米原创 提示3 xff1a 如果您有其他 LoRa 需求或建议 xf
  • 10 分钟内安装“干净”的 win10 和常用软件

    背景需求 因为毕业于计算机专业 xff0c 这 10 多年来经常帮助家人和朋友安装 Windows 系统和软件 xff0c 消耗了大量的业余时间 2020年春节 xff0c 新冠爆发 xff0c 不宜外出 xff0c 特地抽了几天时间组织下
  • Linux 快速排查网络故障

    背景需求 Linux 因为其强大的网络处理能力 xff0c 被广泛用于网关 实例链接 和服务器 实例链接 实际工作中 xff0c 快速排查这些 Linux 设备的网络故障成为解决问题的利器 为此 xff0c 本文列出高频使用的 Linux
  • Linux 系统安全关键命令

    背景需求 Linux 因为其强大的处理能力和开源免费 xff0c 被广泛用于网关 实例链接 和服务器 实例链接 实际工作中 xff0c 保护 Linux 设备的安全成为产品设计的挑战 为此 xff0c 本文列出保护 Linux 系统安全的关
  • LoRa Server 运维常用命令

    背景需求 LoRa Server 是一个开源的 LoRaWAN 网络服务器 xff0c 它具备很多优点 xff1a 工程性 xff0c 模块化 xff0c 功能实现 xff0c 维护活跃度上都是其他项目无法比拟的 xff0c 它是 LoRa
  • 抗击新型冠状病毒肺炎,开源设计 LoRa 红外检测体温

    提示1 xff1a 锐米所有 LoRa 产品严格遵循国际标准的 LoRaWAN 协议 提示2 xff1a 您可以免费复制 xff0c 修改和商用本项目 xff0c 请注明锐米原创 提示3 xff1a 如果您有其他 LoRa 需求或建议 xf
  • AS--›Groovy/Gradle操作使用实例记录(持续更新)

    Groovy gradle 构建脚本使用的 groovy 语言编写 官方地址 http groovy lang org documentation html API文档地址 http groovy lang org api html API
  • 花 1 小时,开源设计 LoRa 烟感烟雾报警器

    提示1 xff1a 锐米所有 LoRa 产品严格遵循国际标准的 LoRaWAN 协议 提示2 xff1a 您可以免费复制 xff0c 修改和商用本项目 xff0c 请注明锐米原创 提示3 xff1a 如果您有其他 LoRa 需求或建议 xf
  • 花 1 小时,开源设计 LoRa 继电器开关

    提示1 xff1a 锐米所有 LoRa 产品严格遵循国际标准的 LoRaWAN 协议 提示2 xff1a 您可以免费复制 xff0c 修改和商用本项目 xff0c 请注明锐米原创 提示3 xff1a 如果您有其他 LoRa 需求或建议 xf
  • 为 LoRaWAN 节点和服务器配置 Class C,实现主动下行通信

    提示1 xff1a 锐米所有 LoRa 产品严格遵循国际标准的 LoRaWAN 协议 提示2 xff1a 如果您有其他 LoRa 需求或建议 xff0c 欢迎联系锐米 support 64 rimelink com 背景需求 在 LoRa
  • 花 1 小时,开源设计 LoRa GPS 定位器

    提示1 xff1a 锐米所有 LoRa 产品严格遵循国际标准的 LoRaWAN 协议 提示2 xff1a 您可以免费复制 xff0c 修改和商用本项目 xff0c 请注明锐米原创 提示3 xff1a 如果您有其他 LoRa 需求或建议 xf
  • 开源免费的手机版 LoRa App,演示和调试 LoRaWAN 数据的神器

    继 可配置数据解析格式的 LoRaAppDemo 64 C 应用工具 的PC工具之后 xff0c 为了帮助 LoRa 用户 演示数据和调试开发 xff0c 开源 xff0c 免费 xff0c 可安装在Android 手机的移动端App 一
  • 开源免费的 LoRa App 设计原理和组件

    为了帮助 LoRa 用户演示数据和调试开发 xff0c 开源免费的 LoRa App 推出后深受好评 下载与使用请链接 开源免费的手机版 LoRa App xff0c 演示和调试 LoRaWAN 数据的神器 https blog csdn
  • 如何测试 LoRaWAN 全球频段

    To be a sailor of the world bound for all ports 做世界的水手 xff0c 游遍所有的港口 背景 自 2015 年 LoRa 联盟创建 LoRaWAN 协议 xff0c 经过 7 年长跑 xff
  • LoRa Server@Ubuntu#2:一键安装

    LoRa Server 64 Ubuntu 2 xff1a 一键安装 LoRa Server 是一个开源的 LoRaWAN 网络服务器 xff0c 它具备很多优点 xff1a 工程性 xff0c 模块化 xff0c 功能实现 xff0c 维
  • 【转帖】VxWork介绍及编程

    VxWork介绍及编程 一 嵌入式操作系统VxWorks简介 VxWorks操作系统是美国WindRiver公司于1983年设计开发的一种嵌入式实时操作系统 xff08 RTOS xff09 xff0c 是嵌入式开发环境的关键组成部分 良好
  • 文件恢复

    对于广大电脑爱好者来说 xff0c 最担心的事莫过于数据损坏 xff08 丢失 xff09 了 xff0c 如果只是系统崩溃 xff0c 那么我们还可以重新安装 xff0c 所花费的只是时间而已 即使是硬件损坏 xff0c 也只是需要更换新
  • Kotlin--›Android RecyclerView滚动处理(滚动到底部/顶部/居中/偏移/动画等特性)

    之前写过一篇Android gt RecyclerView 显示底部 滚动底部 无动画 文章 当时是为了满足需求 没想太多顺手写的 虽然功能上能满足 但是代码上还是有点low 这几天 我的徒弟傻豆 在写一个IM项目 需要滚动到底部 于是我重