使用各种App的时候,发现一个特点,现在的App在可以滑动的页面中,基本上都添加了一个置顶按钮的效果,使的用户可以很方便的回到顶部,就像网页中几乎所有的页面都有置顶效果按钮一样,觉得这个交互不错,以后自己的项目里也肯定会用到的,就抽空把这个小功能实现了一下,现在把实现过程记录下,以方便以后使用。

先看下效果图:

当ScrollView向上滑动超过一定距离后,就渐变的出现一个置顶的按钮,当滑动距离小于我们指定的距离时,按钮又以一个渐变的方式消失。

实现原理:

这个小案例的实现原理很简单:1.就是监听View的onScrollChanged()方法,获取到ScrolView滑动的距离,如果大于我们的距离,则出现置顶按钮,否则,隐藏

2.按钮点击事件的处理,点击按钮让整个ScrollView滑动到(0,0)位置即可。

虽然这个案例很简单,但是却涉及到了android的滑动原理,从onScrollViewChanged这个方法属于View这个类也可以看出,android的任何View都是可以滑动的,在这个案例后,我会说一下我对android滑动原理的理解。

自定义ScrollView代码:

<span style="font-size:14px;">
/*********************************************************** @文件名称:GoTopScrollView.java* @文件作者:rzq* @文件描述:滑动超过一定距离后,出现置顶按钮* @修改历史:2015年3月26日创建初始版本**********************************************************/
public class GoTopScrollView extends ScrollView implements OnClickListener
{private ImageView goTopBtn;private int screenHeight;public GoTopScrollView(Context context, AttributeSet attrs){super(context, attrs);}public void setScrollListener(ImageView goTopBtn){this.goTopBtn = goTopBtn;this.goTopBtn.setOnClickListener(this);}@Overrideprotected void onScrollChanged(int l, int t, int oldl, int oldt){super.onScrollChanged(l, t, oldl, oldt);/*** 滑动距离超过300px,出现向上按钮,可以做为自定义属性*/if (t >= 300){goTopBtn.setVisibility(View.VISIBLE);}else{goTopBtn.setVisibility(View.GONE);}}@Overridepublic void onClick(View v){if (v.getId() == R.id.go_top_btn){this.smoothScrollTo(0, 0);}}
}</span>

Activity使用代码:

<span style="font-size:14px;">private void initView(){container = (RelativeLayout) findViewById(R.id.container);container.setLayoutTransition(new LayoutTransition());goTopBtn = (ImageView) findViewById(R.id.go_top_btn);scrollView = (GoTopScrollView) findViewById(R.id.scroll_view);scrollView.setScrollListener(goTopBtn);}</span>

这样就很轻松的实现了这个置顶的效果,但仅仅这样是不够的,现在我们来分析一下android的滑动原理到底是怎么回事,是不是只有ScrollView才可以滑动。

1.首先,android中任意的View,都是可以滑动的,为什么呢?请看View中的关键源码,只挑出和滑动有关的代码:

 <span style="font-size:14px;"> /*** 指定X轴的滑动,Y轴不动* @param value the x position to scroll to*/public void setScrollX(int value) {scrollTo(value, mScrollY);}/*** 指定Y轴的滑动,X轴不动* @param value the y position to scroll to*/public void setScrollY(int value) {scrollTo(mScrollX, value);}/*** 真正处理滑动的方法* @param x the x position to scroll to* @param y the y position to scroll to*/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();}}}/*** Move the scrolled position of your view. This will cause a call to* {@link #onScrollChanged(int, int, int, int)} and the view will be* invalidated.* @param x the amount of pixels to scroll by horizontally* @param y the amount of pixels to scroll by vertically*/public void scrollBy(int x, int y) {scrollTo(mScrollX + x, mScrollY + y);}/*** 触发滑动后的监听* @param l Current horizontal scroll origin.* @param t Current vertical scroll origin.* @param oldl Previous horizontal scroll origin.* @param oldt Previous vertical scroll origin.*/protected void onScrollChanged(int l, int t, int oldl, int oldt) {if (AccessibilityManager.getInstance(mContext).isEnabled()) {postSendViewScrolledAccessibilityEventCallback();}mBackgroundSizeChanged = true;final AttachInfo ai = mAttachInfo;if (ai != null) {ai.mViewScrollChanged = true;}}</span><span style="font-size:14px;">
</span>

由这几个方法可以看出,android中所有View都是可以滑动的,其实所有的View都有两套坐标系,一个是指定View位置的X,Y,另一对则是指定View滑动坐标系,mScrollX和mScrollY,两对坐标系统是完全独立的,即X,Y坐标的改变不会影响mScrollX和mScrollY,反之也成立。下面,我们则重点分析一下scrollTo这个方法。

scrollTo(int x, int y) 是将View中内容滑动到相应的位置,参考的坐标系原点为parent View的左上角。

调用scrollTo(100, 0)表示将View中的内容移动到x = 100, y = 0的位置,如下图所示。注意,图中黄色矩形区域表示的是一个parent View,绿色虚线矩形为parent view中的内容。一般情况下两者的大小一致,本文为了显示方便,将虚线框画小了一点。图中的黄色区域的位置始终不变,发生位置变化的是显示的内容,如图所示:

mScrollX和mScrollY是View类中专门用于记录滑动位置的变量。这两个函数最终调用onScrollChanged()函数。

先总结一下,android的滑动原理就是:通过调用scrollTo(),使的View的滑动坐标系发生改变,并保存在mScrollX和mScrollY这两个成员变量中,我们可以通过getScrollX()和getScrollY(),获取滑动坐标值。

由图中我们可以看到,当我们调用scrollTo(100,0);时,发现View是向左滑动了,传入一个正数滑动的方向却是向左,这与我们平常理解的坐标轴是相反的,其实并不矛盾,因为滑动坐标系本来就与我们平常的坐标系统不一样,调用scrollTo方法,最终会执行到以下代码:

public void invalidate(int l, int t, int r, int b) {  if (ViewDebug.TRACE_HIERARCHY) {  ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);  }  if (skipInvalidate()) {  return;  }  if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS) ||  (mPrivateFlags & DRAWING_CACHE_VALID) == DRAWING_CACHE_VALID ||  (mPrivateFlags & INVALIDATED) != INVALIDATED) {  mPrivateFlags &= ~DRAWING_CACHE_VALID;  mPrivateFlags |= INVALIDATED;  mPrivateFlags |= DIRTY;  final ViewParent p = mParent;  final AttachInfo ai = mAttachInfo;  //noinspection PointlessBooleanExpression,ConstantConditions  if (!HardwareRenderer.RENDER_DIRTY_REGIONS) {  if (p != null && ai != null && ai.mHardwareAccelerated) {  // fast-track for GL-enabled applications; just invalidate the whole hierarchy  // with a null dirty rect, which tells the ViewAncestor to redraw everything  p.invalidateChild(this, null);  return;  }  }  if (p != null && ai != null && l < r && t < b) {  final int scrollX = mScrollX;  final int scrollY = mScrollY;  final Rect tmpr = ai.mTmpInvalRect;  tmpr.set(l - scrollX, t - scrollY, r - scrollX, b - scrollY);  p.invalidateChild(this, tmpr);  }  }
}  

看倒数第五行代码,更新滑动坐标系时,走的是 tmpr.set(l - scrollX, t - scrollY, r - scrollX, b - scrollY);传入一个正数后一减,就成了负数,自然要向左移动了,这样就又统一起来了。

总结:以上差不多就是View自带的滑动原理,但View中的滑动都是瞬时滑动。没有任何的过渡效果,结合Scroller是可以做出各种各样的过渡效果的,下篇文章,总结一下如何结合Scroller使View更加炫的滑动。

参考文章: 图解android View的scroll原理

android自定义View之自定义可置顶ScrollView,View滑动原理简析相关推荐

  1. Android Handler与Looper原理简析

    一直感觉自己简直就是一个弱智,最近越来越感觉是这样了,真的希望自己有一天能够认同自己,认同自己. 本文转载于:https://juejin.im/post/59083d7fda2f60005d14ef ...

  2. Android V1及V2签名原理简析

    Android为了保证系统及应用的安全性,在安装APK的时候需要校验包的完整性,同时,对于覆盖安装的场景还要校验新旧是否匹配,这两者都是通过Android签名机制来进行保证的,本文就简单看下Andro ...

  3. 帖子置顶原理 php,自定义织梦cms文章置顶及其功能原理分析

    本人在织梦dedecms本发分类信息发布系统那个功能的时候,因为,用到置顶功能,这是很多分类信息系统最很重要的特色,所以,对这个作了一个织梦dedecms系统的研究,以前用织梦dedecms系统建站, ...

  4. android listview标题置顶,Android仿QQ左滑删除置顶ListView操作

    最近闲来无事,于是研究了一下qq的左滑删除效果,尝试着实现了一下,先上效果图: 大致思路原理: - 通过设置margin实现菜单的显示与隐藏 - 监听onTouchEvent,处理滑动事件 上代码 i ...

  5. Android 解决下拉刷新控件和ScrollVIew的滑动冲突问题。

    最近项目要实现ScrollView中嵌套广告轮播图+RecyleView卡片布局,并且RecyleView按照header和内容的排列样式,因为RecyleView的可扩展性很强,所以我毫无疑问的选择 ...

  6. 《Android 网络开发与应用实战详解》——2.1节简析Android安装文件

    本节书摘来自异步社区<Android 网络开发与应用实战详解>一书中的第2章,第2.1节简析Android安装文件,作者 王东华,更多章节内容可以访问云栖社区"异步社区" ...

  7. Android Xposed热修复原理简析

    简单介绍下热修复,基于Xposed中的思想,通过修改c层的Method实例描述,来实现更改与之对应的java方法的行为,从而达到修复的目的. Xposed: 诞生于XDA论坛,类似一个应用平台,不同的 ...

  8. Android Vsync原理简析

    从图中可以看到,有了VSYNC,cpu总是在指定的地方开始. 有人会问: 说白了,真正解决问题的是VSYNC,而不是双缓冲,那不要双缓冲只要VSYNC不是也可以吗? 好,我们假设只有VSYNC,现在假 ...

  9. Android热补丁技术—dexposed原理简析(手机淘宝采用方案)

    上篇文章<Android无线开发的几种常用技术>我们介绍了几种android移动应用开发中的常用技术,其中的热补丁正在被越来越多的开发团队所使用,它涉及到dalvik虚拟机和android ...

  10. android 应用置顶到最前端_uniApp / 小程序实现一个view滚动到一定高度置顶显示

    最近在使用uniApp做一个模仿美团小程序的demo,其中遇到这样一个需求,就是筛选模块滚动到顶部就置顶显示 查阅了uniApp的官方文档以及微信小程序的官方文档,发现都有一个相同的API,如下: 实 ...

最新文章

  1. 链表排序(冒泡、选择、插入、快排、归并、希尔、堆排序)
  2. boost::process::search_path相关的测试程序
  3. Index of Oracle
  4. 京东 你访问的页面需要验证证书_SSL证书安全认证有什么原理?
  5. 图片插值数据_结合PS用这招来增强ArcGIS插值图出图效果
  6. 解决:如何在Android Studio中找出自己项目文件的所在位置
  7. oracle display set,Check if the DISPLAY variable is set
  8. Chapter 2 Open Book——5
  9. 谷歌死磕亚马逊,CES 舞台上的语音入口争夺战
  10. 利用存储过程批量生成数据
  11. 关于营造团队良好氛围的讨论
  12. MYSQL间隙锁详解
  13. Final Cut Pro X 精选插件合集!
  14. 我的家用户外监控摄像头的选购和安装记录
  15. 基于JAVA公立医院绩效考核系统计算机毕业设计源码+数据库+lw文档+系统+部署
  16. Phonics 自然拼读法 S A T I P N Teacher:Lamb
  17. 武僧一龙狂殴韩国拳王 用中国功夫醉拳轻松赢战
  18. html页脚位置调整,将页脚始终控制在页面最下方的几种方法
  19. 全志哪吒D1-H Tina Linux Ubuntu 22.04入门踩坑日记
  20. 详细聊聊固码——第三方支付收单方的机会

热门文章

  1. SQL:postgresql将查询结果拼接起来
  2. JavaScript:递归实现深拷贝
  3. Python:学习笔记之变量
  4. 数据仓库中OLTP和OLAP的区别
  5. 工作心得_在做算法工程师的道路上,你掌握了什么概念或技术使你感觉自我提升突飞猛进?
  6. Centos下安装Mongodb
  7. 80)PHP,扩展工具类
  8. iOS 学习 - 18.TextField 自定义菜单事件,复制和微信分享
  9. I学霸官方免费教程四十二 :Java流之字节流 输入流和输出流 InputStream和OutputStream...
  10. getBoundingClientRect()兼容性处理