Skip to content

Commit 385fe45

Browse files
committed
add layout QMUIContinuousNestedScrollLayout to support more fetures
1 parent 63fd472 commit 385fe45

14 files changed

+507
-106
lines changed

qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/IQMUIContinuousNestedBottomView.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,18 @@
1717
package com.qmuiteam.qmui.nestedScroll;
1818

1919
public interface IQMUIContinuousNestedBottomView {
20+
int HEIGHT_IS_ENOUGH_TO_SCROLL = -1;
2021
/**
2122
* consume scroll
2223
* @param dyUnconsumed the delta value to consume
2324
*/
2425
void consumeScroll(int dyUnconsumed);
26+
27+
/**
28+
* sometimes the content of BottomView is not enough to scroll,
29+
* so BottomView should tell the this info to {@link QMUIContinuousNestedScrollLayout}
30+
* @return {@link #HEIGHT_IS_ENOUGH_TO_SCROLL} if can scroll, or content height.
31+
*/
32+
int getContentHeight();
33+
2534
}

qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/IQMUIContinuousNestedTopView.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,19 @@
1919
public interface IQMUIContinuousNestedTopView {
2020
/**
2121
* consume scroll
22+
*
2223
* @param dyUnconsumed the delta value to consume
2324
* @return the remain unconsumed value
2425
*/
2526
int consumeScroll(int dyUnconsumed);
27+
28+
int getCurrentScroll();
29+
30+
int getScrollRange();
31+
32+
void injectScrollNotifier(OnScrollNotifier notifier);
33+
34+
interface OnScrollNotifier {
35+
void notify(int innerOffset, int innerRange);
36+
}
2637
}

qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/QMUIContinuousNestedBottomDelegateLayout.java

Lines changed: 73 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ public abstract class QMUIContinuousNestedBottomDelegateLayout extends QMUIFrame
4848
private Rect mTempRect = new Rect();
4949
private int mNestedOffsetY = 0;
5050

51-
5251
public QMUIContinuousNestedBottomDelegateLayout(Context context) {
5352
this(context, null);
5453
}
@@ -66,40 +65,78 @@ public QMUIContinuousNestedBottomDelegateLayout(Context context, AttributeSet at
6665
ViewCompat.setNestedScrollingEnabled(this, true);
6766
mHeaderView = onCreateHeaderView();
6867
mContentView = onCreateContentView();
69-
addView(mHeaderView, getHeaderLayoutParam());
70-
addView(mContentView, getContentLayoutParam());
68+
if (!(mContentView instanceof IQMUIContinuousNestedBottomView)) {
69+
throw new IllegalStateException("the view create by onCreateContentView() " +
70+
"should implement from IQMUIContinuousNestedBottomView");
71+
}
72+
addView(mHeaderView, new FrameLayout.LayoutParams(
73+
ViewGroup.LayoutParams.MATCH_PARENT, getHeaderHeightLayoutParam()));
74+
addView(mContentView, new FrameLayout.LayoutParams(
75+
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
7176
mHeaderViewOffsetHelper = new QMUIViewOffsetHelper(mHeaderView);
7277
mContentViewOffsetHelper = new QMUIViewOffsetHelper(mContentView);
7378
mViewFlinger = new ViewFlinger();
7479
}
7580

81+
public View getHeaderView() {
82+
return mHeaderView;
83+
}
84+
85+
public View getContentView() {
86+
return mContentView;
87+
}
88+
89+
@Override
90+
public int getContentHeight() {
91+
IQMUIContinuousNestedBottomView b = (IQMUIContinuousNestedBottomView) mContentView;
92+
int bc = b.getContentHeight();
93+
if (bc == IQMUIContinuousNestedBottomView.HEIGHT_IS_ENOUGH_TO_SCROLL || bc > mContentView.getHeight()) {
94+
return IQMUIContinuousNestedBottomView.HEIGHT_IS_ENOUGH_TO_SCROLL;
95+
}
96+
int bottomMargin = getContentBottomMargin();
97+
if (bc + mHeaderView.getHeight() + bottomMargin > getHeight()) {
98+
return IQMUIContinuousNestedBottomView.HEIGHT_IS_ENOUGH_TO_SCROLL;
99+
}
100+
return mHeaderView.getHeight() + bc + bottomMargin;
101+
}
102+
76103
@NonNull
77104
protected abstract View onCreateHeaderView();
78105

79106
@NonNull
80107
protected abstract View onCreateContentView();
81108

82-
protected FrameLayout.LayoutParams getHeaderLayoutParam() {
83-
return new FrameLayout.LayoutParams(
84-
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
109+
protected int getHeaderStickyHeight() {
110+
return 0;
111+
}
112+
113+
114+
protected int getHeaderHeightLayoutParam() {
115+
return ViewGroup.LayoutParams.WRAP_CONTENT;
85116
}
86117

87-
protected FrameLayout.LayoutParams getContentLayoutParam() {
88-
return new FrameLayout.LayoutParams(
89-
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
118+
protected int getContentBottomMargin() {
119+
return 0;
120+
}
121+
122+
@Override
123+
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
124+
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
125+
126+
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
127+
mContentView.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(
128+
heightSize - getHeaderStickyHeight() - getContentBottomMargin(),
129+
MeasureSpec.EXACTLY));
90130
}
91131

92132
@Override
93133
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
94-
FrameLayout.LayoutParams headerLp = (LayoutParams) mHeaderView.getLayoutParams();
95-
mHeaderView.layout(headerLp.leftMargin, headerLp.topMargin,
96-
headerLp.leftMargin + mHeaderView.getMeasuredWidth(),
97-
headerLp.topMargin + mHeaderView.getMeasuredHeight());
98-
99-
FrameLayout.LayoutParams contentLp = (LayoutParams) mContentView.getLayoutParams();
100-
int contentTop = mHeaderView.getBottom() + headerLp.bottomMargin + contentLp.topMargin;
101-
mContentView.layout(contentLp.leftMargin, contentTop,
102-
contentLp.leftMargin + mContentView.getMeasuredWidth(),
134+
mHeaderView.layout(0, 0, mHeaderView.getMeasuredWidth(),
135+
mHeaderView.getMeasuredHeight());
136+
137+
138+
int contentTop = mHeaderView.getBottom();
139+
mContentView.layout(0, contentTop, mContentView.getMeasuredWidth(),
103140
contentTop + mContentView.getMeasuredHeight());
104141

105142
mHeaderViewOffsetHelper.onViewLayout();
@@ -108,22 +145,18 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto
108145

109146
private int offsetBy(int dyUnConsumed) {
110147
int canConsume = 0;
148+
IQMUIContinuousNestedBottomView b = (IQMUIContinuousNestedBottomView) mContentView;
149+
int contentHeight = b.getContentHeight();
150+
FrameLayout.LayoutParams headerLp = (LayoutParams) mHeaderView.getLayoutParams();
151+
int minOffset = -mHeaderView.getHeight() - headerLp.bottomMargin + getHeaderStickyHeight();
152+
if (contentHeight != IQMUIContinuousNestedBottomView.HEIGHT_IS_ENOUGH_TO_SCROLL) {
153+
minOffset += mContentView.getHeight() - contentHeight;
154+
minOffset = Math.min(minOffset, 0);
155+
}
111156
if (dyUnConsumed > 0) {
112-
FrameLayout.LayoutParams contentLp = (LayoutParams) mContentView.getLayoutParams();
113-
int maxOffset = mContentView.getBottom() + contentLp.bottomMargin - getHeight();
114-
if (maxOffset >= dyUnConsumed) {
115-
canConsume = dyUnConsumed;
116-
} else {
117-
canConsume = maxOffset;
118-
}
157+
canConsume = Math.min(mHeaderView.getTop() - minOffset, dyUnConsumed);
119158
} else if (dyUnConsumed < 0) {
120-
FrameLayout.LayoutParams headerLp = (LayoutParams) mHeaderView.getLayoutParams();
121-
int minOffset = mHeaderView.getTop() - headerLp.topMargin;
122-
if (minOffset <= dyUnConsumed) {
123-
canConsume = dyUnConsumed;
124-
} else {
125-
canConsume = minOffset;
126-
}
159+
canConsume = Math.max(mHeaderView.getTop() - headerLp.topMargin, dyUnConsumed);
127160
}
128161
if (canConsume != 0) {
129162
mHeaderViewOffsetHelper.setTopAndBottomOffset(mHeaderViewOffsetHelper.getTopAndBottomOffset() - canConsume);
@@ -134,17 +167,13 @@ private int offsetBy(int dyUnConsumed) {
134167

135168
@Override
136169
public void consumeScroll(int dy) {
137-
if (mContentView instanceof IQMUIContinuousNestedBottomView) {
138-
((IQMUIContinuousNestedBottomView) mContentView).consumeScroll(dy);
139-
} else {
140-
mScrollConsumed[0] = 0;
141-
mScrollConsumed[1] = 0;
142-
dispatchNestedPreScroll(0, dy, mScrollConsumed, null, ViewCompat.TYPE_NON_TOUCH);
143-
int unConsumed = dy - mScrollConsumed[1];
144-
int remain = offsetBy(unConsumed);
145-
dispatchNestedScroll(0, unConsumed - remain, 0,
146-
remain, null, ViewCompat.TYPE_NON_TOUCH);
147-
}
170+
mScrollConsumed[0] = 0;
171+
mScrollConsumed[1] = 0;
172+
dispatchNestedPreScroll(0, dy, mScrollConsumed, null, ViewCompat.TYPE_NON_TOUCH);
173+
int unConsumed = dy - mScrollConsumed[1];
174+
int remain = offsetBy(unConsumed);
175+
dispatchNestedScroll(0, unConsumed - remain, 0,
176+
remain, null, ViewCompat.TYPE_NON_TOUCH);
148177
}
149178

150179
// NestedScrollingChild2
@@ -393,7 +422,7 @@ public boolean onTouchEvent(MotionEvent ev) {
393422
touchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
394423
}
395424

396-
if(ev.getAction() == MotionEvent.ACTION_DOWN){
425+
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
397426
mNestedOffsetY = 0;
398427
}
399428

qmui/src/main/java/com/qmuiteam/qmui/nestedScroll/QMUIContinuousNestedBottomRecyclerView.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,22 @@ public QMUIContinuousNestedBottomRecyclerView(@NonNull Context context, @Nullabl
4040
public void consumeScroll(int yUnconsumed) {
4141
scrollBy(0, yUnconsumed);
4242
}
43+
44+
@Override
45+
public int getContentHeight() {
46+
Adapter adapter = getAdapter();
47+
if(adapter == null){
48+
return 0;
49+
}
50+
LayoutManager layoutManager = getLayoutManager();
51+
if(layoutManager == null){
52+
return 0;
53+
}
54+
final int scrollRange = computeVerticalScrollRange();
55+
final int offsetRange = scrollRange - computeVerticalScrollExtent();
56+
if(offsetRange > 0){
57+
return HEIGHT_IS_ENOUGH_TO_SCROLL;
58+
}
59+
return scrollRange;
60+
}
4361
}

0 commit comments

Comments
 (0)