当前位置: 首页 > news >正文

【Android】View 的滑动

View 的滑动是 Android 实现自定义控件的基础,同时在开发中我们也难免会遇到 View 的滑动处理。其实不管是哪种滑动方式,其基本思想都是类似的:当点击事件传到 View 时,系统记下触摸点的坐标,手指移动时系统记下移动后触摸的坐标并算出偏移量,并通过偏移量来修改View的坐标。

实现 View 滑动有很多种方法,在这里主要讲解6种滑动方法,分别是 layout()、offsetLeftAndRight() 与 offsetTopAndBottom()、LayoutParams、Animation、scollTo() 与 scollBy(),以及 Scroller。

一、layout() 方法

View 进行绘制的时候会调用 onLayout() 方法来设置显示的位置,因此我们同样也可以通过修改 View 的 left、top、right、bottom 这4种属性来控制 View 的坐标。首先我们要自定义一个 View,在 onTouchEvent() 方法中获取触摸点的坐标,代码如下所示:

override fun onTouchEvent(event: MotionEvent?): Boolean {// 获取手指触摸点的横坐标和纵坐标val x = event?.x?.toInt()val y = event?.y?.toInt()when (event?.action) {MotionEvent.ACTION_DOWN -> {lastX = x ?: 0lastY = y ?: 0}...}...
}

接下来我们在 ACTION_MOVE 事件中计算偏移量,再调用 layout() 方法重新放置这个自定义 View 的位置即可。

override fun onTouchEvent(event: MotionEvent?): Boolean {...when (event?.action) {...MotionEvent.ACTION_MOVE -> {// 计算移动的距离val offsetX = x ?: (0 - lastX)val offsetY = y ?: (0 - lastY)// 调用 layout 方法来重新放置它的位置layout(left + offsetX, top + offsetY, right + offsetX, bottom + offsetY)}}...
}

在每次移动时都会调用 layout() 方法对屏幕重新布局,从而达到移动 View 的效果。自定义 View 的全部代码如下所示:

class CustomView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {private var lastX = 0private var lastY = 0override fun onTouchEvent(event: MotionEvent?): Boolean {// 获取手指触摸点的横坐标和纵坐标val x = event?.x?.toInt()val y = event?.y?.toInt()when (event?.action) {MotionEvent.ACTION_DOWN -> {lastX = x ?: 0lastY = y ?: 0}MotionEvent.ACTION_MOVE -> {// 计算移动的距离val offsetX = x ?: (0 - lastX)val offsetY = y ?: (0 - lastY)// 调用 layout 方法来重新放置它的位置layout(left + offsetX, top + offsetY, right + offsetX, bottom + offsetY)}}return true}
}

随后,我们在布局中引用自定义 View 就可以了:

<com.tyhoo.android.demo.CustomViewandroid:id="@+id/test_view"android:layout_width="100dp"android:layout_height="100dp"android:background="@android:color/holo_red_light"... />

运行程序,效果如图1所示:
请添加图片描述

图1

图1中的方块就是我们自定义的 View,它会随着我们手指的滑动改变自己的位置。

二、offsetLeftAndRight() 与 offsetTopAndBottom()

这两种方法和 layout() 方法的效果差不多,其使用方式也差不多。我们将 ACTION_MOVE 中的代码替换成如下代码:

override fun onTouchEvent(event: MotionEvent?): Boolean {...when (event?.action) {...MotionEvent.ACTION_MOVE -> {// 计算移动的距离val offsetX = x ?: (0 - lastX)val offsetY = y ?: (0 - lastY)// 对 left 和 right 进行偏移offsetLeftAndRight(offsetX)// 对 top 和 bottom 进行偏移offsetTopAndBottom(offsetY)}}...
}

三、LayoutParams

LayoutParams 主要保存了一个 View 的布局参数,因此我们可以通过 LayoutParams 来改变 View 的布局参数从而达到改变 View 位置的效果。同样,我们将 ACTION_MOVE 中的代码替换成如下代码:

override fun onTouchEvent(event: MotionEvent?): Boolean {...when (event?.action) {...MotionEvent.ACTION_MOVE -> {// 计算移动的距离val offsetX = x ?: (0 - lastX)val offsetY = y ?: (0 - lastY)val layoutParams = layoutParams as ConstraintLayout.LayoutParamslayoutParams.leftMargin = left + offsetXlayoutParams.topMargin = top + offsetYsetLayoutParams(layoutParams)}}...
}

因为父控件是 ConstraintLayout,所以我们用了 ConstraintLayout.LayoutParams。如果父控件是 RelativeLayout,则要使用RelativeLayout.LayoutParams。除了使用布局的 LayoutParams 外,我们还可以用 ViewGroup.MarginLayoutParams 来实现:

override fun onTouchEvent(event: MotionEvent?): Boolean {...when (event?.action) {...MotionEvent.ACTION_MOVE -> {// 计算移动的距离val offsetX = x ?: (0 - lastX)val offsetY = y ?: (0 - lastY)val layoutParams = layoutParams as ViewGroup.MarginLayoutParamslayoutParams.leftMargin = left + offsetXlayoutParams.topMargin = top + offsetYsetLayoutParams(layoutParams)}}...
}

四、Animation

可以采用 View 动画来移动,在 res 目录新建 anim 文件夹并创建 translate.xml:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"><translateandroid:duration="1000"android:fromXDelta="0"android:toXDelta="300" />
</set>

接下来在 Kotlin 代码中调用就好了,代码如下所示:

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val testView = findViewById<CustomView>(R.id.test_view)testView.animation = AnimationUtils.loadAnimation(this, R.anim.translate)}
}

运行程序,效果如图2所示:
请添加图片描述

图2

运行程序,我们设置的方块会向右平移300像素,然后又会回到原来的位置。为了解决这个问题,我们需要在 translate.xml 中加上 fillAfter=“true”,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"android:fillAfter="true"><translateandroid:duration="1000"android:fromXDelta="0"android:toXDelta="300" />
</set>

运行程序,效果如图3所示:
请添加图片描述

图3

运行代码后会发现,方块向右平移300像素后就停留在当前位置了。

需要注意的是,View 动画并不能改变 View 的位置参数。如果对一个 View 进行如上的平移动画操作,当 View 平移300像素停留在当前位置时,我们点击这个 View 并不会触发点击事件,但在我们点击这个 View 的原始位置时却触发了点击事件。对于系统来说这个 View 并没有改变原有的位置,所以我们点击其他位置当然不会触发这个 View 的点击事件。

五、scrollTo() 与 scollBy()

scrollTo(x, y) 表示移动到一个具体的坐标点,而 scrollBy(dx, dy) 则表示移动的增量为 dx、dy。其中,scollBy 最终也是要调用 scollTo 的。View 的 scollTo 和 scollBy 的源码如下所示:

public void scrollTo(int x, int y) {if (mScrollX != x || mScrollY != y) {int oldX = mScrollX;int oldY = mScrollY;mScrollX = x;mScrollY = y;invalidateParentCaches();onScrollChanged(mScrollX, mScrollY, oldX, oldY);if (!awakenScrollBars()) {postInvalidateOnAnimation();}}
}public void scrollBy(int x, int y) {scrollTo(mScrollX + x, mScrollY + y);
}

scollTo、scollBy 移动的是 View 的内容,如果在 ViewGroup 中使用,则是移动其所有的子 View。我们将 ACTION_MOVE 中的代码替换成如下代码:

override fun onTouchEvent(event: MotionEvent?): Boolean {...when (event?.action) {...MotionEvent.ACTION_MOVE -> {// 计算移动的距离val offsetX = x ?: (0 - lastX)val offsetY = y ?: (0 - lastY)(parent as View).scrollBy(-offsetX, -offsetY)}}return true
}

这里若要实现自定义 View 随手指移动的效果,就需要将偏移量设置为负值。为什么要设置为负值呢?这是参考对象不同导致的差异。所以我们用 scrollBy 方法的时候要设置负数才会达到自己想要的效果。

六、Scroller

我们在用 scollTo/scollBy 方法进行滑动时,这个过程是瞬间完成的,所以用户体验不大好。这里我们可以使用 Scroller 来实现有过渡效果的滑动,这个过程不是瞬间完成的,而是在一定的时间间隔内完成的。Scroller 本身是不能实现 View 的滑动的,它需要与 View 的 computeScroll() 方法配合才能实现弹性滑动的效果。在这里我们实现自定义 View 平滑地向右移动。首先我们要初始化 Scroller,代码如下所示:

class CustomView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {...private var scroller: Scroller? = nullinit {scroller = Scroller(context)}...
}

接下来重写 computeScroll() 方法,系统会在绘制 View 的时候在 draw() 方法中调用该方法。在这个方法中,我们调用父类的 scrollTo() 方法并通过 Scroller 来不断获取当前的滚动值,每滑动一小段距离我们就调用invalidate() 方法不断地进行重绘,重绘就会调用 computeScroll() 方法,这样我们通过不断地移动一个小的距离并连贯起来就实现了平滑移动的效果。

override fun computeScroll() {super.computeScroll()scroller?.let {if (it.computeScrollOffset()) {(parent as View).scrollTo(it.currX, it.currY)invalidate()}}
}

我们在自定义 View 中写一个 smoothScrollTo 方法,调用 Scroller 的 startScroll() 方法,在 2000ms 内沿 X 轴平移 delta 像素,代码如下所示:

fun smoothScrollTo(destX: Int, destY: Int) {val scrollX = scrollXval delta = destX - scrollXscroller?.startScroll(scrollX, 0, delta, 0, 2000)invalidate()
}

最后我们再调用自定义 View 的 smoothScrollTo() 方法。这里我们设定自定义 View 沿着 X 轴向右平移 400 像素。

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val testView = findViewById<CustomView>(R.id.test_view)testView.smoothScrollTo(-400, 0)}
}

运行程序,效果如图4所示:
请添加图片描述

图4

相关文章:

【Android】View 的滑动

View 的滑动是 Android 实现自定义控件的基础&#xff0c;同时在开发中我们也难免会遇到 View 的滑动处理。其实不管是哪种滑动方式&#xff0c;其基本思想都是类似的&#xff1a;当点击事件传到 View 时&#xff0c;系统记下触摸点的坐标&#xff0c;手指移动时系统记下移动后…...

基于ZYNQ的PCIE高速数据采集卡的设计(一)

作为信息处理的第一步&#xff0c;数据采集的作用越来越重要。目前&#xff0c;数据采集已经在航 空、民用、军事、医疗等领域得到广泛应用。随着相关技术的不断发展&#xff0c;信号频率越 来高&#xff0c;带宽越来越大&#xff0c;使得数据采集技术逐渐向高速大数据的方向…...

渗透测试工具 nmap 详解

官网&#xff1a;Nmap: the Network Mapper - Free Security Scanner -p&#xff1c;端口范围&#xff1e;&#xff1a;仅扫描指定的端口 用于扫描指定端口是否开放&#xff0c;在 -p 后输入指定的端口&#xff0c;以英文","进行拼接多个指定端口。 nmap -p 80&…...

Ubuntu下安装Scala

前言 弄了一下终于成功装上了&#xff0c;这里对此进行一下总结 安装虚拟机 VMware虚拟机安装Ubuntu&#xff08;超详细图文教程&#xff09;_vmware安装ubuntu-CSDN博客https://blog.csdn.net/qq_43374681/article/details/129248167Download Ubuntu Desktop | Download | …...

无法启动报,To install it, you can run: npm install --save @/components/iFrame/index

运行的过程中后台报错 npm install --save /components/iFrame/index&#xff0c;以为是安装三方依赖错误&#xff0c;经过多次重装node_modules依然没有用。 没办法&#xff0c;只能在项目中搜索 components/iFrame/index这个文件。。突然醒悟。。。 有时候&#xff0c;犯迷…...

深入理解现代JavaScript:从语言特性到应用实践

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 JavaScript作为一门动态、解释性脚本语言&…...

ThreadPoolExecutor 学习

ThreadPoolExecutor 是开发中最常用的线程池&#xff0c;今天来简单学习一下它的用法以及内部构造。 1、线程池存在的意义&#xff1f; 一般在jvm上&#xff0c;用户线程和操作系统内核线程是1&#xff1a;1的关系&#xff0c;也就是说&#xff0c;每次创建、销毁线程的时候&am…...

深入理解计算机操作系统书籍阅读感悟(一)

1.sp&#xff1a;表示为空格&#xff0c;ASCII为32 2.在我们写的每行程序结尾都有一个隐藏的\n&#xff08;ASCII码值为10&#xff09; 3.在书上的P2页上说&#xff1a;文本文件是指以ASCII码字符构成的文件&#xff0c;其余都是二进制文件 除了这种理解&#xff0c;更常见的…...

使用query请求数据出现500的报错

我在写项目的时候遇到了一个问题&#xff0c;就是在存商品id的时候我将它使用了JSON.stringify的格式转换了&#xff01;&#xff01;&#xff01;于是便爆出了500这个错误&#xff01;&#xff01;&#xff01; 我将JSON.stringify的格式去除之后&#xff0c;它就正常显示了&…...

PostgreSQL教程(二十一):服务器管理(三)之服务器设置和操作

本章讨论如何设置和运行数据库服务器&#xff0c;以及它与操作系统的交互。 一、PostgreSQL用户账户 和对外部世界可访问的任何服务器守护进程一样&#xff0c;我们也建议在一个独立的用户账户下运行PostgreSQL。这个用户账户应该只拥有被该服务器管理的数据&#xff0c;并且…...

Linux运维_Bash脚本_编译安装GNU-Tools

Linux运维_Bash脚本_编译安装GNU-Tools Bash (Bourne Again Shell) 是一个解释器&#xff0c;负责处理 Unix 系统命令行上的命令。它是由 Brian Fox 编写的免费软件&#xff0c;并于 1989 年发布的免费软件&#xff0c;作为 Sh (Bourne Shell) 的替代品。 您可以在 Linux 和 …...

leetcode 121.买卖股票的最佳时机

声明&#xff1a;以下仅代表个人想法&#xff0c;非官方答案或最优题解&#xff01; 题目&#xff1a; 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的…...

javaWebssh酒店客房管理系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 java ssh酒店客房管理系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0…...

vue3基础教程(2)——创建vue3+vite项目

博主个人微信小程序已经上线&#xff1a;【中二少年工具箱】。欢迎搜索试用 正文开始 专栏简介1. 前言2.node版本检测3.创建vue项目 专栏简介 本系列文章由浅入深&#xff0c;从基础知识到实战开发&#xff0c;非常适合入门同学。 零基础读者也能成功由本系列文章入门&#x…...

部署DNS 实战篇

二、DNS 部署 环境介绍 服务器3台、系统centos 安装软件 yum install -y bind bind-utils bind-chrootbind 主包bind-utils 客户端测试工具&#xff08;host 、dig 、nslookup&#xff09;bind-chroot chroot环境 禁锢dns服务器的工作目录caching-nameserver(rhel5提供…...

2023 2024年全国职业院校技能大赛中职组网络建设与运维赛项服务器Linux部分教程解析

欢迎合作 需要资料请私 Rocky 9 包含各种常考服务(包括新题型KVM等)...

Flask g对象和插件

四、Flask进阶 1. Flask插件 I. flask-caching 安装 pip install flask-caching初始化 from flask_cache import Cache cache Cache(config(CACHE_TYPE:"simple" )) cache.init_app(appapp)使用 在视图函数上添加缓存 blue.route("/") cache.cached(tim…...

26、Qt调用.py文件中的函数

一、开发环境 Qt5.12.0 Python3.7.8 64bit 二、使用 新建一个Qt项目&#xff0c;右击项目名称&#xff0c;选择“添加库” 选择“外部库”&#xff0c;点击“下一步” 点击“浏览”&#xff0c;选择Python安装目录下的libs文件夹中的“python37.lib”文件&#xff0c;点击“下…...

计算机网络实验一 网线制作

实验目的与要求&#xff1a; 实验目的 了解以太网网线&#xff08;双绞线&#xff09;和制作方法 实验内容 了解网线和水晶头 学习网线制作方法 实验环境和要求 网线 水晶头 压线钳 剥线钳 网线测试器 方法、步骤&#xff1a; 步骤一 准备工具和材料 步骤二 剥掉双绞线的外…...

android TextView 实现富文本显示

android TextView 实现富文本显示&#xff0c;实现抖音直播间公屏消息案例 使用&#xff1a; val tvContent: TextView helper.getView(R.id.tvContent)//自己根据UI业务要求&#xff0c;可以控制 图标显示 大小val levelLabel MyImgLabel( bitmap 自己业务上的bitmap )va…...

Pure Live完整指南:3分钟掌握跨平台纯净直播聚合工具

Pure Live完整指南&#xff1a;3分钟掌握跨平台纯净直播聚合工具 【免费下载链接】pure_live A Flutter project can make you watch live with ease. 项目地址: https://gitcode.com/gh_mirrors/pu/pure_live 在当今数字娱乐时代&#xff0c;直播已成为人们日常娱乐的重…...

X86与ARM架构深度解析:从指令集到生态的全面对比

1. 项目概述&#xff1a;为什么我们需要重新审视X86与ARM最近几年&#xff0c;无论是选购新电脑、关注手机芯片&#xff0c;还是围观科技新闻&#xff0c;你肯定没少听到“X86”和“ARM”这两个词。苹果的Mac电脑全面转向自研的M系列芯片&#xff0c;让“ARM架构”从手机、平板…...

ElevenLabs缅甸文TTS落地难题全拆解:从音素对齐失败到语调失真,3步精准修复

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;ElevenLabs缅甸文TTS落地难题的根源认知 ElevenLabs官方API当前未原生支持缅甸文&#xff08;Burmese, my-MM&#xff09;&#xff0c;其语音合成模型训练语料库中缺乏足够规模、高质量、带韵律标注的缅…...

高效解包Godot游戏资源:PCK文件解析与自动化提取实战指南

高效解包Godot游戏资源&#xff1a;PCK文件解析与自动化提取实战指南 【免费下载链接】godot-unpacker godot .pck unpacker 项目地址: https://gitcode.com/gh_mirrors/go/godot-unpacker Godot游戏资源解包是游戏开发者和逆向工程师经常面临的技术挑战。本文将深入介绍…...

KaTrain终极指南:用AI围棋教练快速提升你的棋艺水平

KaTrain终极指南&#xff1a;用AI围棋教练快速提升你的棋艺水平 【免费下载链接】katrain Improve your Baduk skills by training with KataGo! 项目地址: https://gitcode.com/gh_mirrors/ka/katrain 你是否曾经在对局后感到困惑&#xff0c;不知道自己的失误究竟在哪…...

Word怎么转图片?免费在线转换工具对比|2026实用方案

Word文档转换为图片是职场和学习中常见的需求。无论是为了方便分享、制作演示素材&#xff0c;还是保护文档隐私&#xff0c;掌握多种转换方法都能大幅提升工作效率。本文将为你盘点2026年最实用的Word转图片在线工具&#xff0c;以及电脑和手机端的完整解决方案。为什么要把Wo…...

Omnizart代码实现原理:解密音乐AI背后的深度学习算法

Omnizart代码实现原理&#xff1a;解密音乐AI背后的深度学习算法 【免费下载链接】omnizart Omniscient Mozart, being able to transcribe everything in the music, including vocal, drum, chord, beat, instruments, and more. 项目地址: https://gitcode.com/gh_mirrors…...

OpenClaw Windows一键部署包简体中文版下载

OpenClaw&#xff08;小龙虾&#xff09;Windows 一键部署保姆级教程 | 10分钟养出你的数字员工&#xff08;2026最新版&#xff09; 前言&#xff1a;2026年爆火的开源AI智能体OpenClaw&#xff08;昵称小龙虾&#xff09;&#xff0c;GitHub星标超28万&#xff0c;凭“本地运…...

Agent生产费用智能管控与超支预警功能配置:2026企业级ROI重塑指南

在2026年5月的当下&#xff0c;全球人工智能产业已从“大模型参数竞赛”全面转向“智能体&#xff08;Agent&#xff09;价值落地阶段”。根据2026年5月21日最新的行业数据显示&#xff0c;企业对Agent的投入已占到其IT预算的35%以上。然而&#xff0c;随着Agent系统从实验性De…...

经营分析——解读集团经营分析报告框架【附全文阅读】

集团经营分析报告框架推介总结 适应人群&#xff1a;集团高管、经营管理部、财务负责人、各业务单元负责人、经营分析专员、数据分析师及战略规划人员。 重要性总结&#xff1a;本 PPT 是集团级经营分析的标准化、体系化顶层框架&#xff0c;构建 “战略 — 环境 — 业绩 — 问…...