Scroller的使用,让View随心所欲的移动
来源:互联网 发布:python统计字母个数 编辑:程序博客网 时间:2024/05/01 17:01
Scroller的使用,让View随心所欲的移动吧
效果图:
Scroller能控制View自由的移动,相对于其他让View移动的api,例如ViewDragHelper,
我个人倾向于使用Scroller,因为他使用起来简单,感觉更灵活一些。
值得注意的是,在一个View使用Scroller是不能让这个View移动的,
需要在ViewGroup或者他的子类里使用Scroller,才能让ViewGroup里的所有子View一起移动起来。
下面说Scroller让ViewGroup的所有子类移动的方法:
1,首先,在ViewGroup中创建Scroller对象。
public class ScrollViewGroup extends FrameLayout { private static final String TAG = ScrollViewGroup.class.getSimpleName(); private Scroller mScroller; private int mHeight; private int mWidth; public ScrollViewGroup(Context context) { super(context); init(); } public ScrollViewGroup(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ScrollViewGroup(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mScroller = new Scroller(getContext()); }
2,其次,复写View的 computeScroll() 方法,代码为模版代码,复制使用即可。
@Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } }
3,然后,使用Scroller,让ViewGroup的子View移动起来。
public void smoothScrollTo(int startX, int startY, int dx, int dy, int duration) { mScroller.startScroll(startX, startY, dx, dy, duration); invalidate(); }
就这三步骤,就能让ViewGroup里的所有子View移动了。
下面说怎么玩Scroller
看这行代码:
mScroller.startScroll(startX, startY, dx, dy, duration); invalidate();
startX是这个ViewGroup开始的X坐标,就是ViewGroup左上角的X坐标。而startY对应的是Y坐标。意思是,你想让ViewGroup初始点在哪里。
dx是X坐标的偏移量,你想让这个ViewGroup里的子View在X轴方向移动多少距离。而dy是Y坐标的偏移量。
duration是完成这一次移动所需要的时间。
如图:
例如,让ViewGroup里的子View,向下移动整个ViewGroup的高度,也就是上面动态图的向下移动down
1,首先,获取到ViewGroup的高度。
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mHeight = getMeasuredHeight(); mWidth = getMeasuredWidth(); Log.i(TAG, "height:" + mHeight + " ,width:" + mWidth); }
2,其次,设置参数,使用Scroller完成移动。
public void down() { int startX = 0; int startY = 0; int dx = 0; int dy = -mHeight; int duration = 2000; smoothScrollTo(startX, startY, dx, dy, duration); }
值得注意的是,开始点0,0,也就是手机屏幕左上角那一点,移动Y方向的偏移量为 -mHeight,没错,是负的ViewGroup的高度。
这样,就可以让ViewGroup里的所有子View向下移动了。
接下来,来分析Scroller是如何完成移动的工作的。
看这两行代码:
mScroller.startScroll(startX, startY, dx, dy, duration); invalidate();
CTRL+B,进入到startScroll方法里:
public void startScroll(int startX, int startY, int dx, int dy, int duration) { mMode = SCROLL_MODE; mFinished = false; mDuration = duration; mStartTime = AnimationUtils.currentAnimationTimeMillis(); mStartX = startX; mStartY = startY; mFinalX = startX + dx; mFinalY = startY + dy; mDeltaX = dx; mDeltaY = dy; mDurationReciprocal = 1.0f / (float) mDuration; }
可以看到,方法里把传递进来的参数赋值了一下,开始坐标,偏移量,使用的时间,还有移动后的坐标mFinalX,Y
再看这个方法:
@Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } }
模版代码,一直可以这样写。进入到computeScrollOffset里:
public boolean computeScrollOffset() { if (mFinished) { return false; }
可以看到,当mFinished为true时返回false,也就是不会继续走computeScroll里的代码了,在继续看:
public boolean computeScrollOffset() { if (mFinished) { return false; } int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime); if (timePassed < mDuration) { switch (mMode) { case SCROLL_MODE: final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal); mCurrX = mStartX + Math.round(x * mDeltaX); mCurrY = mStartY + Math.round(x * mDeltaY); break;
可以看出,mCurrX ,Y 的值是有开始坐标不断的增加的。增加的与x有关,注意这里的timePassed ,可以看出,这是一个不断接近duration的值,
mStartTime 在之前:
mStartTime = AnimationUtils.currentAnimationTimeMillis();
为一个时间戳,开始的时间。
timePassed 是一个随着时间流逝的值,根据这个差值进行计算,累加到mCurrX ,Y,达到一个在一段时间内,不断累加的效果,设计得挺妙的。
然后:
scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate();
再然后:
public final int getCurrX() { return mCurrX; }
可以看到使用了增加之后的mCurrX ,Y,调用scrollTo来完成ViewGroup的子View移动的效果。
scrollTo是一下子移动到某一点,并没有动态,循序渐进的效果。那为什么会出现动态的效果呢?
答案就是这个方法:
postInvalidate
重新绘制View,也就是调用View的draw方法进行重新绘制视图。
查看View源码找到draw方法:
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
发现里面有调用:
if (!drawingWithRenderNode) { computeScroll(); sx = mScrollX; sy = mScrollY; }
说明, postInvalidate调用了draw再调用了computeScroll,然而computeScroll里又调用了scrollTo,移动的偏移量又是一点一点增大的值。
噢,我明白了,原来是通过不断的重绘来调用scrollTo让ViewGroup子View一点一点的移动,最终达到动态的效果,就像帧动画一样。
然而,什么时候结束移动呢?
看这个方法里:
public boolean computeScrollOffset() { if (mFinished) { return false; }
发现了:
if (mCurrX == mFinalX && mCurrY == mFinalY) { mFinished = true; }
让mCurrX ,Y一点一点增加,增加到终点坐标之后,mFinished等于true,return false,就不会再绘制,就不再scrollTo了,移动停止了。
@Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } }
Scroller的移动,如何实现,使用,分析一下原理,也就到这里了。总体感觉设计移动的增量使用时间戳的方式的确很精妙,可以借鉴学习,通过重绘与scrollTo的调用来完成动态效果,控制移动结束也很精妙,学习了。
要到两点了,就到这里吧。Demo下载:我的Github
2016年6月20日 1:53:48
- Scroller的使用,让View随心所欲的移动
- View的基础知识和Scroller的使用
- scroller的使用和view的位移
- Scroller类的使用(用来在ViewGroup里面让子view经常优美 的滑动)
- 使用Scroller实现View的弹性滑动
- 使用XIB, 如何让文字随心所欲的换行
- 使用XIB, 如何让文字随心所欲的换行
- 让input随心所欲的提示选择
- View的滚动与Scroller
- Scroller实现View的滑动
- scroller的使用
- Scroller的使用;
- Scroller的使用
- scroller的使用
- Scroller的使用
- Scroller的使用用法
- 在Android中动画移动一个View的位置,采用Scroller类实现
- View移动、Scroller、GestureDetector详解
- Java 序列化Serializable详解(附详细例子)
- 基本算法之一——直接插入排序
- 怎么用maven创建一个Java Web项目?
- PHP 浏览器禁用cookie,解决session变量不能传值
- TabHost
- Scroller的使用,让View随心所欲的移动
- HTML5多媒体audio和video(一)
- android UI TextView setText显示服务器返回数据
- RAM,ROM和Flash memory等存储器的比较
- 求连续子数组的最大和
- Wine虚拟技术及其使用
- JSON
- IOS-TextField详解
- dp 杂练/专练 round2