Android 仿 IOS 拖拽回弹之进阶 ReboundFrameLayout
前言
IOS 拖拽回弹给用户的体验不得不赞然后 Android 原生的 API 各种不支持, 于是乎出现的很多仿 IOS 拖拽回弹的 Android 控件 ReboundScrollView, 比起原生的 ScrollView, 体验上好了很多, 但是还是不尽人意: RebounScrollView 里的内容超出屏幕时, 你想一次性在显示完 ScrollView 里的内容紧跟着拽出屏幕, 你发现你做不到, 臣妾也做不到! 于是乎, 我想我是不是可以试试? 下面分享一个让你也可以有 IOS 拖拽回弹般体验的 ReboundFrameLayout 既然 Android 原生的 ScrollView 做不到, 那我干脆舍弃不用, 我们自己做类似 ScrollView 的滑动, 这样方便我们在滑动到顶部 (或底部) 时可以继续向下(或向上滑动)
这里链接两个大神写的拖拽回弹控件:
1ReboundScrollView 仿 IOS 拖拽回弹
2 Android 上实现仿 IOS 弹性 ScrollView
我的思路
思路图
效果展示
ReboundFramLayout
实现
利用 Scroller 方式实现滚动
代码实现比较简单, 且有详细的注释ReboundFrameLayout.java
packagejsc.kit.org.rebound;
importandroid.content.Context;
importandroid.graphics.Rect;
importandroid.util.AttributeSet;
importandroid.view.MotionEvent;
importandroid.view.View;
importandroid.view.ViewConfiguration;
importandroid.widget.FrameLayout;
importandroid.widget.Scroller;
/**
* 布局上下反弹
*
* @author jsc
*/
publicclassReboundFrameLayoutextendsFrameLayout{
privatefinalStringTAG=getClass().getSimpleName();
privatefinalfloatRATIO=0.65f;
privatefinalintANIM_TIME=300;
privateViewmChildView;
privatefloatmCurY;
privatefloatmLastY;
privateScrollermScroller;
privateintmTouchSlop;
publicReboundFrameLayout(Contextcontext,AttributeSetattrs){
super(context,attrs);
mScroller=newScroller(context);
mTouchSlop=ViewConfiguration.get(context).getScaledPagingTouchSlop();
}
@Override
protectedvoidonFinishInflate(){
super.onFinishInflate();
mChildView=getChildAt(0);
}
@Override
publicbooleanonInterceptTouchEvent(MotionEventev){
switch(ev.getAction()){
caseMotionEvent.ACTION_DOWN:
mCurY=ev.getY();
mLastY=mCurY;
caseMotionEvent.ACTION_MOVE:
floatmCurY=ev.getY();
intmark=(int)(mCurY-mLastY);
intslop=Math.abs(mark);
mLastY=mCurY;
// 当滑动的距离小于 10px 的时候, 我们认为这次滑动是无效的, 销毁这次事件
if(slop>=10){
returntrue;
}
break;
caseMotionEvent.ACTION_UP:
break;
}
returnsuper.onInterceptTouchEvent(ev);
}
@Override
publicbooleanonTouchEvent(MotionEventev){
switch(ev.getAction()){
caseMotionEvent.ACTION_DOWN:
floatmCurY=ev.getY();
mLastY=mCurY;
caseMotionEvent.ACTION_MOVE:
mCurY=ev.getY();
intmYOffset=(int)((mCurY-mLastY)*RATIO);
scroll(mChildView,mYOffset);
mLastY=mCurY;
returntrue;
caseMotionEvent.ACTION_UP:
if(!mScroller.isFinished()){
mScroller.forceFinished(true);
}
mScroller.startScroll(0,getScrollY(),0,-getScrollY(),ANIM_TIME);
invalidate();
default:
break;
}
returntrue;
}
@Override
publicvoidcomputeScroll(){
if(mScroller.computeScrollOffset()){
scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
postInvalidate();
}
}
privatevoidscroll(ViewmChildView,intmYOffset){
//child 内容下滑高度
intchildScrolledY=mChildView.getScrollY();
//child 在其 parent 中的可见部分
Rectrect=newRect();
mChildView.getLocalVisibleRect(rect);
//child 实际内容高度
mChildView.measure(0,0);
intrealHeight=mChildView.getMeasuredHeight();
//child 内容滑动到其 parent 底部时需要向上滑动的距离
intdistanceFromBottom=realHeight-rect.bottom;
if(mYOffset>0){// 向下滑动
// 如果向下滑动的距离小于 child 内容上滑到其 parent 顶部的距离, 则向下滑动 child 内容 mYOffset 距离
if(mYOffset
mChildView.scrollBy(0,-mYOffset);
}else{// 如果滑动的距离大于或等于 child 内容已经下滑的高度, 则向下滑动 child 的内容至其 parent 的顶部, 再向下滑动超出部分的距离
mChildView.scrollBy(0,-childScrolledY);
scrollBy(0,-(mYOffset-childScrolledY));
}
}elseif(mYOffset<0){// 向上滑动
intdistance=Math.abs(mYOffset);
// 如果向上滑动的距离小于 child 内容下滑到其 parent 底部的距离, 则向上滑动 child 内容 distance 距离
if(distance
mChildView.scrollBy(0,distance);
}else{
mChildView.scrollBy(0,distanceFromBottom);
scrollBy(0,distance-distanceFromBottom);
}
}
}
}
关键在于方法
scroll(View mChildView, int mYOffset)
里面的处理注释很详细, 请自行参阅
最后附上源码的下载地址: https://github.com/JustinRoom/JscKit/tree/master/rebound
来源: http://www.jianshu.com/p/53d13719a6c4