-
Notifications
You must be signed in to change notification settings - Fork 2.7k
QMUIContinuousNestedScrollLayout
QMUI版本要求: v1.3.2+
QMUIContinuousNestedScrollLayout
,用于连接两个上下滚动的容器,使他们能够连续滚动。例如一个 WebView
用于显示网页内容, 一个 RecyclerView
用于显示评论。 在 QMUI 里, 把上面的滚动容器叫 TopView
, 把下面的滚动容器叫 BottomView
。
// 1. 提供 TopView
IQMUIContinuousNestedTopView topView = ...
// 2. 提供 BottomView
IQMUIContinuousNestedBottomView bottomView = ...
QMUIContinuousNestedScrollLayout scrollLayout = new QMUIContinuousNestedScrollLayout(context)
// 3. 将 TopView 赋值给 QMUIContinuousNestedScrollLayout
CoordinatorLayout.LayoutParams topLp = new CoordinatorLayout.LayoutParams(matchParent, matchParent);
topLp.setBehavior(new QMUIContinuousNestedTopAreaBehavior(context)); // not necessary
scrollLayout.setTopAreaView(topView, topLp);
// 4. 将 BottomView 赋值给 QMUIContinuousNestedScrollLayout
CoordinatorLayout.LayoutParams recyclerViewLp = new CoordinatorLayout.LayoutParams( matchParent, matchParent);
recyclerViewLp.setBehavior(new QMUIContinuousNestedBottomAreaBehavior()); // not necessary
scrollLayout.setBottomAreaView(bottomView, recyclerViewLp);
QMUI 默认实现了常用的三种 TopView
:
- QMUIContinuousNestedTopLinearLayout
- QMUIContinuousNestedTopRecyclerView
- QMUIContinuousNestedTopWebView
以及可以包装 Header 与 Footer 的容器类: QMUIContinuousNestedTopDelegateLayout
。
如果上述 TopView
不能满足需求,可以通过实现 IQMUIContinuousNestedTopView
实现自定义的 TopView
。
public interface IQMUIContinuousNestedTopView extends IQMUIContinuousNestedScrollCommon {
// 消耗滚动,传入参数为可消耗量,返回值为未被消耗的值
int consumeScroll(int dyUnconsumed);
// 当前滚动位置
int getCurrentScroll();
// 可滚动范围
int getScrollOffsetRange();
}
public interface IQMUIContinuousNestedScrollCommon {
// 滚动状态, 与 RecyclerView 相同
int SCROLL_STATE_IDLE = RecyclerView.SCROLL_STATE_IDLE;
int SCROLL_STATE_DRAGGING = RecyclerView.SCROLL_STATE_DRAGGING;
int SCROLL_STATE_SETTLING = RecyclerView.SCROLL_STATE_SETTLING;
// 保存当前滚动信息
void saveScrollInfo(@NonNull Bundle bundle);
// 恢复滚动信息
void restoreScrollInfo(@NonNull Bundle bundle);
// 注入滚动监听器
void injectScrollNotifier(OnScrollNotifier notifier);
interface OnScrollNotifier {
void notify(int innerOffset, int innerRange);
void onScrollStateChange(View view, int newScrollState);
}
}
QMUI 只提供了 QMUIContinuousNestedBottomRecyclerView
以及可以包装 Header 的容器类: QMUIContinuousNestedBottomDelegateLayout
。
如果上述 BottomView
不能满足需求,可以通过实现 IQMUIContinuousNestedBottomView
实现自定义的 BottomView
:
public interface IQMUIContinuousNestedBottomView extends IQMUIContinuousNestedScrollCommon {
int HEIGHT_IS_ENOUGH_TO_SCROLL = -1;
// 消耗滚动,传入参数为可消耗量,没有返回值,意味着 BottomView 必须处理所有的滚动
// IQMUIContinuousNestedBottomView 需要是 NestedScrollingChild,才能方便的处理所有的滚动
void consumeScroll(int dyUnconsumed);
// 在 duration 的时间内滚动 dy
void smoothScrollYBy(int dy, int duration);
// 取消上面的smoothScroll
void stopScroll();
// 内容高度,如果 BottomView 内容高度足够滚动,则返回 HEIGHT_IS_ENOUGH_TO_SCROLL
int getContentHeight();
// 当前滚动位置
int getCurrentScroll();
// 滚动范围
int getScrollOffsetRange();
}
要使得 QMUIContinuousNestedScrollLayout 可以正常工作,BottomView
最好实现 NestedScroll 机制。
QMUIContinuousNestedScrollLayout
本质是 CoordinatorLayout
, 因此 TopView
和 BottomView
的 LayoutParam
需要是 CoordinatorLayout.LayoutParam
。
QMUIContinuousNestedScrollLayout
重定义了 TopView
的 LayoutParam
的 height 值的含义:
- 如果 height == MATCH_PARENT, 则其高度最高为父容器高度。
QMUIContinuousNestedTopRecyclerView
、QMUIContinuousNestedTopWebView
、QMUIContinuousNestedTopDelegateLayout
应该设置为它,当内容高度超出容器高度时,可以进行内部滚动。 - 如果 height != MATCH_PARENT, 则其高度等于它内容的高度。
QMUIContinuousNestedTopLinearLayout
应当设置为它(wrap_content),因为 LinearLayout 内部无法滚动,只能靠外部去驱动它滚动。
QMUIContinuousNestedScrollLayout
支持以下scroll 行为:
方法 | 描述 |
---|---|
scrollBy(int dy) | 滚动 dy 的距离,正值向上滚,负值向下滚动 |
smoothScrollBy(int dy, int duration) | 在给定时间滚动dy的距离 |
scrollToTop() | 滚动到顶部 |
scrollToBottom() | 滚动到底部 |
scrollBottomViewToTop() | 将 BottomView 滚动到顶部 |
可以通过 QMUIContinuousNestedScrollLayout.addOnScrollListener
添加监听器, 监听器需要实现 OnScrollListener
接口:
public interface OnScrollListener {
// topCurrent、topRange 是指 TopView 的内部滚动值与滚动范围
// bottomCurrent、bottomRange 是指 BottomView 的内部滚动值与滚动范围
// offsetCurrent、offsetRange 是指 TopView 与 BottomView 自身的滚动偏移值和整体可偏移值
void onScroll(int topCurrent, int topRange,
int offsetCurrent, int offsetRange,
int bottomCurrent, int bottomRange);
// 滚动状态:
// fromTopBehavior 标识有 TopBehavior 触发的滚动状态改变,否则为 BottomView 内部触发滚动状态变化。
void onScrollStateChange(int newScrollState, boolean fromTopBehavior);
}
对于很多用户友好型的页面,最好能够在再次进入同一个页面时,能够恢复到上次阅读位置。 因此 QMUIContinuousNestedScrollLayout
提供了对当前位置信息的恢复与还原机制。
方法 | 描述 |
---|---|
saveScrollInfo(Bundle bundle) | 将当前滚动位置存储到 Bundle 里 |
restoreScrollInfo(Bundle bundle) | 从 Bundle 里恢复滚动位置 |
如果是自定义的 TopView / BottomView
,可以通过重写 saveScrollInfo(@NonNull Bundle bundle)
和 void restoreScrollInfo(@NonNull Bundle bundle)
来做完成滚动位置的恢复工作。