Skip to content

Commit 2016e42

Browse files
committed
Merge pull request timehop#66 from AntonPukhonin/master
Fixed headers for reverse LinearLayoutManager.
2 parents e257dcd + 2573530 commit 2016e42

File tree

7 files changed

+73
-22
lines changed

7 files changed

+73
-22
lines changed

library/src/main/java/com/timehop/stickyheadersrecyclerview/HeaderPositionCalculator.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,11 @@ public boolean hasStickyHeader(View itemView, int orientation, int position) {
5757
* list that immediately precedes it. Items with no headers will always return false.
5858
*
5959
* @param position of the list item in questions
60+
* @param isReverseLayout TRUE if layout manager has flag isReverseLayout
6061
* @return true if this item has a different header than the previous item in the list
6162
* @see {@link StickyRecyclerHeadersAdapter#getHeaderId(int)}
6263
*/
63-
public boolean hasNewHeader(int position) {
64+
public boolean hasNewHeader(int position, boolean isReverseLayout) {
6465
if (indexOutOfBounds(position)) {
6566
return false;
6667
}
@@ -71,7 +72,14 @@ public boolean hasNewHeader(int position) {
7172
return false;
7273
}
7374

74-
return position == 0 || headerId != mAdapter.getHeaderId(position - 1);
75+
long nextItemHeaderId = -1;
76+
int nextItemPosition = position + (isReverseLayout? 1: -1);
77+
if (!indexOutOfBounds(nextItemPosition)){
78+
nextItemHeaderId = mAdapter.getHeaderId(nextItemPosition);
79+
}
80+
int firstItemPosition = isReverseLayout? mAdapter.getItemCount()-1 : 0;
81+
82+
return position == firstItemPosition || headerId != nextItemHeaderId;
7583
}
7684

7785
private boolean indexOutOfBounds(int position) {
@@ -119,7 +127,8 @@ private boolean isStickyHeaderBeingPushedOffscreen(RecyclerView recyclerView, Vi
119127
return false;
120128
}
121129

122-
if (firstViewUnderHeaderPosition > 0 && hasNewHeader(firstViewUnderHeaderPosition)) {
130+
boolean isReverseLayout = mOrientationProvider.isReverseLayout(recyclerView);
131+
if (firstViewUnderHeaderPosition > 0 && hasNewHeader(firstViewUnderHeaderPosition, isReverseLayout)) {
123132
View nextHeader = mHeaderProvider.getHeader(recyclerView, firstViewUnderHeaderPosition);
124133
Rect nextHeaderMargins = mDimensionCalculator.getMargins(nextHeader);
125134
Rect headerMargins = mDimensionCalculator.getMargins(stickyHeader);
@@ -168,7 +177,10 @@ private void translateHeaderWithNextHeader(RecyclerView recyclerView, int orient
168177
* @return first item that is fully beneath a header
169178
*/
170179
private View getFirstViewUnobscuredByHeader(RecyclerView parent, View firstHeader) {
171-
for (int i = 0; i < parent.getChildCount(); i++) {
180+
boolean isReverseLayout = mOrientationProvider.isReverseLayout(parent);
181+
int step = isReverseLayout? -1 : 1;
182+
int from = isReverseLayout? parent.getChildCount()-1 : 0;
183+
for (int i = from; i >= 0 && i <= parent.getChildCount() - 1; i += step) {
172184
View child = parent.getChildAt(i);
173185
if (!itemIsObscuredByHeader(parent, child, firstHeader, mOrientationProvider.getOrientation(parent))) {
174186
return child;

library/src/main/java/com/timehop/stickyheadersrecyclerview/StickyRecyclerHeadersDecoration.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, Recycle
6060
if (itemPosition == RecyclerView.NO_POSITION) {
6161
return;
6262
}
63-
if (mHeaderPositionCalculator.hasNewHeader(itemPosition)) {
63+
if (mHeaderPositionCalculator.hasNewHeader(itemPosition, mOrientationProvider.isReverseLayout(parent))) {
6464
View header = getHeaderView(parent, itemPosition);
6565
setItemOffsetsForHeader(outRect, header, mOrientationProvider.getOrientation(parent));
6666
}
@@ -99,10 +99,9 @@ public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State st
9999
}
100100

101101
boolean hasStickyHeader = mHeaderPositionCalculator.hasStickyHeader(itemView, mOrientationProvider.getOrientation(parent), position);
102-
if (hasStickyHeader || mHeaderPositionCalculator.hasNewHeader(position)) {
102+
if (hasStickyHeader || mHeaderPositionCalculator.hasNewHeader(position, mOrientationProvider.isReverseLayout(parent))) {
103103
View header = mHeaderProvider.getHeader(parent, position);
104-
Rect headerOffset = mHeaderPositionCalculator.getHeaderBounds(parent, header,
105-
itemView, hasStickyHeader);
104+
Rect headerOffset = mHeaderPositionCalculator.getHeaderBounds(parent, header, itemView, hasStickyHeader);
106105
mRenderer.drawHeader(parent, canvas, header, headerOffset);
107106
mHeaderRects.put(position, headerOffset);
108107
}

library/src/main/java/com/timehop/stickyheadersrecyclerview/util/LinearLayoutOrientationProvider.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,21 @@ public class LinearLayoutOrientationProvider implements OrientationProvider {
1111
@Override
1212
public int getOrientation(RecyclerView recyclerView) {
1313
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
14+
throwIfNotLinearLayoutManager(layoutManager);
15+
return ((LinearLayoutManager) layoutManager).getOrientation();
16+
}
17+
18+
@Override
19+
public boolean isReverseLayout(RecyclerView recyclerView) {
20+
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
21+
throwIfNotLinearLayoutManager(layoutManager);
22+
return ((LinearLayoutManager) layoutManager).getReverseLayout();
23+
}
1424

15-
if (layoutManager instanceof LinearLayoutManager) {
16-
return ((LinearLayoutManager) layoutManager).getOrientation();
17-
} else {
25+
private void throwIfNotLinearLayoutManager(RecyclerView.LayoutManager layoutManager){
26+
if (!(layoutManager instanceof LinearLayoutManager)) {
1827
throw new IllegalStateException("StickyListHeadersDecoration can only be used with a " +
1928
"LinearLayoutManager.");
2029
}
2130
}
22-
2331
}

library/src/main/java/com/timehop/stickyheadersrecyclerview/util/OrientationProvider.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ public interface OrientationProvider {
99

1010
public int getOrientation(RecyclerView recyclerView);
1111

12+
public boolean isReverseLayout(RecyclerView recyclerView);
1213
}

sample/src/main/java/com/timehop/stickyheadersrecyclerview/sample/MainActivity.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
import android.os.Looper;
99
import android.support.v7.widget.LinearLayoutManager;
1010
import android.support.v7.widget.RecyclerView;
11+
import android.util.Log;
1112
import android.view.LayoutInflater;
1213
import android.view.View;
1314
import android.view.ViewGroup;
1415
import android.widget.Button;
1516
import android.widget.TextView;
1617
import android.widget.Toast;
18+
import android.widget.ToggleButton;
1719

1820
import com.timehop.stickyheadersrecyclerview.StickyRecyclerHeadersAdapter;
1921
import com.timehop.stickyheadersrecyclerview.StickyRecyclerHeadersDecoration;
@@ -28,8 +30,9 @@ protected void onCreate(Bundle savedInstanceState) {
2830
super.onCreate(savedInstanceState);
2931
setContentView(R.layout.activity_main);
3032

31-
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
33+
final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
3234
Button button = (Button) findViewById(R.id.button_update);
35+
final ToggleButton isReverseButton = (ToggleButton) findViewById(R.id.button_is_reverse);
3336

3437
// Set adapter populated with example dummy data
3538
final SampleArrayHeadersAdapter mAdapter = new SampleArrayHeadersAdapter();
@@ -56,7 +59,7 @@ public void run() {
5659

5760
// Set layout manager
5861
int orientation = getLayoutManagerOrientation(getResources().getConfiguration().orientation);
59-
final LinearLayoutManager layoutManager = new LinearLayoutManager(this, orientation, false);
62+
final LinearLayoutManager layoutManager = new LinearLayoutManager(this, orientation, isReverseButton.isChecked());
6063
recyclerView.setLayoutManager(layoutManager);
6164

6265
// Add the sticky headers decoration
@@ -74,7 +77,7 @@ public void run() {
7477
@Override
7578
public void onHeaderClick(View header, int position, long headerId) {
7679
Toast.makeText(MainActivity.this, "Header position: " + position + ", id: " + headerId,
77-
Toast.LENGTH_SHORT).show();
80+
Toast.LENGTH_SHORT).show();
7881
}
7982
});
8083
recyclerView.addOnItemTouchListener(touchListener);
@@ -85,10 +88,21 @@ public void onItemClick(View view, int position) {
8588
}
8689
}));
8790
mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
88-
@Override public void onChanged() {
91+
@Override
92+
public void onChanged() {
8993
headersDecor.invalidateHeaders();
9094
}
9195
});
96+
97+
isReverseButton.setOnClickListener(new View.OnClickListener() {
98+
@Override
99+
public void onClick(View v) {
100+
boolean isChecked = isReverseButton.isChecked();
101+
isReverseButton.setChecked(isChecked);
102+
layoutManager.setReverseLayout(isChecked);
103+
mAdapter.notifyDataSetChanged();
104+
}
105+
});
92106
}
93107

94108
private String[] getDummyDataSet() {

sample/src/main/res/layout/activity_main.xml

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,34 @@
66
tools:context=".MainActivity"
77
android:paddingTop="18dp">
88

9-
<Button
10-
android:id="@+id/button_update"
11-
android:layout_width="wrap_content"
9+
<LinearLayout
10+
android:id="@+id/buttons_container"
11+
android:layout_width="match_parent"
1212
android:layout_height="wrap_content"
13-
android:layout_centerHorizontal="true"
14-
android:text="@string/button_update"/>
13+
android:orientation="horizontal">
14+
15+
<Button
16+
android:id="@+id/button_update"
17+
android:layout_width="0dp"
18+
android:layout_height="wrap_content"
19+
android:layout_weight="1"
20+
android:text="@string/button_update"/>
21+
22+
<ToggleButton
23+
android:id="@+id/button_is_reverse"
24+
android:layout_width="0dp"
25+
android:layout_height="wrap_content"
26+
android:layout_weight="1"
27+
android:textOff="@string/button_reverse"
28+
android:textOn="@string/button_reverse"/>
29+
30+
</LinearLayout>
1531

1632
<android.support.v7.widget.RecyclerView
1733
android:id="@+id/recyclerview"
1834
android:layout_width="match_parent"
1935
android:layout_height="match_parent"
20-
android:layout_below="@+id/button_update"
36+
android:layout_below="@id/buttons_container"
2137
android:layout_margin="18dp"
2238
android:padding="16dp"
2339
android:background="#ffbef0ff"

sample/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33

44
<string name="app_name">StickyHeadersRecyclerView</string>
55
<string name="button_update">Start Update</string>
6+
<string name="button_reverse">Is reverse</string>
67

78
</resources>

0 commit comments

Comments
 (0)