仿写第三篇之仿写scrollview
2015-06-01 19:49
302 查看
前言
读完自定义viewgroup实践之仿写LinearLayout和自定义虚线两篇文章,相信你对自定义view和自定义viewgroup有一定的了解了。今天带大家实现垂直滚动的viewgroup。对scrollview滚动原理的思考:首先scrollview有子view,所以其必是一个viewgroup。而其可上下滚动,这就说明肯定是在onTouchEvent方法中调了scrollTo或scrollBy方法。
好了,知道了其骨架。接下来看源码进一步分析:
1.看继承关系:
scrollview继承自framelayout,既然是viewgroup子孙,那么重要的也就是onmeasure、onlayou和LayoutParam了。看scrollview的addview()方法:
@Override public void addView(View child, ViewGroup.LayoutParams params) { if (getChildCount() > 0) { throw new IllegalStateException("ScrollView can host only one direct child"); } super.addView(child, params); }它没写自己的LayoutParam,用的是viewgroup的LayoutParam。所以我写的也就没写LayoutParam。
2.onMeasure()
其首先调用父类的onmeasure方法。由于是framelayout,其measure子view时调用了measureChildWithMargins方法,而scrollview重写了该方法。看重写代码:
@Override protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( lp.topMargin + lp.bottomMargin, MeasureSpec.UNSPECIFIED); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
其给子view的childHeightMeasureSpec 是以 MeasureSpec.UNSPECIFIED生成的。所以子view想要多高就可以多高。说个题外话,平常我们测量一个view的宽高时总是这样调用view.measure(0,0),而MeasureSpec.UNSPECIFIED的值就是0,也就是测量view有多大就多大,调用后就可以getMeasuredWidth和getMeasuredHeight获取宽高。
3.onlayout方法
由于其只有一个子view,也就是linearlayout,所以super.onLayout(changed, l, t, r, b),这里没啥可说的。
接下来是核心部分:
声明:mIsBeingDragged,true为开始拖动onInterceptTouchEvent ------- true为截取事件,该方法返回的是mIsBeingDragged,
我们依据事件的整个过程来说明,首先触发ACTION_DOWN事件,如果还在滚动,则mIsBeingDragged为true。在ACTION_MOVE中,如果y方向的偏移大于了mTouchSlop(滚动之前可以滑动的距离),则mIsBeingDragged为true。在滚动中了
if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) { return true; }最后ACTION_UP,mIsBeingDragged为false。
onTouchEvent------ 具体的滚动逻辑在其中
mScroller也就是OverScroller,不熟悉的可百度下,它就是滚动辅助类。ACTION_DOWN:在滚动中手指down,则停止滚动。保存mLastMotionY(最后触摸点的值)。
ACTION_MOVE:关键在overScrollBy()方法。这个方法中又调用了onOverScrolled()方法,而scrollview重写了该方法,直接看代码:
@Override protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { // Treat animating scrolls differently; see #computeScroll() for why. if (!mScroller.isFinished()) { final int oldX = mScrollX; final int oldY = mScrollY; mScrollX = scrollX; mScrollY = scrollY; invalidateParentIfNeeded(); onScrollChanged(mScrollX, mScrollY, oldX, oldY); if (clampedY) { mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange()); } } else { super.scrollTo(scrollX, scrollY); } awakenScrollBars(); }也就是其调用了scrollTo方法。
ACTION_UP:计算velocityTracker,然后判断其是否可以fling。
flying --- 其中使用了scroller
public void fling(int velocityY) { if (getChildCount() > 0) { int height = getHeight() - mPaddingBottom - mPaddingTop; int bottom = getChildAt(0).getHeight(); mScroller.fling(mScrollX, mScrollY, 0, velocityY, 0, 0, 0, Math.max(0, bottom - height), 0, height/2); if (mFlingStrictSpan == null) { mFlingStrictSpan = StrictMode.enterCriticalSpan("ScrollView-fling"); } postInvalidateOnAnimation(); } }而view重绘时会调用computeScroll()方法。computeScroll一般与scroller一起使用,不熟悉的百度。
最后附上源码,也就是简单版的scrollview
源码
相关文章推荐
- java中访问控制符
- [oc学习日记]字典
- 第十六篇 休闲的星期一
- 内存地址形式转换成可读的Crash日志文件教程
- Enable Geolocation in a WebView (Android)
- sgu-260 Puzzle
- Android:长按连续响应的Button
- js类型检测
- 指针与引用
- centos 7 virtualbox guest additions installation centos minimal
- 读写xml文件
- MCV分页实现
- 第五十九天 how can I 坚持 --------补昨天5月31号
- CSAPP 深入理解计算机系统 Buflab实验,缓冲区溢出攻击实验(3)
- VirtualBox Guest Additions on Fedora 21/20, CentOS/RHEL 7.1/6.6/5.11
- 会员卡管理系统技术解析(七)会员卡挂失记录查询
- POJ 2002 几何+hash
- Android ListView嵌套
- 关于操作系统
- sgu245:Black-White Army(技巧暴力)