转载请注明出处:http://blog.csdn.net/llew2011/article/details/52626148
之前写过一篇Android UI设计之<十>自定义ListView,实现QQ空间阻尼下拉刷新和渐变菜单栏效果的文章,写完那篇文章后想趁热打铁再写一篇用ScrollView来实现同样效果的文章,可是写了点开头就没有继续写下去了,当时想的是等用到再写吧,于是把它扔在了草稿箱中。近来恰好有用到,赶紧就把该文章补充完整发表出来,希望能给大家一点帮助......
使用ScrollView来实现QQ空间的阻尼下拉刷新和渐变菜单栏效果的原理和Android UI设计之<十>自定义ListView,实现QQ空间阻尼下拉刷新和渐变菜单栏效果的原理是一样的都是用到了Android 2.3版本后的overScrollBy()方法,如果你不熟悉该方法请阅读上篇文章,在上篇文章中我对该方法做了介绍或者是小伙伴们自行google。现在我们先看一下效果吧:
阅读到这里希望你已经看了我写的这篇同类文章,没看过也不要紧,我会带着小伙伴们一步一步的来实现我们想要的效果,首先我们看一下QQ空间的运行效果,当滚动到最顶部后,这时候如果我们手指继续下滑,则最顶部的View出现拉伸的效果,如果我们手指离开屏幕,则刚刚拉伸的View出现了阻尼回弹效果,当我们往上滚动时菜单栏就会随着滚动距离的增大其透明度逐渐增大直到完全不透明,反之逐渐透明。这样的体验感觉很棒有木有?说实话我是非常喜欢QQ的用户体验,平时也喜欢模仿QQ的各种特效,撤远了(*^__^*) ……
实现QQ空间运行效果前需要考虑两个问题:
- 如何实现菜单透明度渐变
通过观察QQ空间的运行效果可知其菜单栏默认为透明,随着滚动距离变化而变化,要想实现透明度的变化就要知道ScrollView的滚动距离,所以有关透明度的问题也就转化成了滚动距离的问题。
- 如何实现阻尼下拉和回弹效果
要想利用ScrollView实现阻尼效果就要求ScrollView首先滚动到了顶部,当ScrollView滚动到了顶部之后若继续手动下滑就要求其第一个Child变化来模拟下拉效果,当手指离开屏幕后该Child要恢复到初始状态。
我们先看第一个问题:要想实现透明度渐变就要先获取到ScrollView的滚动距离,通过滚动距离来计算出相应的透明度。在上篇文章中由于ListView的复用机制导致没法直接的获取到滚动距离,因此当时采用了addHeaderView()的方式,但是在ScrollVie中我们可以直接调用getScrollY()方法来获取滚动距离,因为该方法的返回值就是当前的滚动距离,有了滚动距离我们就可以计算出透明度了,所以在ScrollView中第一个问题那就不是事(*^__^*) ……
我们先实现菜单栏透明度渐变的功能。定义自己的ScrollView,取名为FlexibleScrollView,单词flexible是灵活的、多样的的意思,因为我们的ScrollView不仅要实现菜单栏的透明度渐变还要实现阻尼效果,所以取名为FlexibleScrollView比较恰当。FlexibleScrollView继承ScrollView后需要实现其构造方法,代码如下:
public class FlexibleScrollView extends ScrollView {
public FlexibleScrollView(Context context) {
super(context);
}
public FlexibleScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FlexibleScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
FlexibleScrollView仅仅是继承了ScrollView,这本质上和ScrollView没有区别。由于在ScrollView中可以直接通过getScrollY()方法获取到滚动距离,所以接下来就是判断ScrollView的滚动时机,在上篇文章中我们知道ListView发生滚动时总会调用onScrollChanged()方法,因此我们重写了onScrollChanged()方法来计算透明度,那我们在FlexibleScrollView中是否还能重写该方法呢?答案是OK的,熟悉ScrollView的滚动原理的童靴们应该清楚,ScrollView的滚动可分为两部分,一部分是手指触摸屏幕触发的滚动,另一部分是手指离开屏幕可能发生的滚动。
我们先看手指触摸屏幕触发的滚动时机,其源码如下:
@Override
public boolean onTouchEvent(MotionEvent ev) {
......
switch (action & MotionEvent.ACTION_MASK) {
......
case MotionEvent.ACTION_MOVE:
if (mIsBeingDragged) {
......
/** 在这里调用了onScrollChanged()方法 **/
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
......
}
break;
......
}
return true;
}
根据源码我们知道在ScrollView的onTouchEvent()方法中当发生了ACTION_MOVE事件后总会调用onScrollChanged()方法,所以重写该方法似乎是可行的。
接着我们再看一下当手指离开屏幕后发生的情况,源码如下:
@Override
public boolean onTouchEvent(MotionEvent ev) {
......
switch (action & MotionEvent.ACTION_MASK) {
......
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
if (getChildCount() > 0) {
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
fling(-initialVelocity);
} else {
if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0,
getScrollRange