前言
IOS拖拽回弹给用户的体验不得不赞。然后Android原生的API各种不支持,于是乎出现的很多仿IOS拖拽回弹的Android控件ReboundScrollView
,比起原生的ScrollView
,体验上好了很多,但是还是不尽人意:RebounScrollView
里的内容超出屏幕时,你想一次性在显示完ScrollView
里的内容紧跟着拽出屏幕,你发现你做不到,臣妾也做不到!于是乎,我想我是不是可以试试?下面分享一个让你也可以有IOS拖拽回弹般体验的ReboundFrameLayout。既然Android原生的ScrollView
做不到,那我干脆舍弃不用,我们自己做类似ScrollView
的滑动,这样方便我们在滑动到顶部(或底部)时可以继续向下(或向上滑动)。
这里链接两个大神写的拖拽回弹控件:
1、ReboundScrollView 仿IOS 拖拽回弹
2、 Android上实现仿IOS弹性ScrollView
我的思路

截屏效果

立即体验
扫描以下二维码下载体验App(从0.2.3
版本开始,体验App内嵌版本更新检测功能):

JSCKit库传送门:https://github.com/JustinRoom/JSCKit
源码简析
利用Scroller方式实现滚动。
ReboundFrameLayout.java关键逻辑代码(请参阅代码里的详细注释):
private void executeScroll(View target, int mYOffset) {
//为了方便下面的描述,我们用content代表target的内容,即:content=target的内容。
//content下滑的高度
int childScrollY = target.getScrollY();
//content在target中的可见区域
Rect rect = new Rect();
target.getLocalVisibleRect(rect);
//content的实际高度
int realHeight = target.getMeasuredHeight();
//content底部与target底部对齐时需要向上滑动的距离。
// 如果target底部已向上经拉出屏幕外,则认为distanceFromBottom为0
int distanceFromBottom = rect.bottom < 0 ? 0 : realHeight - rect.bottom;
int scrollY = getScrollY();
Log.i(TAG, "executeScroll: scrollY = " + scrollY);
int childScrolledY = Math.abs(childScrollY);
int scrolledY = Math.abs(scrollY);
int distance = Math.abs(mYOffset);
if (mYOffset < 0){//向下滑动
//向下滑动target与ReboundFrameLayout底部对齐
scrollBy(0, -Math.min(distance, scrolledY));
distance = distance - scrolledY;
if (distance <= 0){
return;
}
//向下滑动content与target顶部对齐
target.scrollBy(0, -Math.min(distance, childScrolledY));
distance = distance - childScrolledY;
if (distance <= 0){
return;
}
//向下滑动target
scrollBy(0, -distance);
} else if (mYOffset > 0) {//向上滑动
//向上滑动target与ReboundFrameLayout顶部对齐
scrollBy(0, Math.min(distance, scrolledY));
distance = distance - scrolledY;
if (distance <= 0){
return;
}
//向上滑动content与target底部对齐
target.scrollBy(0, Math.min(distance, distanceFromBottom));
distance = distance - distanceFromBottom;
if (distance <= 0){
return;
}
//向上滑动target
scrollBy(0, distance);
}
}
我们来看看下面几处重要的地方:
int realHeight = target.getMeasuredHeight();
- 关于获取
target
的内容高度问题。
1、如果target
是从layout
布局文件中解释出来的,我们可以通过target.measure(0, 0)
测量出来。
2、如果target
是new
出来的,通过target.measure(0, 0)
是测量不出来的。
为了考虑到上面两种情况,所以我们在protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
方法中预先测量target
。 - 在测量之前,我们先看看
ViewGroup
的解析流程,根据测试结果,流程图如下:
ViewGroup解析流程图.jpg
根据上图,不管是从layout
布局文件中解释出来的还是new
出来的,最终都会走测量方法onMeasure(int widthMeasureSpec, int heightMeasureSpec)
,那么接下来我们就在这里测量好target
就ok了 - 测量
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//先调用super方法固定本身的高度
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//测量target的实际内容高度
if (getChildCount() > 0) {
View target = getChildAt(0);
ViewGroup.LayoutParams params = target.getLayoutParams();
int newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(params.height, MeasureSpec.AT_MOST);
target.measure(widthMeasureSpec, newHeightMeasureSpec);
}
}
公共方法

结尾
不求打赏,点个赞加个关注吧!我是JustinEoy,QQ:1006368252
。
网友评论