close
当前位置: 物联网在线 > 技术文库 > android >

自定义View——弹性滑动

滑动是Android开发中非常重要的UI效果,几乎所有应用都包含了滑动效果,而本文将对滑动的使用以及原理进行介绍。

一、scrollTo与ScrollBy

View提供了专门的方法用于实现滑动效果,分别为scrollTo与scrollBy。先来看看它们的源码:

/** * Set 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 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); }

从源码中可以看出scrollBy实际上是调用了scrollTo函数来实现它的功能。scrollBy实现的是输入参数的相对滑动,而scrollTo是绝对滑动。需要说明的是mScrollX、mScrollY这两个View的属性,这两个属性可以通过getScrollX、getScrollY获得。

mScrollX : View的左边缘与View内容的左边缘在水平方向上的距离,即从右向左滑动时,为正值,反之为负值。

mScrollY : View的上边缘与View内容的上边缘在竖直方向上的距离,即从下向上滑动时,为正值,反之为负值。

自定义View——弹性滑动

下面我们来实现一个滑动的效果:

public class HorizontalScroller extends ViewGroup { private int mTouchSlop; private float mLastXIntercept=0; private float mLastYIntercept=0; private float mLastX=0; private float mLastY=0; private int leftBorder; private int rightBorder; public HorizontalScroller(Context context) { super(context); init(context); } public HorizontalScroller(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public HorizontalScroller(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context){ ViewConfiguration configuration = ViewConfiguration.get(context); // 获取TouchSlop值 mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean intercept = false; float xIntercept = ev.getX(); float yIntercept = ev.getY(); switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: intercept = false; break; case MotionEvent.ACTION_MOVE: float deltaX = xIntercept-mLastXIntercept; float deltaY = yIntercept-mLastYIntercept; // 当水平方向的滑动距离大于竖直方向的滑动距离,且手指拖动值大于TouchSlop值时,拦截事件 if (Math.abs(deltaX)>Math.abs(deltaY) && Math.abs(deltaX)>mTouchSlop) { intercept=true; }else { intercept = false; } break; case MotionEvent.ACTION_UP: intercept = false; break; default: break; } mLastX = xIntercept; mLastY = yIntercept; mLastXIntercept = xIntercept; mLastYIntercept = yIntercept; return intercept; } @Override public boolean onTouchEvent(MotionEvent event) { float xTouch = event.getX(); float yTouch = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: break; case MotionEvent.ACTION_MOVE: float deltaX = xTouch-mLastX; float deltaY = yTouch-mLastY; float scrollByStart = deltaX; if (getScrollX() - deltaX < leftBorder) { scrollByStart = getScrollX()-leftBorder; } else if (getScrollX() + getWidth() - deltaX > rightBorder) { scrollByStart = rightBorder-getWidth()-getScrollX(); } scrollBy((int) -scrollByStart, 0); break; case MotionEvent.ACTION_UP: // 当手指抬起时,根据当前的滚动值来判定应该滚动到哪个子控件的界面 int targetIndex = (getScrollX() + getWidth() / 2) / getWidth(); int dx = targetIndex * getWidth() - getScrollX(); scrollTo(getScrollX()+dx,0); break; default: break; } mLastX = xTouch; mLastY = yTouch; return super.onTouchEvent(event); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View childView = getChildAt(i); // 测量每一个子控件的大小 measureChild(childView, widthMeasureSpec, heightMeasureSpec); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (changed) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View childView = getChildAt(i); // 在水平方向上对子控件进行布局 childView.layout(i * getMeasuredWidth(), 0, i * getMeasuredWidth()+childView.getMeasuredWidth()+getPaddingLeft(), childView.getMeasuredHeight()); } // 初始化左右边界值 leftBorder = 0; rightBorder = getChildCount()*getMeasuredWidth(); } } }

现在我们来分析下这段代码:

首先在构造函数中获取了最小滑动距离TouchSlop。


(责任编辑:ioter)

用户喜欢...

Android Studio 自定义皮肤主题和背景

新的一年,新的开始,在这里先祝福大家在新的一年收获多多,多升职,多加薪,代码没BUG。 第一 Android Studio自定义皮肤主题-ColorThemes 寒假期间有人问我, “你代码这是用的什么字体? 看着...


用AndroidStudio开发自定义 Gradle plugin

利用AndroidStudio,编写自定义Gradle plugin MavenDeployer 发布plugin 使用Gradle plugin 简介 项目中引入自定义Gradle plugin一般有三种方法: 直接写在 build.gradle中. plugin源码放到rootProjectDir/buildSrc/src/main/groo...


Android 自定义View 跳动的水果和文字

这是自定义View和动画的第二篇,第一篇是Android drawPath实现QQ拖拽泡泡,主要介绍了drawPath 绘制二次贝塞尔曲线的过程。 话不多说,还是先上效果图吧!(今天手贱升级了Genymotion,就成这个傻...


Android TextView 添加超链接的两种实现方式

在textView添加超链接,有两种方式,第一种通过HTML格式化你的网址,一种是设置autolink,让系统自动识别超链接,下面为大家介绍下这两种方法的实现 代码如下: 第一种 public class MainActivit...


Android自定义9宫格图片视图

类似微信朋友圈中的图片展示大家肯定很熟悉了,这篇文章讲述的自定义View就是类似这个展示方式的View了。 先看效果图: 展示规则 1、如果只有1张图片,则图片宽度占父控件总宽度的2/3(...


详解View的基础概念

在Android中, View作为最重要的概念, 参数较多. 显示控件都继承于View, 包含ViewGroup也是继承于View. 在View中, 核心概念包含Position(位置), MotionEvent(运动事件), TouchSlop(触摸间隔), VelocityTracker(速度追...


Android带你解析ScrollView--仿QQ空间标题栏渐变

今天来研究的是ScrollView-滚动视图,滚动视图又分横向滚动视图(HorizontalScrollView)和纵向滚动视图(ScrollView),今天主要研究纵向的。相信大家在开发中经常用到,ScrollView的功能已经很强大...


自定义View——invalidate传递与绘制流程分析

上一篇文章 自定义View——View的弹性滑动 中,我们对View的滑动进行了实战以及简单分析。但在文章的最后,仍然遗留了两个问题,第一个是invalidate与postInvalidate有什么区别呢?第二个是inval...


Android开源 - 自定义CheckBox

继承View还是CheckBox 要实现的效果是类似 考虑到关键是动画效果,所以直接继承View。不过CheckBox的超类CompoundButton实现了Checkable接口,这一点值得借鉴。 下面记录一下遇到的问题,并从源码的...


Android 自定义选项菜单,仿 QQ 主界面菜单

Android自定义选项菜单,弹性缩放动画,仿QQ主界面菜单弹窗 效果图如下...