Scroller 的使用详解二 (侧滑删除按钮的实现)

本贴最后更新于 2853 天前,其中的信息可能已经事过境迁

更对分享:http://www.cherylgood.cn

  • 我们在前面 Scroller 的使用详解一中学习了 Scroller 的基本使用,当时的 demo 的滚动是跳跃式的,并没有实现我们预期中的平滑滚动,在接下来的内容中,我们将通过一个侧滑删除按钮的 demo 来继续学习 Scroller 的相关知识。
  • 没图啥也不用说,先上图吧!

  • Scroller 的使用主要可以分为以下几个步骤:
  • 创建 Scroller 实例
  • 调用 Scroller 对象的 startScroll()方法来初始化滚动数据并
    调用 invalidate 方法刷新界面
  • 重写 computeScroll()方法,并在其内部完成平滑滚动的逻辑,刷新时也需调用 invalidate 刷新界面

废话不多说,直接上代码:
我们创建一个 EasySwipeMenuLayout 继承自 ViewGroup

package guanaj.com.scrollerdemo; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.Scroller; /** * Created by guanaj on 2017/4/18. */ public class EasySwipeMeunLayout extends ViewGroup { private static final String TAG = "EasySwipeMeunLayout"; private int mScaledTouchSlop; private int mContentWidth; private float lastx; private float lasty; private float firstx; private float firsty; private Scroller mScroller; private int mRightMenuWidths; public EasySwipeMeunLayout(Context context) { this(context, null); } public EasySwipeMeunLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public EasySwipeMeunLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs, defStyleAttr); } private void init(Context context, AttributeSet attrs, int defStyleAttr) { //创建辅助类对象 //用户需滑动的最小的距离才被认为是滑动了,单位像素 ViewConfiguration viewConfiguration = ViewConfiguration.get(context); mScaledTouchSlop = viewConfiguration.getScaledTouchSlop(); mScroller = new Scroller(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int mHeight = 0; mContentWidth = 0; //获取child view 数量 int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View childView = getChildAt(i); childView.setClickable(true); if (childView.getVisibility() != GONE) { //测量child view measureChild(childView, widthMeasureSpec, heightMeasureSpec); mHeight = Math.max(mHeight, childView.getMeasuredHeight()); if (i == 0) { //第一个为内容view mContentWidth = childView.getMeasuredWidth(); } else { //第二个为左滑按钮 mRightMenuWidths = childView.getMeasuredWidth(); } } } setMeasuredDimension(getPaddingLeft() + getPaddingRight() + mContentWidth, mHeight + getPaddingTop() + getPaddingBottom()); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View childView = getChildAt(i); if (childView.getVisibility() != GONE) { if (i == 0) { childView.layout(getPaddingLeft(), getPaddingTop(), getPaddingLeft() + childView.getMeasuredWidth(), getPaddingTop() + childView.getMeasuredHeight()); } else { childView.layout(mContentWidth, getPaddingTop(), mContentWidth + childView.getMeasuredWidth(), getPaddingTop() + childView.getMeasuredHeight()); } } } } @Override public boolean onInterceptTouchEvent(MotionEvent event) { Log.d(TAG, "dispatchTouchEvent() called with: " + "ev = [" + event + "]"); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { lastx = event.getRawX(); lasty = event.getRawY(); firstx = event.getRawX(); firsty = event.getRawY(); break; } case MotionEvent.ACTION_MOVE: { //对左边界进行处理 float distance = lastx - event.getRawX(); if (Math.abs(distance) > mScaledTouchSlop) { // 当手指拖动值大于mScaledTouchSlop值时,认为应该进行滚动,拦截子控件的事件 return true; } break; } } return super.onInterceptTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_MOVE: { float distance = lastx - event.getRawX(); Log.d(TAG, "onTouchEvent() ACTION_MOVE getScrollX:" + getScrollX()); lastx = event.getRawX(); lasty = event.getRawY(); scrollBy((int) distance, 0); break; } case MotionEvent.ACTION_UP: if (getScrollX() <= 0) { //对右边界进行处理,不让其滑出 mScroller.startScroll(getScrollX(), 0, -getScrollX(), 0); } else if (getScrollX() > 0 && getScrollX() >= mRightMenuWidths / 3) { //删除按钮滑出区域大于1/3,滑出删除按钮 mScroller.startScroll(getScrollX(), 0, mRightMenuWidths-getScrollX(), 0); } else { //删除按钮滑出区域小于1/3,滑回原来的位置 mScroller.startScroll(getScrollX(), 0, -getScrollX(), 0); } invalidate(); //通知View重绘-invalidate()->onDraw()->computeScroll() break; } return super.onTouchEvent(event); } @Override public void computeScroll() { //判断Scroller是否执行完毕: if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); //通知View重绘-invalidate()->onDraw()->computeScroll() invalidate(); } } }

过程讲解:

  • 第一步:重写三个构造方法,并在第三个构造方法中调用 init 方法初始化必要的对象;

  • 第二步:在 onMeasure 方法中测量子 view 的大小,获得内容 view 的宽度以及删除按钮的宽度

  • 第三步:在 onLayout 方法中设置 child view 的位置,

  • 第四步:重写 onInterceptTouchEvent 方法,获取首次触摸位置以及在滑动距离大于滑动的最小距离 mScaledTouchSlop 拦截子控件的触摸事件

  • 第五步:重写 onTouchEvent 方法,在里面进行滑动效果的实现,从下面代码中我们可以看到,主要是通过 scrollBy 来实现滑动中的效果

    case MotionEvent.ACTION_MOVE: { float distance = lastx - event.getRawX(); Log.d(TAG, "onTouchEvent() ACTION_MOVE getScrollX:" + getScrollX()); lastx = event.getRawX(); lasty = event.getRawY(); scrollBy((int) distance, 0); break; }

    而在用户松开手的时候,我们主要处理三种情况,如下代码:

    • 1、左边区域被滑出,我们应将其恢复;

    • 2、右边删除按钮被滑出,我们这里假设滑出三分之一以上即为滑出,所以需判断是否滑出可视区域占其三分之一以上;

    • 3、滑出区域小与三分之一,将其恢复;

      case MotionEvent.ACTION_UP: if (getScrollX() <= 0) { //对右边界进行处理,不让其滑出 mScroller.startScroll(getScrollX(), 0, -getScrollX(), 0); } else if (getScrollX() > 0 && getScrollX() >= mRightMenuWidths / 3) { //删除按钮滑出区域大于1/3,滑出删除按钮 mScroller.startScroll(getScrollX(), 0, mRightMenuWidths-getScrollX(), 0); } else { //删除按钮滑出区域小于1/3,滑回原来的位置 mScroller.startScroll(getScrollX(), 0, -getScrollX(), 0); } invalidate(); //通知View重绘-invalidate()->onDraw()->computeScroll() break;
  • 第六步:重写 computeScroll 方法,通过 mScroller.computeScrollOffset 犯法判断滑动是否完成,未完成则调用 scrollTo 方法使其继续滑动。

    @Override public void computeScroll() { //判断Scroller是否执行完毕: if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); //通知View重绘-invalidate()->onDraw()->computeScroll() invalidate(); } }

使用时布局文件:

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" tools:context="guanaj.com.scrollerdemo.MainActivity"> <LinearLayout android:id="@+id/ll_content" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/holo_green_light"> <TextView android:id="@+id/txt" android:layout_width="50dp" android:layout_height="50dp" android:layout_marginBottom="6dp" android:layout_marginTop="6dp" android:background="@android:color/holo_red_dark" android:gravity="center" android:text="我滚" /> </LinearLayout> <Button android:id="@+id/start_scrollby" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:text="scrollBy" /> <Button android:id="@+id/start_scrollto" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="scrollTO" /> <guanaj.com.scrollerdemo.EasySwipeMeunLayout android:layout_width="match_parent" android:layout_height="60dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="#cccccc"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> <TextView android:layout_width="100dp" android:layout_height="match_parent" android:background="#ff0000" android:gravity="center" android:text="删除" android:textColor="@android:color/white" android:textSize="20sp" /> </guanaj.com.scrollerdemo.EasySwipeMeunLayout> </LinearLayout>

代码已上传马云 https://git.oschina.net/osczaizai/AndroidDemo

  • Android

    Android 是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    335 引用 • 324 回帖
  • Scroller
    2 引用 • 1 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • Windows

    Microsoft Windows 是美国微软公司研发的一套操作系统,它问世于 1985 年,起初仅仅是 Microsoft-DOS 模拟环境,后续的系统版本由于微软不断的更新升级,不但易用,也慢慢的成为家家户户人们最喜爱的操作系统。

    226 引用 • 476 回帖
  • Hibernate

    Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。

    39 引用 • 103 回帖 • 719 关注
  • OpenShift

    红帽提供的 PaaS 云,支持多种编程语言,为开发人员提供了更为灵活的框架、存储选择。

    14 引用 • 20 回帖 • 649 关注
  • Eclipse

    Eclipse 是一个开放源代码的、基于 Java 的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。

    76 引用 • 258 回帖 • 631 关注
  • 小说

    小说是以刻画人物形象为中心,通过完整的故事情节和环境描写来反映社会生活的文学体裁。

    31 引用 • 108 回帖
  • jsoup

    jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。

    6 引用 • 1 回帖 • 489 关注
  • 心情

    心是产生任何想法的源泉,心本体会陷入到对自己本体不能理解的状态中,因为心能产生任何想法,不能分出对错,不能分出自己。

    59 引用 • 369 回帖
  • 国际化

    i18n(其来源是英文单词 internationalization 的首末字符 i 和 n,18 为中间的字符数)是“国际化”的简称。对程序来说,国际化是指在不修改代码的情况下,能根据不同语言及地区显示相应的界面。

    8 引用 • 26 回帖 • 2 关注
  • Rust

    Rust 是一门赋予每个人构建可靠且高效软件能力的语言。Rust 由 Mozilla 开发,最早发布于 2014 年 9 月。

    58 引用 • 22 回帖 • 2 关注
  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 28 关注
  • 30Seconds

    📙 前端知识精选集,包含 HTML、CSS、JavaScript、React、Node、安全等方面,每天仅需 30 秒。

    • 精选常见面试题,帮助您准备下一次面试
    • 精选常见交互,帮助您拥有简洁酷炫的站点
    • 精选有用的 React 片段,帮助你获取最佳实践
    • 精选常见代码集,帮助您提高打码效率
    • 整理前端界的最新资讯,邀您一同探索新世界
    488 引用 • 384 回帖 • 3 关注
  • Wide

    Wide 是一款基于 Web 的 Go 语言 IDE。通过浏览器就可以进行 Go 开发,并有代码自动完成、查看表达式、编译反馈、Lint、实时结果输出等功能。

    欢迎访问我们运维的实例: https://wide.b3log.org

    30 引用 • 218 回帖 • 641 关注
  • WebComponents

    Web Components 是 W3C 定义的标准,它给了前端开发者扩展浏览器标签的能力,可以方便地定制可复用组件,更好的进行模块化开发,解放了前端开发者的生产力。

    1 引用 • 8 关注
  • Markdown

    Markdown 是一种轻量级标记语言,用户可使用纯文本编辑器来排版文档,最终通过 Markdown 引擎将文档转换为所需格式(比如 HTML、PDF 等)。

    169 引用 • 1527 回帖
  • SSL

    SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS 与 SSL 在传输层对网络连接进行加密。

    70 引用 • 193 回帖 • 416 关注
  • Love2D

    Love2D 是一个开源的, 跨平台的 2D 游戏引擎。使用纯 Lua 脚本来进行游戏开发。目前支持的平台有 Windows, Mac OS X, Linux, Android 和 iOS。

    14 引用 • 53 回帖 • 544 关注
  • 支付宝

    支付宝是全球领先的独立第三方支付平台,致力于为广大用户提供安全快速的电子支付/网上支付/安全支付/手机支付体验,及转账收款/水电煤缴费/信用卡还款/AA 收款等生活服务应用。

    29 引用 • 347 回帖
  • Node.js

    Node.js 是一个基于 Chrome JavaScript 运行时建立的平台, 用于方便地搭建响应速度快、易于扩展的网络应用。Node.js 使用事件驱动, 非阻塞 I/O 模型而得以轻量和高效。

    139 引用 • 269 回帖
  • FFmpeg

    FFmpeg 是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。

    23 引用 • 32 回帖
  • 脑图

    脑图又叫思维导图,是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具。

    31 引用 • 96 回帖 • 1 关注
  • Git

    Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

    211 引用 • 358 回帖 • 2 关注
  • jQuery

    jQuery 是一套跨浏览器的 JavaScript 库,强化 HTML 与 JavaScript 之间的操作。由 John Resig 在 2006 年 1 月的 BarCamp NYC 上释出第一个版本。全球约有 28% 的网站使用 jQuery,是非常受欢迎的 JavaScript 库。

    63 引用 • 134 回帖 • 734 关注
  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1063 引用 • 3455 回帖 • 164 关注
  • 区块链

    区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。所谓共识机制是区块链系统中实现不同节点之间建立信任、获取权益的数学算法 。

    92 引用 • 752 回帖 • 1 关注
  • Python

    Python 是一种面向对象、直译式电脑编程语言,具有近二十年的发展历史,成熟且稳定。它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务。它的语法简捷和清晰,尽量使用无异义的英语单词,与其它大多数程序设计语言使用大括号不一样,它使用缩进来定义语句块。

    556 引用 • 674 回帖
  • 阿里巴巴

    阿里巴巴网络技术有限公司(简称:阿里巴巴集团)是以曾担任英语教师的马云为首的 18 人,于 1999 年在中国杭州创立,他们相信互联网能够创造公平的竞争环境,让小企业通过创新与科技扩展业务,并在参与国内或全球市场竞争时处于更有利的位置。

    43 引用 • 221 回帖 • 75 关注
  • Access
    1 引用 • 3 回帖 • 7 关注