android ScrollView实现原理,求助
能解释下ScrollView滚动显示的原理吗?就比如说你的ScollView有LinearLayout然后LinearLayout内有100个Button(id=1,2,...
能解释下ScrollView滚动显示的原理吗?就比如说你的ScollView有LinearLayout然后LinearLayout内有100个Button(id=1,2,3,4......),创建初,屏幕显示id=1~10的Button,当你向下拖的时候如何重新绘制控件,显示id=3~12的Button,我看ScrollView的源代码,并没有找到这部分功能,不知道能否解释下到底是怎么实现的,在哪里实现的?多谢!!
展开
3个回答
展开全部
视图的滚动过程,其实是在不断修改原点坐标。当手指触摸后,ScrollView会暂时拦截触摸事件,使用一个计时器。
假如在计时器到点后没有发生手指移动事件,那么ScrollView发送tracking events到被点击的subView;若是在计时器到点后发生了移动事件,那么ScrollView取消tracking自己促发滚动。
其子类可以重载
touchesShouldBegin: withEvent: inContentView: 决定自己是否接收touch事件。
pagingEnabled: 当值是YES会自动滚动到subView的边界,默认是NO。
touchesShouldCancelInContentView: 开始发送tracking messages消息给subView的时候会调用这个方法。以决定是否发送tracking messages消息到subView。假如返回NO,发送。YES则不发送。若是canCancelContentTouches属性是NO,则不调用这个方法来影响如何处理滚动手势。
ScrollView还可处理缩放和平移手势,要实现这必须实现委托viewForZoomingInScrollView:和scrollViewDidEndZooming: withView: atScale:两个方法。另外maximumZoomScale和minimumZoomScale两个属性要不一样。
常用属性介绍
maximumZoomScale 能放大的最大倍数,是浮点数。
minimumZoomScale 能缩小的最小倍数,是浮点数。
pagingEnabled 是否自动滚动到subView边界
scrollEnabled 是否可以滚动
contentSize 里面内容的大小,即可以滚动的大小,默认是0,没有滚动效果
showsHorizontalScrollIndicator 滚动时是否显示水平滚动条
showsVerticalScrollIndicator 滚动时是否显示垂直滚动条
bounces 默认是YES,就是滚动超过边界会反弹,即有反弹回来的效果。若是NO,则滚动到达边界会立刻停止
bouncesZoom 与bounces类似,只是反映在缩放效果上。
directionalLockEnabled 默认是NO,可以在垂直和水平方向同时运动。当值是YES时,视哪个方向开始则锁定另外一个方向的滚动。
indicatorStyle 滚动条的样式。总共3色:默认、黑、白
scrollIndicatorInsets 设置滚动条位置
tracking 当touch后还没有拖动的时候是YES,否则NO
zoomBouncing 当内容放大到最大或者最小的时候值是YES,否则NO
zooming 当正在缩放的时候值是YES,否则NO
decelerating 当滚动后,手指放开但还在继续滚动中。此时是YES,其它时候都是NO
decelerationRate 设置手指放开后的减速率
基本使用方法:
初始化:一般的控件初始化都是可以用alloc和init来初始化的。
UIScrollView *sv = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0,0.0,self.view.frame.size.width, 400)];
关于控件添加与初始化,建议都采用代码调用合适的初始化方法来操作,虽然IB布局能够节省时间,但不能哪过很好了解整个代码执行流程。
委托方法:UIScrollView也要指定委托对象,该委托对象的控制器同样也要遵循UIScrollViewDelegate协议,实现其相应的代理方法。
scrollViewDidScroll:
scrollViewWillBeginDragging:
scrollViewDidEndDragging:
scrollViewDidEndDecelerating:
属性作用CGPoint contentOffSet监控目前滚动的位置CGSize contentSize滚动范围的大小UIEdgeInsets contentInset视图在scrollView中的位置id<UIScrollerViewDelegate>
delegate设置协议BOOL directionalLockEnabled指定控件是否只能在一个方向上滚动BOOL bounces控制控件遇到边框是否反弹BOOL alwaysBounceVertical控制垂直方向遇到边框是否反弹BOOL alwaysBounceHorizontal控制水平方向遇到边框是否反弹BOOL pagingEnabled控制控件是否整页翻动BOOL scrollEnabled控制控件是否能滚动BOOL showsHorizontalScrollIndicator控制是否显示水平方向的滚动条BOOL
showsVerticalScrollIndicator控制是否显示垂直方向的滚动条UIEdgeInsets scrollIndicatorInsets指定滚动条在scrollerView中的位置UIScrollViewIndicatorStyle
indicatorStyle设定滚动条的样式float decelerationRate改变scrollerView的减速点位置BOOL tracking监控当前目标是否正在被跟踪BOOL dragging监控当前目标是否正在被拖拽BOOL decelerating监控当前目标是否正在减速BOOL delaysContentTouches控制视图是否延时调用开始滚动的方法BOOL canCancelContentTouches控制控件是否接触取消touch的事件float minimumZoomScale缩小的最小比例float maximumZoomScale放大的最大比例float zoomScale设置变化比例BOOL bouncesZoom控制缩放的时候是否会反弹BOOL zooming判断控件的大小是否正在改变BOOL zoomBouncing判断是否正在进行缩放反弹BOOL scrollsToTop控制控件滚动到顶部
这里把UIScrollView的几个要点总结下:
从你的手指touch屏幕开始,scrollView开始一个timer,如果:
1. 150ms内如果你的手指没有任何动作,消息就会传给subView。
2. 150ms内手指有明显的滑动(一个swipe动作),scrollView就会滚动,消息不会传给subView,这里就是产生问题二的原因。
3. 150ms内手指没有滑动,scrollView将消息传给subView,但是之后手指开始滑动,scrollView传送touchesCancelled消息给subView,然后开始滚动。
观察下tableView的情况,你先按住一个cell,cell开始高亮,手不要放开,开始滑动,tableView开始滚动,高亮取消。
delaysContentTouches的作用:
这个标志默认是YES,使用上面的150ms的timer,如果设置为NO,touch事件立即传递给subView,不会有150ms的等待。
cancelsTouches的作用:
这个标准默认为YES,如果设置为NO,这消息一旦传递给subView,这scroll事件不会再发生。
假如在计时器到点后没有发生手指移动事件,那么ScrollView发送tracking events到被点击的subView;若是在计时器到点后发生了移动事件,那么ScrollView取消tracking自己促发滚动。
其子类可以重载
touchesShouldBegin: withEvent: inContentView: 决定自己是否接收touch事件。
pagingEnabled: 当值是YES会自动滚动到subView的边界,默认是NO。
touchesShouldCancelInContentView: 开始发送tracking messages消息给subView的时候会调用这个方法。以决定是否发送tracking messages消息到subView。假如返回NO,发送。YES则不发送。若是canCancelContentTouches属性是NO,则不调用这个方法来影响如何处理滚动手势。
ScrollView还可处理缩放和平移手势,要实现这必须实现委托viewForZoomingInScrollView:和scrollViewDidEndZooming: withView: atScale:两个方法。另外maximumZoomScale和minimumZoomScale两个属性要不一样。
常用属性介绍
maximumZoomScale 能放大的最大倍数,是浮点数。
minimumZoomScale 能缩小的最小倍数,是浮点数。
pagingEnabled 是否自动滚动到subView边界
scrollEnabled 是否可以滚动
contentSize 里面内容的大小,即可以滚动的大小,默认是0,没有滚动效果
showsHorizontalScrollIndicator 滚动时是否显示水平滚动条
showsVerticalScrollIndicator 滚动时是否显示垂直滚动条
bounces 默认是YES,就是滚动超过边界会反弹,即有反弹回来的效果。若是NO,则滚动到达边界会立刻停止
bouncesZoom 与bounces类似,只是反映在缩放效果上。
directionalLockEnabled 默认是NO,可以在垂直和水平方向同时运动。当值是YES时,视哪个方向开始则锁定另外一个方向的滚动。
indicatorStyle 滚动条的样式。总共3色:默认、黑、白
scrollIndicatorInsets 设置滚动条位置
tracking 当touch后还没有拖动的时候是YES,否则NO
zoomBouncing 当内容放大到最大或者最小的时候值是YES,否则NO
zooming 当正在缩放的时候值是YES,否则NO
decelerating 当滚动后,手指放开但还在继续滚动中。此时是YES,其它时候都是NO
decelerationRate 设置手指放开后的减速率
基本使用方法:
初始化:一般的控件初始化都是可以用alloc和init来初始化的。
UIScrollView *sv = [[UIScrollView alloc] initWithFrame:CGRectMake(0.0,0.0,self.view.frame.size.width, 400)];
关于控件添加与初始化,建议都采用代码调用合适的初始化方法来操作,虽然IB布局能够节省时间,但不能哪过很好了解整个代码执行流程。
委托方法:UIScrollView也要指定委托对象,该委托对象的控制器同样也要遵循UIScrollViewDelegate协议,实现其相应的代理方法。
scrollViewDidScroll:
scrollViewWillBeginDragging:
scrollViewDidEndDragging:
scrollViewDidEndDecelerating:
属性作用CGPoint contentOffSet监控目前滚动的位置CGSize contentSize滚动范围的大小UIEdgeInsets contentInset视图在scrollView中的位置id<UIScrollerViewDelegate>
delegate设置协议BOOL directionalLockEnabled指定控件是否只能在一个方向上滚动BOOL bounces控制控件遇到边框是否反弹BOOL alwaysBounceVertical控制垂直方向遇到边框是否反弹BOOL alwaysBounceHorizontal控制水平方向遇到边框是否反弹BOOL pagingEnabled控制控件是否整页翻动BOOL scrollEnabled控制控件是否能滚动BOOL showsHorizontalScrollIndicator控制是否显示水平方向的滚动条BOOL
showsVerticalScrollIndicator控制是否显示垂直方向的滚动条UIEdgeInsets scrollIndicatorInsets指定滚动条在scrollerView中的位置UIScrollViewIndicatorStyle
indicatorStyle设定滚动条的样式float decelerationRate改变scrollerView的减速点位置BOOL tracking监控当前目标是否正在被跟踪BOOL dragging监控当前目标是否正在被拖拽BOOL decelerating监控当前目标是否正在减速BOOL delaysContentTouches控制视图是否延时调用开始滚动的方法BOOL canCancelContentTouches控制控件是否接触取消touch的事件float minimumZoomScale缩小的最小比例float maximumZoomScale放大的最大比例float zoomScale设置变化比例BOOL bouncesZoom控制缩放的时候是否会反弹BOOL zooming判断控件的大小是否正在改变BOOL zoomBouncing判断是否正在进行缩放反弹BOOL scrollsToTop控制控件滚动到顶部
这里把UIScrollView的几个要点总结下:
从你的手指touch屏幕开始,scrollView开始一个timer,如果:
1. 150ms内如果你的手指没有任何动作,消息就会传给subView。
2. 150ms内手指有明显的滑动(一个swipe动作),scrollView就会滚动,消息不会传给subView,这里就是产生问题二的原因。
3. 150ms内手指没有滑动,scrollView将消息传给subView,但是之后手指开始滑动,scrollView传送touchesCancelled消息给subView,然后开始滚动。
观察下tableView的情况,你先按住一个cell,cell开始高亮,手不要放开,开始滑动,tableView开始滚动,高亮取消。
delaysContentTouches的作用:
这个标志默认是YES,使用上面的150ms的timer,如果设置为NO,touch事件立即传递给subView,不会有150ms的等待。
cancelsTouches的作用:
这个标准默认为YES,如果设置为NO,这消息一旦传递给subView,这scroll事件不会再发生。
展开全部
1. scrollview使用的时候里面都要放一个linearlayout,这是第一步。
2. 我们先不管linearlayout里面有什么,就假设有100个button,这时候linearlayout的显示长度肯定大于屏幕尺寸,这是第二步。
3. 这里就是重点了,scrollview通过实现手势监听器接口响应滑动事件,接收到滑动消息以后通过改变内部的linearlayout的margin参数来改变显示。重点就是改变其内部的linearlayout的layoutparams参数实现移动linearlayout。方法是:首先调用getLayoutParams获取linearlayout的布局参数,然后强转为MarginLayoutparams类型,然后改变它的marginTop即可实现上下滑动效果。
参考,如果感兴趣的话可以百度下滑动菜单,我是看过那个代码以后产生的灵感。
希望能够帮到你!
2. 我们先不管linearlayout里面有什么,就假设有100个button,这时候linearlayout的显示长度肯定大于屏幕尺寸,这是第二步。
3. 这里就是重点了,scrollview通过实现手势监听器接口响应滑动事件,接收到滑动消息以后通过改变内部的linearlayout的margin参数来改变显示。重点就是改变其内部的linearlayout的layoutparams参数实现移动linearlayout。方法是:首先调用getLayoutParams获取linearlayout的布局参数,然后强转为MarginLayoutparams类型,然后改变它的marginTop即可实现上下滑动效果。
参考,如果感兴趣的话可以百度下滑动菜单,我是看过那个代码以后产生的灵感。
希望能够帮到你!
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
展开全部
参考scrollView的 onTouchEvent 的。。
从 scrollBy--> invalidate();-----> p.invalidateChild(this, r);
最终通过调用 scrollView的 父View来重绘 scrollView的特定范围(r)实现的。
450 case MotionEvent.ACTION_DOWN:
451 /*
452 * If being flinged and user touches, stop the fling. isFinished
453 * will be false if being flinged.
454 */
455 if (!mScroller.isFinished()) {
456 mScroller.abortAnimation();
457 }
458
459 // Remember where the motion event started
460 // 记录开始的点击坐标
mLastMotionY = y;
461 break;
462 case MotionEvent.ACTION_MOVE:
463 // Scroll to follow the motion event
// 手指移动时的点击坐标
464 final int deltaY = (int) (mLastMotionY - y);
// 更新点击坐标
mLastMotionY = y;
466
// 往下滚动
467 if (deltaY < 0) {
468 if (mScrollY > 0) {
469 scrollBy(0, deltaY);
470 }
// 往上滚动
471 } else if (deltaY > 0) {
472 final int bottomEdge = getHeight() - mPaddingBottom;
473 final int availableToScroll = getChildAt(0).getBottom() - mScrollY - bottomEdge;
474 if (availableToScroll > 0) {
475 scrollBy(0, Math.min(availableToScroll, deltaY));
476 }
477 }
478 break;
追问
如果触摸点中有子控件的话,ScrollView是不会调用onTouchEvent()的,只调用了onIntercepEvent(),但是onIntercepEvent()并没有调用任何方法。。
追答
你参考一下 触摸消息派发
http://androidfanlf.blogspot.jp/2013/06/android-13-view.html.
另外 onInterceptTouchEvent 事件是确定是否打断当前view的 onTouchEvent。
所有的触摸事件,都是从顶层的view一层一层派发下来的。 所以,父控件肯定比子控件先接触到
onTouchEvent。
下面有android各个版本的源代码。
http://grepcode.com/project/repository.grepcode.com/java/ext/com.google.android/android/。
开源的东西,你可以设置断点一点一点进去看一下就明白了。
本回答被提问者采纳
已赞过
已踩过<
评论
收起
你对这个回答的评价是?
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询