【Android】View—基础知识,滑动,弹性滑动

news2024/11/13 11:59:08

基础知识

什么是View

在 Android 中,View 是用户界面(UI)中的基本组件,用于绘制图形和处理用户交互。所有的 UI 组件(如按钮、文本框、图片等)都是 View 的子类。可以说,View 是构建 Android 应用界面的基础。

View 是一种界面层的控件的一种抽象,它代表了一个控件。除了 View 还有 ViewGroup ,里面包含了许多个控件,即一组 View 。在Android设计中,ViewGroup 也继承了 View ,也就意味着 View 本身可以是单个控件也可以是由多个控件组成的一组控件。

View的位置参数

View 的位置主要由它的四个顶点决定:top、left、right、bottom,top是左上角纵坐标,left是左上角横坐标,right是右下角横坐标,bottom是右下角纵坐标。这些坐标都是相对于 View 的父容器来说的,是一种相对坐标。

width = right - left

height = bottom - top

Left = getLeft()

Right = getRight()

Top = getTop()

Bottom = getBottom()

从Android3.0开始,View增加了额外的几个参数:x、y、translationX、translationY,其中x和y是View左上角的坐标,translationX和translationY是View左上角相对于父容器的偏移量。

y = top + translationY

MotionEvent 和 TouchSlop

  1. MotionEvent

    MotionEvent 是 Android 中用于描述触摸屏幕的事件类。当用户在屏幕上进行触摸操作(如点击、滑动、拖动等)时,系统会生成一个 MotionEvent 对象并传递给相应的 View 的触摸事件处理方法(例如 onTouchEvent())。

MotionEvent 的主要方法和常量:

  • 常用事件类型(通过 getAction() 获取):
    • ACTION_DOWN:表示手指刚刚触碰屏幕,此时可以记录触摸的起始坐标。
    • ACTION_MOVE:表示手指在屏幕上移动,通常用于检测滑动、拖动等操作。
    • ACTION_UP:表示手指离开屏幕,通常在这里结束触摸操作或触发点击事件。
    • ACTION_CANCEL:表示触摸事件被中断,比如手指从屏幕上滑动到不可触摸区域。
  • 坐标获取
    • getX()getY():获取事件发生点相对于当前 View 的 x 和 y 轴坐标。
    • getRawX()getRawY():获取事件发生点相对于屏幕的绝对 x 和 y 轴坐标。
  • 多点触控MotionEvent 支持多点触控,可以通过 getPointerCount() 来获取触控点数量,或通过 getPointerId(int index) 来获取特定触控点的 ID。

MotionEvent 使用场景

MotionEvent 通常用于实现复杂的手势或触控效果,比如检测滑动方向、双指缩放、拖动等。通过组合 ACTION_DOWNACTION_MOVEACTION_UP 的坐标变化,可以实现自定义的滑动或手势检测逻辑。

  1. TouchSlop

    TouchSlop 是一个阈值,用于判断用户的触摸是否足够显著,足以被认为是“滑动”而不是“轻微抖动”或“点击”。在触摸屏幕时,有时用户会产生轻微的抖动,而 TouchSlop 的作用就是过滤掉这种无意的微小移动。

    • TouchSlop 的值在设备中是固定的(基于屏幕密度),可以通过 ViewConfiguration.get(context).getScaledTouchSlop() 来获取。
    • TouchSlop 的单位是像素,通常的使用方式是当手指移动距离超过 TouchSlop 时,才认为这是一个有效的滑动操作。

TouchSlop 使用场景

TouchSlop 常用于判断滑动是否开始,例如在处理自定义滑动手势时,可以使用以下伪代码来判断滑动:

// 假设 downX 和 downY 是手指按下时的初始坐标
float deltaX = currentX - downX;
float deltaY = currentY - downY;

if (Math.sqrt(deltaX * deltaX + deltaY * deltaY) > touchSlop) {
    // 开始滑动
}

使用 TouchSlop 可以避免在轻微抖动时触发滑动,从而提高手势的识别精度。

VelocityTracker、GestureDetector和Scroller

在 Android 中,VelocityTrackerGestureDetectorScroller 是处理触摸事件和手势操作的三个常用工具,适用于实现复杂的滑动、手势识别、惯性滚动等效果。以下是对它们的介绍和应用场景:

  1. VelocityTracker

    VelocityTracker 用于追踪触摸事件的移动速度,特别是在实现滑动和快速滑动手势(如快速滑动删除、甩动等)时非常有用。

    • 主要方法

      • addMovement(MotionEvent event):将当前的触摸事件加入到 VelocityTracker 中,用于计算滑动速度。
      • computeCurrentVelocity(int units):计算速度,units 参数通常设为 1000,表示每秒的像素速率。
      • getXVelocity()getYVelocity():获取 x 和 y 方向的滑动速度,返回值是每秒的像素速度。
      • clear():清除 VelocityTracker 中的事件。
    • 使用场景:通常在实现滑动或甩动删除功能时会用到,例如根据手指的滑动速度来判断是否应触发滑动效果。

速度 = (终点位置 - 起点位置) / 时间段

示例代码

VelocityTracker velocityTracker = VelocityTracker.obtain();
velocityTracker.addMovement(event);
velocityTracker.computeCurrentVelocity(1000);
float xVelocity = velocityTracker.getXVelocity();
float yVelocity = velocityTracker.getYVelocity();
  1. GestureDetector

    GestureDetector 用于检测标准的手势操作,例如单击、双击、长按、滑动等。它简化了常见手势的识别,不需要手动计算每个 MotionEvent 的位置和时间。

    • 常用手势方法

      • onSingleTapUp(MotionEvent e):单击。
      • onLongPress(MotionEvent e):长按。
      • onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY):滑动。
      • onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY):快速滑动。
      • onDoubleTap(MotionEvent e):双击。
    • 使用场景GestureDetector 可以简化各种常见手势的实现,适用于需要检测点击、长按、双击等的场景。例如在图片查看应用中实现缩放或拖拽手势。

    示例代码

    GestureDetector gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            // 单击事件
            return true;
        }
    
        @Override
        public void onLongPress(MotionEvent e) {
            // 长按事件
        }
    
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            // 滑动事件
            return true;
        }
    
        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            // 快速滑动事件
            return true;
        }
    });
    
    // 在触摸事件中使用 GestureDetector
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return gestureDetector.onTouchEvent(event);
    }
    
  2. Scroller

    Scroller 是 Android 中用于实现滚动和惯性滑动的工具类。它本身不会滚动 View,而是提供计算滚动位置的接口,通常与 ViewcomputeScroll() 方法结合使用。

    • 主要方法

      • startScroll(int startX, int startY, int dx, int dy, int duration):设置起点、滚动距离和持续时间。
      • fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY):使用速度(通常配合 VelocityTracker)和边界来实现甩动效果。
      • computeScrollOffset():判断滚动是否完成,用于在 computeScroll() 方法中进行判断。
      • getCurrX()getCurrY():获取当前滚动位置。
    • 使用场景:通常用于实现平滑滚动效果,如实现自定义滑动 View,滑动菜单等。在 ScrollView 中,Scroller 可用于实现惯性滚动效果。

    示例代码

    Scroller scroller = new Scroller(context);
    
    // 触发滚动
    scroller.startScroll(0, 0, deltaX, deltaY, 1000);
    
    // 在 View 中重写 computeScroll 方法
    @Override
    public void computeScroll() {
        if (scroller.computeScrollOffset()) {
            scrollTo(scroller.getCurrX(), scroller.getCurrY());
            invalidate(); // 刷新界面,继续执行滚动
        }
    }
    

View的滑动

使用scrollTo/scrollBy

下面是scrollTo和scrollBy的源码:

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

可以看到scrollBy实际上也调用了scrollTo方法,实现了基于当前位置的相对滑动,而scrollTo实现了基于所传递参数的绝对滑动。

View 边缘是指 View 的位置,由四个顶点组成,而 View 内容边缘是指 View 中的内容的边缘,scrollToTo和scrollBy只能改变 View 内容的位置而不能改变 View 在布局中的位置。mScrollX和mScrollY的单位为像素,并且当 View 左边缘在 View 内容左边缘的右边时,mScrollX为正值,反之为负值;当 View 上边缘在View内容上边缘的下边时,mScrollY为正值,反之为负值。换句话说,如果从左向右滑动,那么mScrollX为负值,反之为正值;如果从上往下滑动,那么mScrollY为负值,反之为正值。

image-20241110102031436

改变布局参数

ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) button.getLayoutParams();
params.width += 100;
params.leftMargin += 100;
button.requestLayout();

可以通过改变LayoutParams的方式去实现View的滑动。

弹性滑动

使用Scroller

实现平滑滚动:

public class SmoothScrollView extends View {
    private Scroller scroller;

    public SmoothScrollView(Context context) {
        super(context);
        scroller = new Scroller(context);
    }

    // 定义平滑滚动到指定位置的方法
    public void smoothScrollTo(int destX, int destY) {
        int deltaX = destX - getScrollX();  // x方向滚动的距离
        int deltaY = destY - getScrollY();  // y方向滚动的距离
        // 设定滚动参数
        scroller.startScroll(getScrollX(), getScrollY(), deltaX, deltaY, 1000);  // 1秒滚动完成
        invalidate();  // 触发重绘
    }

    @Override
    public void computeScroll() {
        if (scroller.computeScrollOffset()) {
            scrollTo(scroller.getCurrX(), scroller.getCurrY());
            postInvalidate();  // 保持刷新
        }
    }
}

通过动画

使用动画来实现滑动天然就具有弹性效果,下面代码可以让一个View100ms内向右移动100像素:

ObjectAnimator.ofFloat(button, "translationX", 0, 100).setDuration(100).start();

如果模仿Scroller来实现View的弹性滑动,利用动画的特性,我们可以采用如下方式:

final int startX = 0;
final int deltaX = 100;
ValueAnimator animator = ValueAnimator.ofInt(0, 1).setDuration(1000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
	@Override
	public void onAnimationUpdate(@NonNull ValueAnimator animation) {
		float fraction = animator.getAnimatedFraction();
		button.scrollTo(startX + (int)(deltaX * fraction), 0);
	}
});
animator.start();
  • startX 表示按钮的初始水平滚动位置,这里设为 0

  • deltaX 表示滚动的水平距离(即目标位置与起始位置的差距),设为 100。在动画过程中,按钮内容的滚动位置会从 startX 移动到 startX + deltaX

  • ValueAnimator.ofInt(0, 1) 创建了一个整数类型的 ValueAnimator 对象,该对象的值从 0 变为 1,并且 setDuration(1000) 设置动画的持续时间为 1 秒(1000 毫秒)。

    这里将动画的数值范围设定为 01,这意味着动画本身并不依赖具体的值变化,而是利用动画的进度来计算滚动的距离。

  • addUpdateListeneranimator 添加了一个更新监听器 AnimatorUpdateListener。在动画进行过程中,每一帧都会回调 onAnimationUpdate() 方法。
  • onAnimationUpdate() 方法中,使用 animator.getAnimatedFraction() 获取当前动画的完成度(即已运行时间占总时间的百分比)。这个完成度 fraction 是一个 01 的小数,0 表示动画刚开始,1 表示动画结束。
  • button.scrollTo(startX + (int)(deltaX * fraction), 0);:调用 scrollTo() 方法,将按钮内容沿水平轴滚动到 startX + (int)(deltaX * fraction) 位置。
    • deltaX * fraction 表示当前滚动的位置,每一帧会根据完成度 fraction 计算当前的滚动距离,使得滚动效果在整个动画持续时间内平滑进行。
    • 例如,当 fraction0.5 时,滚动的位置为 startX + deltaX * 0.5 = 50,即按钮内容滚动到距离初始位置 50 的位置。

已经到底啦!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2237820.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Unity】Game Framework框架学习使用

前言 之前用过一段时间的Game Framework框架,后来有那么一段时间都做定制小软件,框架就没再怎么使用了。 现在要做大型项目了,感觉还是用框架好一些。于是又把Game Framework拾起来了。 这篇文章主要是讲Game Framework这个框架是怎么用的…

【SoC设计指南 基于Arm Cortex-M】学习笔记1——AMBA

AMBA简介 先进微控制器总线架构(Advanced Microcontroller Bus Architecture,AMBA)是用在arm处理器上的片上总线协议规范集。 AMBA总线协议规范集包含AHB、APB、AXI等。 AHB:先进高性能总线(Advanced High-performance Bus) APB&…

pytorch模型转onnx的动态batch转换说明

将PyTorch模型(.pth)转换为ONNX格式时,通常需要指定一个batch size。这是因为ONNX模型需要一个固定的输入形状,而批处理大小是输入形状的一部分。 下面是一个简单的转换示例,假设你已经加载了一个PyTorch模型&#xff…

【王木头】最大似然估计、最大后验估计

目录 一、最大似然估计(MLE) 二、最大后验估计(MAP) 三、MLE 和 MAP 的本质区别 四、当先验是均匀分布时,MLE 和 MAP 等价 五、总结 本文理论参考王木头的视频: 贝叶斯解释“L1和L2正则化”&#xff…

从0到1基于LangChain制作一个AI猫娘

前言: 看到B站上的AIVtuber的项目落地了,就心血来潮想制作一个AI的猫娘供自己使用,顺便出一个简单的教程,跳过理论,直接实践,作者也还在学习摸索中,所以有错误可以直接在评论区指正。&#xff0…

Vue全栈开发旅游网项目(7)-搜索界面开发及其接口联调

1.搜索界面开发 1.1 模糊查询 文件地址:pycharm- class SightListView(ListView):paginate_by 5def get_queryset(self):#is_validTrue:表中is_valid列,有值则被查询出来query Q(is_validTrue)#1.获得热门景点is_hot self.request.GET.…

python识别ocr 图片和pdf文件

#识别图片 pip3 install paddleocr pip3 install paddlepaddle#识别pdf pip3 install PyMuPDF 重点:路径不能有中文,不然pdf文件访问不了 from paddleocr import PaddleOCR from rest_framework.response import Response from rest_framework.views im…

量化分析工具日常操作日记-5-通合科技

使用量化分析微信小程序工具“梦想兔企业智能风险分析助手”日常操作日记-5-军工-通合科技(300491)。 周末国家新政策,要大力支持军工行业,我用工具挖掘了两个低位股,供大家参考。通合科技(300491&#xff…

详解基于C#开发Windows API的SendMessage方法的鼠标键盘消息发送

在C#中,SendMessage方法是一个强大的工具,它允许我们与Windows API交互,模拟键盘和鼠标事件。本文将详细介绍如何使用SendMessage方法来发送鼠标和键盘消息。 1. SendMessage方法概述 SendMessage是Windows API中的一个函数,它用…

CSS教程(三)- CSS 三大特性

1. 层叠性 介绍 多组CSS样式共同作用于一个元素,就会出现 覆盖(层叠) 另一个冲突的样式。 层叠原则 样式冲突:遵循就近原则(哪个样式离结构近,就执行哪个样式) 样式不冲突,就不会重…

CyclicBarrier使用详解及遇到的坑

上一篇文章讲的是关于是使用CountDownLatch实现生成年底报告遇到的问题,这个计数器和CyclicBarrier也有类似功能,但是应用场景不同。 一、应用场景 CountDownLatch: 有ABCD四个任务,ABC是并行执行,等ABC三个任务都执行完…

Java-I/O框架14:Properties集合及使用

视频链接:16.32 Properties使用(2)_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Tz4y1X7H7?spm_id_from333.788.player.switch&vd_sourceb5775c3a4ea16a5306db9c7c1c1486b5&p32 1.Properties集合 特性: 存储…

Windows下mysql数据库备份策略

Windows下mysql的增量备份和全量备份,并利用schtasks设置定时任务执行bat脚本。 一、备份要求 序号 备份类型 备份频次 备份时间 1 增量备份 每周一-每周六各一次 18:00:00 2 全量备份 每周日一次 18:00:00 二、备份方法 2.1增量备份 2.1.1准备工作…

架构师备考-概念背诵(软件工程)

软件工程 软件开发生命周期: 软件定义时期:包括可行性研究和详细需求分析过程,任务是确定软件开发工程必须完成的总目标,具体可分成问题定义、可行性研究、需求分析等。软件开发时期:就是软件的设计与实现,可分成概要设计、详细设计、编码、测试等。软件运行和维护:就是…

【Linux】Linux入门实操——vim、目录结构、远程登录、重启注销

一、Linux 概述 1. 应用领域 服务器领域 linux在服务器领域是最强的,因为它免费、开源、稳定。 嵌入式领域 它的内核最小可以达到几百KB, 可根据需求对软件剪裁,近些年在嵌入式领域得到了很大的应用。 主要应用:机顶盒、数字电视、网络…

【Java项目】基于SpringBoot的【生鲜交易系统】

技术简介: 系统软件架构选择B/S模式、java技术和MySQL数据库等,总体功能模块运用自顶向下的分层思想。 系统简介: 考虑到实际生活中在生鲜交易方面的需要以及对该系统认真的分析,将系统权限按管理员,用户这两类涉及用户划分。 (…

AI Weekly『11月4-10日』: Anthropic发布Claude 3.5 Haiku,腾讯开源混元-Large模型!

大家好,我是木易,一个持续关注AI领域的互联网技术产品经理,国内Top2本科,美国Top10 CS研究生,MBA。我坚信AI是普通人变强的“外挂”,专注于分享AI全维度知识,包括但不限于AI科普,AI工…

贪心算法day3(最长递增序列问题)

目录 1.最长递增三元子序列 2.最长连续递增序列 1.最长递增三元子序列 题目链接:. - 力扣(LeetCode) 思路:我们只需要设置两个数进行比较就好。设a为nums[0],b 为一个无穷大的数,只要有比a小的数字就赋值…

vue实现图片无限滚动播放

本人vue新手菜鸡,文章为自己在项目中遇到问题的记录,如有不足还请大佬指正 文章目录 实现效果代码展示总结 因为刚接触vue,本想着看看能不能用一些element的组件实现图片的轮播效果,尝试使用过element-UI里的走马灯Carouse&#x…

[ 内网渗透实战篇-2 ] 父域子域架构的搭建与安装域环境判断域控定位组策略域森林架构配置信任关系

🍬 博主介绍 👨‍🎓 博主介绍:大家好,我是 _PowerShell ,很高兴认识大家~ ✨主攻领域:【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 🎉点赞➕评论➕收藏 养成习…