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

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

更对分享: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 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    336 引用 • 324 回帖 • 1 关注
  • Scroller
    2 引用 • 1 回帖

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • Windows

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

    228 引用 • 476 回帖
  • PWA

    PWA(Progressive Web App)是 Google 在 2015 年提出、2016 年 6 月开始推广的项目。它结合了一系列现代 Web 技术,在网页应用中实现和原生应用相近的用户体验。

    14 引用 • 69 回帖 • 182 关注
  • frp

    frp 是一个可用于内网穿透的高性能的反向代理应用,支持 TCP、UDP、 HTTP 和 HTTPS 协议。

    17 引用 • 7 回帖
  • 人工智能

    人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。

    115 引用 • 318 回帖
  • GAE

    Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。

    14 引用 • 42 回帖 • 820 关注
  • Log4j

    Log4j 是 Apache 开源的一款使用广泛的 Java 日志组件。

    20 引用 • 18 回帖 • 33 关注
  • Chrome

    Chrome 又称 Google 浏览器,是一个由谷歌公司开发的网页浏览器。该浏览器是基于其他开源软件所编写,包括 WebKit,目标是提升稳定性、速度和安全性,并创造出简单且有效率的使用者界面。

    63 引用 • 289 回帖
  • 支付宝

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

    29 引用 • 347 回帖
  • IPFS

    IPFS(InterPlanetary File System,星际文件系统)是永久的、去中心化保存和共享文件的方法,这是一种内容可寻址、版本化、点对点超媒体的分布式协议。请浏览 IPFS 入门笔记了解更多细节。

    20 引用 • 245 回帖 • 234 关注
  • SSL

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

    70 引用 • 193 回帖 • 414 关注
  • 互联网

    互联网(Internet),又称网际网络,或音译因特网、英特网。互联网始于 1969 年美国的阿帕网,是网络与网络之间所串连成的庞大网络,这些网络以一组通用的协议相连,形成逻辑上的单一巨大国际网络。

    98 引用 • 367 回帖
  • Unity

    Unity 是由 Unity Technologies 开发的一个让开发者可以轻松创建诸如 2D、3D 多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

    25 引用 • 7 回帖 • 121 关注
  • 资讯

    资讯是用户因为及时地获得它并利用它而能够在相对短的时间内给自己带来价值的信息,资讯有时效性和地域性。

    56 引用 • 85 回帖
  • BND

    BND(Baidu Netdisk Downloader)是一款图形界面的百度网盘不限速下载器,支持 Windows、Linux 和 Mac,详细介绍请看这里

    107 引用 • 1281 回帖 • 37 关注
  • Ruby

    Ruby 是一种开源的面向对象程序设计的服务器端脚本语言,在 20 世纪 90 年代中期由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)设计并开发。在 Ruby 社区,松本也被称为马茨(Matz)。

    7 引用 • 31 回帖 • 261 关注
  • OpenShift

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

    14 引用 • 20 回帖 • 664 关注
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    947 引用 • 1460 回帖 • 2 关注
  • Ant-Design

    Ant Design 是服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。

    17 引用 • 23 回帖 • 3 关注
  • H2

    H2 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,不受平台的限制,同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。

    11 引用 • 54 回帖 • 671 关注
  • Openfire

    Openfire 是开源的、基于可拓展通讯和表示协议 (XMPP)、采用 Java 编程语言开发的实时协作服务器。Openfire 的效率很高,单台服务器可支持上万并发用户。

    6 引用 • 7 回帖 • 119 关注
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    189 引用 • 1349 回帖 • 1 关注
  • Sphinx

    Sphinx 是一个基于 SQL 的全文检索引擎,可以结合 MySQL、PostgreSQL 做全文搜索,它可以提供比数据库本身更专业的搜索功能,使得应用程序更容易实现专业化的全文检索。

    1 引用 • 229 关注
  • Thymeleaf

    Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。类似 Velocity、 FreeMarker 等,它也可以轻易的与 Spring 等 Web 框架进行集成作为 Web 应用的模板引擎。与其它模板引擎相比,Thymeleaf 最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个 Web 应用。

    11 引用 • 19 回帖 • 395 关注
  • 导航

    各种网址链接、内容导航。

    45 引用 • 177 回帖 • 2 关注
  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖
  • ZeroNet

    ZeroNet 是一个基于比特币加密技术和 BT 网络技术的去中心化的、开放开源的网络和交流系统。

    1 引用 • 21 回帖 • 654 关注
  • DNSPod

    DNSPod 建立于 2006 年 3 月份,是一款免费智能 DNS 产品。 DNSPod 可以为同时有电信、网通、教育网服务器的网站提供智能的解析,让电信用户访问电信的服务器,网通的用户访问网通的服务器,教育网的用户访问教育网的服务器,达到互联互通的效果。

    6 引用 • 26 回帖 • 537 关注