Skip to content

Commit 792a6b9

Browse files
AlexNi245AndyScherzinger
authored andcommitted
finish implementation of sticky header implementation. This feature was created according to the this implementation : https://stackoverflow.com/questions/32949971/how-can-i-make-sticky-headers-in-recyclerview-without-external-lib
Signed-off-by: alex <alex.plutta@googlemail.com> Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
1 parent 13e7aff commit 792a6b9

File tree

4 files changed

+147
-20
lines changed

4 files changed

+147
-20
lines changed

src/main/java/com/owncloud/android/ui/activities/ActivitiesActivity.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import com.owncloud.android.ui.activity.FileActivity;
4242
import com.owncloud.android.ui.activity.FileDisplayActivity;
4343
import com.owncloud.android.ui.adapter.ActivityListAdapter;
44+
import com.owncloud.android.ui.adapter.StickyHeaderAdapter;
4445
import com.owncloud.android.ui.interfaces.ActivityListInterface;
4546
import com.owncloud.android.ui.preview.PreviewImageActivity;
4647
import com.owncloud.android.ui.preview.PreviewImageFragment;
@@ -174,7 +175,7 @@ private void setupContent() {
174175
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
175176

176177
recyclerView.setLayoutManager(layoutManager);
177-
recyclerView.addItemDecoration(new ActivityListItemDecoration());
178+
recyclerView.addItemDecoration(new ActivityListItemDecoration( adapter));
178179
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
179180

180181
@Override

src/main/java/com/owncloud/android/ui/activities/ActivityListItemDecoration.java

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,54 @@
22

33
import android.graphics.Canvas;
44
import android.graphics.Color;
5+
import android.util.Log;
6+
import android.view.LayoutInflater;
57
import android.view.View;
8+
import android.view.ViewGroup;
69

7-
import com.owncloud.android.ui.adapter.ActivityListAdapter;
10+
import com.owncloud.android.R;
11+
12+
import com.owncloud.android.ui.adapter.StickyHeaderAdapter;
813

914
import androidx.annotation.NonNull;
1015
import androidx.recyclerview.widget.RecyclerView;
1116

1217
public class ActivityListItemDecoration extends RecyclerView.ItemDecoration {
1318
private final String TAG = this.getClass().getSimpleName();
14-
private View currentHeader;
19+
private final StickyHeaderAdapter adapter;
20+
21+
22+
public ActivityListItemDecoration(StickyHeaderAdapter stickyHeaderAdapter) {
23+
this.adapter = stickyHeaderAdapter;
24+
}
1525

1626
@Override
1727
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
1828
super.onDrawOver(c, parent, state);
1929

20-
2130
View topChild = parent.getChildAt(0);
31+
if (topChild == null) {
32+
return;
33+
}
2234
int topChildPosition = parent.getChildAdapterPosition(topChild);
23-
if (topChildPosition == -1) {
35+
36+
if (topChildPosition == RecyclerView.NO_POSITION) {
2437
return;
2538
}
26-
ActivityListAdapter adapter = (ActivityListAdapter) parent.getAdapter();
39+
View currentHeader = getHeaderViewForItem(topChildPosition, parent);
40+
fixLayoutSize(parent, currentHeader);
41+
int contactPoint = currentHeader.getBottom();
42+
View childInContact = getChildInContact(parent, contactPoint);
2743

28-
Object topElement = adapter.getActivityAtByPosition(topChildPosition);
44+
if (childInContact == null) {
45+
return;
46+
}
2947

30-
if (topElement instanceof ActivityListHeader) {
31-
currentHeader = parent.getChildAt(0);
32-
currentHeader.setBackgroundColor(Color.WHITE);
48+
if (adapter.isHeader(parent.getChildAdapterPosition(childInContact))) {
49+
moveHeader(c, currentHeader, childInContact);
50+
return;
3351
}
52+
3453
drawHeader(c, currentHeader);
3554
}
3655

@@ -40,4 +59,50 @@ private void drawHeader(Canvas c, View header) {
4059
header.draw(c);
4160
c.restore();
4261
}
62+
63+
private void moveHeader(Canvas c, View currentHeader, View nextHeader) {
64+
c.save();
65+
c.translate(0, nextHeader.getTop() - currentHeader.getHeight());
66+
currentHeader.draw(c);
67+
c.restore();
68+
}
69+
70+
private View getChildInContact(RecyclerView parent, int contactPoint) {
71+
View childInContact = null;
72+
for (int i = 0; i < parent.getChildCount(); i++) {
73+
View currentChild = parent.getChildAt(i);
74+
if (currentChild.getBottom() > contactPoint) {
75+
if (currentChild.getTop() <= contactPoint) {
76+
childInContact = currentChild;
77+
break;
78+
}
79+
}
80+
}
81+
return childInContact;
82+
}
83+
84+
private View getHeaderViewForItem(int itemPosition, RecyclerView parent) {
85+
int headerPosition = adapter.getHeaderPositionForItem(itemPosition);
86+
int layoutId = adapter.getHeaderLayout(itemPosition);
87+
View header = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
88+
header.setBackgroundColor(Color.WHITE);
89+
adapter.bindHeaderData(header, headerPosition);
90+
return header;
91+
}
92+
93+
private void fixLayoutSize(ViewGroup parent, View view) {
94+
95+
// Specs for parent (RecyclerView)
96+
int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY);
97+
int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED);
98+
99+
// Specs for children (headers)
100+
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, parent.getPaddingLeft() + parent.getPaddingRight(), view.getLayoutParams().width);
101+
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, parent.getPaddingTop() + parent.getPaddingBottom(), view.getLayoutParams().height);
102+
103+
view.measure(childWidthSpec, childHeightSpec);
104+
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
105+
}
106+
107+
43108
}

src/main/java/com/owncloud/android/ui/adapter/ActivityListAdapter.java

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,13 @@
8080
import java.util.Locale;
8181

8282
import androidx.annotation.NonNull;
83+
import androidx.recyclerview.widget.LinearLayoutManager;
8384
import androidx.recyclerview.widget.RecyclerView;
8485

8586
/**
8687
* Adapter for the activity view
8788
*/
88-
public class ActivityListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
89+
public class ActivityListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements StickyHeaderAdapter {
8990

9091
static final int HEADER_TYPE = 100;
9192
static final int ACTIVITY_TYPE = 101;
@@ -142,7 +143,7 @@ public void setActivityItems(List<Object> activityItems, OwnCloudClient client,
142143
values.add(activity);
143144
} else {
144145
sTime = time;
145-
values.add(new ActivityListHeader(sTime));
146+
values.add(sTime);
146147
values.add(activity);
147148
}
148149
}
@@ -238,14 +239,9 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi
238239
}
239240
} else {
240241
ActivityViewHeaderHolder activityViewHeaderHolder = (ActivityViewHeaderHolder) holder;
241-
ActivityListHeader header = (ActivityListHeader) values.get(position);
242-
activityViewHeaderHolder.title.setText((String) header.getHeadline());
243-
}
244-
}
245-
246-
public Object getActivityAtByPosition(int pos) {
247-
return values.get(pos);
248242

243+
activityViewHeaderHolder.title.setText((String) values.get(position));
244+
}
249245
}
250246

251247
private ImageView createThumbnailNew(PreviewObject previewObject) {
@@ -435,6 +431,37 @@ CharSequence getHeaderDateString(Context context, long modificationTimestamp) {
435431
}
436432
}
437433

434+
435+
@Override
436+
public int getHeaderPositionForItem(int itemPosition) {
437+
int headerPosition = 0;
438+
do {
439+
if (this.isHeader(itemPosition)) {
440+
headerPosition = itemPosition;
441+
break;
442+
}
443+
itemPosition -= 1;
444+
} while (itemPosition >= 0);
445+
return headerPosition;
446+
}
447+
448+
@Override
449+
public int getHeaderLayout(int headerPosition) {
450+
return R.layout.activity_list_item_header;
451+
}
452+
453+
@Override
454+
public void bindHeaderData(View header, int headerPosition) {
455+
TextView textView = header.findViewById(R.id.title_header);
456+
String headline = (String) values.get(headerPosition);
457+
textView.setText(headline);
458+
}
459+
460+
@Override
461+
public boolean isHeader(int itemPosition) {
462+
return this.getItemViewType(itemPosition) == HEADER_TYPE;
463+
}
464+
438465
protected class ActivityViewHolder extends RecyclerView.ViewHolder {
439466

440467
private final ImageView activityIcon;
@@ -453,7 +480,7 @@ protected class ActivityViewHolder extends RecyclerView.ViewHolder {
453480
}
454481
}
455482

456-
protected class ActivityViewHeaderHolder extends RecyclerView.ViewHolder {
483+
public class ActivityViewHeaderHolder extends RecyclerView.ViewHolder {
457484

458485
private final TextView title;
459486

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.owncloud.android.ui.adapter;
2+
3+
import android.view.View;
4+
5+
public interface StickyHeaderAdapter {
6+
/**
7+
* This method gets called by {@link HeaderItemDecoration} to fetch the position of the header item in the adapter
8+
* that is used for (represents) item at specified position.
9+
* @param itemPosition int. Adapter's position of the item for which to do the search of the position of the header item.
10+
* @return int. Position of the header item in the adapter.
11+
*/
12+
int getHeaderPositionForItem(int itemPosition);
13+
14+
/**
15+
* This method gets called by {@link HeaderItemDecoration} to get layout resource id for the header item at specified adapter's position.
16+
* @param headerPosition int. Position of the header item in the adapter.
17+
* @return int. Layout resource id.
18+
*/
19+
int getHeaderLayout(int headerPosition);
20+
21+
/**
22+
* This method gets called by {@link HeaderItemDecoration} to setup the header View.
23+
* @param header View. Header to set the data on.
24+
* @param headerPosition int. Position of the header item in the adapter.
25+
*/
26+
void bindHeaderData(View header, int headerPosition);
27+
28+
/**
29+
* This method gets called by {@link HeaderItemDecoration} to verify whether the item represents a header.
30+
* @param itemPosition int.
31+
* @return true, if item at the specified adapter's position represents a header.
32+
*/
33+
boolean isHeader(int itemPosition);
34+
}

0 commit comments

Comments
 (0)