Skip to content

Commit e83164f

Browse files
committed
- fixed some wrong values due to float errors
- added the DynamicStackSaveManager which can be used to save and load the stack (useful for lifecycle events) - updated README - updated example app to show the usage of the Savemanager - updated documentation
1 parent 081e4eb commit e83164f

File tree

9 files changed

+210
-73
lines changed

9 files changed

+210
-73
lines changed

DynamicStackAdapter/src/main/java/de/taop/hskl/dynamicStackAdapter/BuilderNotReadyException.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
*/
66
class BuilderNotReadyException extends RuntimeException {
77

8-
public BuilderNotReadyException(String message) {
9-
super(message);
10-
}
8+
public BuilderNotReadyException(String message) {
9+
super(message);
10+
}
1111

12-
public BuilderNotReadyException(String message, Throwable throwable) {
13-
super(message, throwable);
14-
}
12+
public BuilderNotReadyException(String message, Throwable throwable) {
13+
super(message, throwable);
14+
}
1515

16-
}
16+
}

DynamicStackAdapter/src/main/java/de/taop/hskl/dynamicStackAdapter/DynamicStackAdapter.java

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import android.support.v7.widget.RecyclerView;
44
import android.support.v7.widget.helper.ItemTouchHelper;
5-
import android.util.Log;
65
import android.view.LayoutInflater;
76
import android.view.View;
87
import android.view.ViewGroup;
@@ -28,24 +27,20 @@ public abstract class DynamicStackAdapter<T, VH extends DynamicStackViewHolder>
2827
implements ItemTouchHelperAdapter {
2928

3029
public RecyclerView container;
31-
private ArrayList<T> dataSet;
3230
protected SimpleItemTouchHelperCallback callback;
33-
3431
int minHeightPX;
35-
3632
float marginPixels;
3733
int maxItems;
3834
boolean autoResizeItems;
39-
40-
private float heightOfAllItems;
41-
42-
private ArrayList<VH> expandedNotSuitableItems;
43-
private Class<? extends DynamicStackViewHolder> holderClass;
44-
private VH vh;
4535
int resizeViewResourceID;
4636
boolean allowUserResize;
4737
int itemLayout;
4838
boolean reverseStack;
39+
private ArrayList<T> dataSet;
40+
private float heightOfAllItems;
41+
private ArrayList<VH> expandedNotSuitableItems;
42+
private Class<? extends DynamicStackViewHolder> holderClass;
43+
private VH vh;
4944

5045
protected DynamicStackAdapter(final RecyclerView container, Class<VH> holderClass) {
5146
dataSet = new ArrayList<>();
@@ -122,7 +117,7 @@ public void onBindViewHolder(final VH holder, final int position) {
122117
holder.itemView.post(new Runnable() {
123118
@Override
124119
public void run() {
125-
holder.updateOnResize(holder.getAdapterPosition(), object, holder.calculateHeightPercentage());
120+
holder.updateOnResizeInternal(holder.getAdapterPosition(), object);
126121
}
127122
});
128123

@@ -210,7 +205,18 @@ public void removeItemRange(int startPosition, int endPosition) {
210205
}
211206

212207
/**
213-
* Sets the Dataset for this Adapter.
208+
* Gets the Dataset of this Adapter. Keep in mind
209+
* that a reference to the list is returned!
210+
*
211+
* @return this adapters dataset
212+
*/
213+
public ArrayList<T> getDataSet() {
214+
return dataSet;
215+
}
216+
217+
/**
218+
* Sets the Dataset for this Adapter. Keep in mind
219+
* that a reference to the list is used!
214220
*
215221
* @param dataSet a list containing items
216222
*/
@@ -229,14 +235,14 @@ private boolean fitNewItem() {
229235
}
230236

231237

232-
return (heightOfAllItems + minHeightPX) <= container.getHeight();
238+
return (heightOfAllItems + minHeightPX) <= (container.getHeight() - marginPixels);
233239
}
234240

235241
private boolean fitNewItemAndAdjustHeight() {
236242
float accumulatedHeight = 0f;
237243

238244
if (!fitNewItem()) {
239-
float extraHeight = heightOfAllItems + minHeightPX - container.getHeight();
245+
float extraHeight = heightOfAllItems + minHeightPX - (container.getHeight() - marginPixels);
240246
expandedNotSuitableItems.clear();
241247
for (int i = 0; i < getItemCount(); i++) {
242248
VH itemHolder = (VH) container.findViewHolderForAdapterPosition(i);
@@ -247,7 +253,7 @@ private boolean fitNewItemAndAdjustHeight() {
247253

248254
itemHolder.isExpanded = itemHolder.itemView.getLayoutParams().height > minHeightPX;
249255

250-
itemHolder.updateOnResize(i, getItem(i), itemHolder.calculateHeightPercentage());
256+
itemHolder.updateOnResizeInternal(i, getItem(i));
251257

252258
return true;
253259
} else {
@@ -262,18 +268,18 @@ private boolean fitNewItemAndAdjustHeight() {
262268
if (accumulatedHeight >= minHeightPX) {
263269

264270
float deletedHeight = 0.0f;
265-
boolean fittetItem = false;
271+
boolean fittedItem = false;
266272

267-
for (int j = 0; j < expandedNotSuitableItems.size() && !fittetItem; j++) {
273+
for (int j = 0; j < expandedNotSuitableItems.size() && !fittedItem; j++) {
268274
DynamicStackViewHolder vh = expandedNotSuitableItems.get(j);
269275
deletedHeight += vh.itemView.getHeight() - minHeightPX;
270276
vh.itemView.getLayoutParams().height = minHeightPX;
271277
vh.itemView.requestLayout();
272278
vh.isExpanded = false;
273279

274-
vh.updateOnResize(vh.getAdapterPosition(), getItem(vh.getAdapterPosition()), vh.calculateHeightPercentage());
280+
vh.updateOnResizeInternal(vh.getAdapterPosition(), getItem(vh.getAdapterPosition()));
275281

276-
fittetItem = (deletedHeight >= minHeightPX);
282+
fittedItem = (deletedHeight >= minHeightPX);
277283

278284
}
279285
expandedNotSuitableItems.clear();
@@ -303,4 +309,5 @@ public void onItemMove(int fromPosition, int toPosition) {
303309
notifyItemMoved(fromPosition, toPosition);
304310
}
305311

312+
306313
}

DynamicStackAdapter/src/main/java/de/taop/hskl/dynamicStackAdapter/DynamicStackBuilder.java

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,19 @@
2020

2121
public class DynamicStackBuilder<A extends DynamicStackAdapter, VH extends DynamicStackViewHolder> {
2222

23-
private Class<A> adapterType;
24-
private Class<VH> viewHolderType;
25-
private A adapter;
26-
private RecyclerView rv;
27-
2823
public static final int DEFAULT_MAX_ITEMS = 6;
29-
public static final float DEFAULT_PIXEL_PADDING = 1.5f;
24+
public static final int DEFAULT_PIXEL_PADDING = 0;
3025
public static final boolean DEFAULT_AUTO_RESIZE_ITEMS = false;
3126
public static final boolean DEFAULT_USER_RESIZE = false;
3227
public static final boolean DEFAULT_USER_MOVE = true;
3328
public static final boolean DEFAULT_USER_DELETE = true;
3429
public static final boolean DEFAULT_REVERSE_STACK = false;
35-
30+
private Class<A> adapterType;
31+
private Class<VH> viewHolderType;
32+
private A adapter;
33+
private RecyclerView rv;
3634
private int maxItems;
37-
private float pixelPadding;
35+
private int pixelPadding;
3836
private List dataSet;
3937
private boolean autoResizeItems;
4038
private boolean userResize;
@@ -140,14 +138,17 @@ public DynamicStackBuilder setDataSet(List dataSet) {
140138
* Sets the Padding of the recyclerview. This is used when calculating
141139
* the minimal allowed height of the items. A higher value means a lower
142140
* minimal height. Play with this value if your max item count doesn't match the
143-
* shown number of items in your recyclerview. Note: negative values are
144-
* allowed. By default, the value is set to {@value DEFAULT_PIXEL_PADDING}.
141+
* shown number of items in your recyclerview or you specified a margin
142+
* in your itemView. <b>Note that this is a workaround (hence the
143+
* deprecated annotation). You should avoid using it because it will lead to some
144+
* float errors!</b>By default, the value is set to {@value DEFAULT_PIXEL_PADDING}.
145145
*
146146
* @param pixelPadding the padding in pixel
147147
* @return this Builder for chaining
148148
* @see #setMaxItems(int)
149149
*/
150-
public DynamicStackBuilder setPixelPadding(float pixelPadding) {
150+
@Deprecated
151+
public DynamicStackBuilder setPixelPadding(int pixelPadding) {
151152
this.pixelPadding = pixelPadding;
152153
return this;
153154
}
@@ -197,7 +198,7 @@ public DynamicStackBuilder allowUserDeleteItems(boolean userDelete) {
197198
* items stack at the top. new items will be added on the bottom and
198199
* then "float" to the top. By default, the value is set to
199200
* {@value DEFAULT_REVERSE_STACK}.
200-
*
201+
* <p>
201202
* <b>NOT WORKING AT THE MOMENT</b>
202203
*
203204
* @param reverseStack Whether or not the stack is reversed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package de.taop.hskl.dynamicStackAdapter;
2+
3+
import android.os.Bundle;
4+
5+
import java.util.ArrayList;
6+
7+
/**
8+
* Created by Adrian on 26.07.2017.
9+
*/
10+
11+
public class DynamicStackSaveManager {
12+
13+
private static final String STATE_ITEMS = "dynamic-stack-state-items";
14+
private static final String STATE_PERCENTAGE = "dynamic-stack-state-percentage-of-items";
15+
16+
private static ArrayList<Float> percentageOfItems = new ArrayList<>();
17+
18+
public static void clearHeightOfItems() {
19+
percentageOfItems.clear();
20+
}
21+
22+
private static void setPercentageOfPosition(int position, float percentage) {
23+
percentageOfItems.add(position, percentage);
24+
}
25+
26+
private static float getPercentageOfPosition(int position) {
27+
float height = percentageOfItems.get(position);
28+
return height <= 0f ? -1f : height;
29+
}
30+
31+
public static void saveDynamicStackAdapter(Bundle outState, DynamicStackAdapter adapter) {
32+
outState.putSerializable(STATE_ITEMS, adapter.getDataSet());
33+
34+
float percentage = 0f;
35+
36+
for (int i = 0; i < adapter.getItemCount(); i++) {
37+
DynamicStackViewHolder vh = ((DynamicStackViewHolder) adapter.container.findViewHolderForAdapterPosition(i));
38+
if (vh != null) {
39+
setPercentageOfPosition(i, vh.calculateHeightPercentage());
40+
percentage += getPercentageOfPosition(i);
41+
}
42+
}
43+
44+
outState.putSerializable(STATE_PERCENTAGE, percentageOfItems);
45+
}
46+
47+
public static void restoreDynamicStackAdapter(final Bundle savedInstanceState, final DynamicStackAdapter adapter) {
48+
adapter.setDataSet((ArrayList) savedInstanceState.getSerializable(STATE_ITEMS));
49+
percentageOfItems = (ArrayList<Float>) savedInstanceState.getSerializable(STATE_PERCENTAGE);
50+
51+
adapter.container.post(new Runnable() {
52+
@Override
53+
public void run() {
54+
55+
float extraHeight;
56+
float heightOfAllItems = 0f;
57+
58+
for (int i = 0; i < adapter.getItemCount(); i++) {
59+
float percentage = getPercentageOfPosition(i);
60+
61+
DynamicStackViewHolder vh = (DynamicStackViewHolder) adapter.container.findViewHolderForAdapterPosition(i);
62+
vh.percentage = percentage;
63+
vh.itemView.getLayoutParams().height =
64+
percentage <= 0f ? adapter.minHeightPX : (int) (percentage * (adapter.container.getHeight() - adapter.marginPixels));
65+
vh.itemView.requestLayout();
66+
vh.isExpanded = vh.itemView.getLayoutParams().height > adapter.minHeightPX;
67+
68+
heightOfAllItems += vh.itemView.getLayoutParams().height;
69+
}
70+
71+
DynamicStackViewHolder vh = (DynamicStackViewHolder) adapter.container.findViewHolderForAdapterPosition(adapter.getItemCount() - 1);
72+
if (heightOfAllItems > (adapter.container.getHeight() - adapter.marginPixels)) {
73+
vh.itemView.getLayoutParams().height -= (int) (heightOfAllItems - adapter.container.getHeight() - adapter.marginPixels);
74+
vh.itemView.requestLayout();
75+
}
76+
77+
}
78+
});
79+
80+
81+
}
82+
83+
84+
}

DynamicStackAdapter/src/main/java/de/taop/hskl/dynamicStackAdapter/DynamicStackViewHolder.java

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package de.taop.hskl.dynamicStackAdapter;
22

33
import android.support.v7.widget.RecyclerView;
4-
import android.util.Log;
54
import android.view.MotionEvent;
65
import android.view.View;
7-
import android.view.ViewGroup;
86

97
import de.taop.hskl.dynamicStackAdapter.helpers.ItemTouchHelperViewHolder;
108

@@ -25,7 +23,7 @@ public abstract class DynamicStackViewHolder extends RecyclerView.ViewHolder imp
2523
public int originalHeight;
2624
public boolean isExpanded = false;
2725
boolean allowUserResize;
28-
26+
float percentage;
2927
private View dynamicItemResize;
3028

3129
protected DynamicStackViewHolder(final View itemView, final DynamicStackAdapter adapter) {
@@ -54,7 +52,7 @@ public boolean onTouch(View view, MotionEvent me) {
5452
accumulatedHeight = 0f;
5553
for (int i = 0; i < adapter.getItemCount(); i++) {
5654
if (i != getAdapterPosition()) {
57-
View item = adapter.container.findViewHolderForAdapterPosition(i).itemView;
55+
View item = adapter.container.findViewHolderForAdapterPosition(i).itemView;
5856
accumulatedHeight += item.getHeight();
5957
}
6058
}
@@ -71,7 +69,7 @@ public boolean onTouch(View view, MotionEvent me) {
7169
float heightDiff = origY - me.getY();
7270

7371
if ((itemView.getLayoutParams().height + heightDiff) > adapter.minHeightPX) {
74-
if ((accumulatedHeight + itemView.getHeight() + heightDiff) <= adapter.container.getHeight()) {
72+
if ((accumulatedHeight + itemView.getHeight() + heightDiff) <= (adapter.container.getHeight() - adapter.marginPixels)) {
7573

7674
itemView.getLayoutParams().height += heightDiff;
7775
if (adapter.reverseStack) {
@@ -80,7 +78,7 @@ public boolean onTouch(View view, MotionEvent me) {
8078
}
8179
itemView.requestLayout();
8280
} else {
83-
itemView.getLayoutParams().height = (int) (adapter.container.getHeight() - accumulatedHeight);
81+
itemView.getLayoutParams().height = (int) (adapter.container.getHeight() - accumulatedHeight - adapter.marginPixels);
8482
itemView.requestLayout();
8583
}
8684

@@ -90,22 +88,21 @@ public boolean onTouch(View view, MotionEvent me) {
9088
isExpanded = false;
9189
}
9290

93-
updateOnResize(DynamicStackViewHolder.this.getAdapterPosition(),
94-
adapter.getItem(DynamicStackViewHolder.this.getAdapterPosition()),
95-
calculateHeightPercentage());
91+
updateOnResizeInternal(DynamicStackViewHolder.this.getAdapterPosition(),
92+
adapter.getItem(DynamicStackViewHolder.this.getAdapterPosition()));
9693

9794
}
9895

9996
return true;
10097
}
10198

102-
10399
});
104100
}
105101
}
106102

107103
float calculateHeightPercentage() {
108-
return itemView.getLayoutParams().height / (float) adapter.container.getHeight();
104+
percentage = itemView.getLayoutParams().height / (adapter.container.getHeight() - adapter.marginPixels);
105+
return percentage;
109106
}
110107

111108
/**
@@ -118,14 +115,24 @@ float calculateHeightPercentage() {
118115
protected abstract void findCustomViews(View itemView);
119116

120117
/**
121-
* You need to assign all your views which you want to use from the specified item layout here!
122-
* E.g.: customView = itemView.findViewByID(R.id.myView);
118+
* This method is internally called when a view is resized.
119+
* Do not override this!
120+
*
121+
* @param position the position of the ViewHolder
122+
* @param object the object at the position
123+
*/
124+
void updateOnResizeInternal(int position, Object object) {
125+
updateOnResize(position, object, calculateHeightPercentage());
126+
}
127+
128+
/**
129+
* This method is called when a view is resized.
123130
*
124-
* @param position the position of the ViewHolder
125-
* @param object the object at the position
126-
* @param sizeChangeAmount the percentage [0.0,1.0] of change in size since the resizing started
131+
* @param position the position of the ViewHolder
132+
* @param object the object at the position
133+
* @param itemViewPercentage the percentage [0.0,1.0] of the views height in relation to the recyclerViews height
127134
*/
128-
protected abstract void updateOnResize(int position, Object object, float sizeChangeAmount);
135+
public abstract void updateOnResize(int position, Object object, float itemViewPercentage);
129136

130137
@Override
131138
public void onItemSelected() {

0 commit comments

Comments
 (0)