项目中有用到一个类似于日期时间的滑动选择控件,然而感觉 android 自带的 DataPicker 与 app 的整体风格不太一样,于是想着自己写一个超级简洁美丽大方的控件出来。
本来想到滑动选择,就想到了 scrollview,然后在网上搜了一下,谁想到网上资料全都是一样的。。一点营养都没有,虽然最后由于 API 版本的原因没有采用 scrollview 的方式,但是中间对其研究了一下,特此分享。
ScrollView基本用法介绍
ScrollView的主要用处是,当你在一个界面上显示的内容超过了display大小的时候,用scrollview滑动显示,比如大众点评一些app的主页面。这里还要提一下ScrollView里用到了很多次Nest,这是为什么?
因为Nest是嵌套的意思,个人认为ScrollView的设计初衷是嵌套在整个Display中,如果你创建一个带ActionBar的Activity,就会发现,向下滑动ScrollView,ActionBar会自动消失,向上滑动则会出现。用法:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_gravity="center" android:layout_width="100dp" android:layout_height="80dp" tools:context=".MainActivity"> <LinearLayout android:id="@+id/scrollview" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:id="@+id/text" android:text="2010" android:textSize="30dp" android:layout_width="fill_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/text23" android:text="2010" android:textSize="30dp" android:layout_width="fill_parent" android:layout_height="wrap_content" /> </LinearLayout> </ScrollView>
这里要注意,ScrollView只能有一个childview,一般用一个LinearLayout来实现,在LinearLayout中添加其他控件。
ScrollView初始化
ScrollView继承FrameLayout,所以等级和LinearLayout这些是一样的,初始化也大同小异,无非就是传递一些attr和style的值,这里有几个变量介绍一下,在initScrollView()中
mTouchSlop = configuration.getScaledTouchSlop(); mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); mOverscrollDistance = configuration.getScaledOverscrollDistance(); mOverflingDistance = configuration.getScaledOverflingDistance();
Velocity是速率的意思,fling是掷的意思,我理解为手指在屏幕上的快速滑动
Touch行为
和其他View的Touch一样,先传给onTouchEvent之前,传递给onInterceptTouchEvent函数,首先*ACTION.DOWN*行为,
if (actionMasked == MotionEvent.ACTION_DOWN) { mNestedYOffset = 0; } vtev.offsetLocation(0, mNestedYOffset);
记录mNestedYOffset,Y方向上的offset
case MotionEvent.ACTION_DOWN: { if (getChildCount() == 0) { return false; } if ((mIsBeingDragged = !mScroller.isFinished())) { final ViewParent parent = getParent(); if (parent != null) { parent.requestDisallowInterceptTouchEvent(true); } }/* * If being flinged and user touches, stop the fling. isFinished * will be false if being flinged. */ if (!mScroller.isFinished()) { mScroller.abortAnimation(); if (mFlingStrictSpan != null) { mFlingStrictSpan.finish(); mFlingStrictSpan = null; } } // Remember where the motion event started mLastMotionY = (int) ev.getY(); mActivePointerId = ev.getPointerId(0); startNestedScroll(SCROLL_AXIS_VERTICAL); break;</pre>
这里会检测到ScrollView是否正在被拉动,如果没有被拉动,就停止动画
同时得到mLastMotionY,就是手指按下的位置
还有mActivePointerId,这里的Pointer可以当做手指产生的trace代号,如果有multi-touch,就会有多个pointer,在这里不是重点,不细说。
接下来是*ACTION_MOVE*
final int y = (int) ev.getY(activePointerIndex); int deltaY = mLastMotionY - y; if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) { deltaY -= mScrollConsumed[1]; vtev.offsetLocation(0, mScrollOffset[1]); mNestedYOffset += mScrollOffset[1]; }deltaY大家都能看懂,重点是dispatchNestedPreScroll(0,deltaY,mScrollConsumed,mScrollOffset)这个函数,先说一下后两个参数的意义,mScrollConsumed就是Parent消耗的滑动的部分(其实我不太明白为什么会有这个参数,是为了让滑动不要太敏感吗?但是我们可以从这个参数知道,我们最终ScrollView滑动的距离是由parent消耗的一部分和真正的滑动的一部分所组成的),第4个参数就是scrollview已经产生的offset了。这个函数名字叫做PreScroll,我们也可以知道这是预滑动,也就是childview还没有开始滑动前做的动作。
在这个函数里做了什么呢?
其实调用了mNestedScrollingParent.onNestedPreScroll,也就是parent的prescroll,然后产生一个consumed和offset传递回来。
返回之后,我们可以看到deltaY-mScrollConsumed[1]得到的就是真正的childview所需要滑动的deltaY了,然后记录下来。
其实后面的代码也和上面类似,就不做多解释了。看完这个,你应该对ScrollView有了一个大致的了解,之后运用起来相信会更加的娴熟。
千万要记住一点,不要在ScrollView中嵌套上已经具有Scroll功能的控件里,比如ListView,比如装载不下的TextView。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于