@phucprime/react-native-image-editor 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +247 -0
- package/android/build.gradle +62 -0
- package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/android/gradle/wrapper/gradle-wrapper.properties +5 -0
- package/android/gradlew +240 -0
- package/android/gradlew.bat +91 -0
- package/android/libs/photo-editor-android.jar +0 -0
- package/android/src/main/AndroidManifest.xml +5 -0
- package/android/src/main/java/com/ahmedadeltito/photoeditor/BaseActivity.java +300 -0
- package/android/src/main/java/com/ahmedadeltito/photoeditor/ColorPickerAdapter.java +129 -0
- package/android/src/main/java/com/ahmedadeltito/photoeditor/EmojiAdapter.java +91 -0
- package/android/src/main/java/com/ahmedadeltito/photoeditor/EmojiFragment.java +56 -0
- package/android/src/main/java/com/ahmedadeltito/photoeditor/GalleryUtils.java +73 -0
- package/android/src/main/java/com/ahmedadeltito/photoeditor/ImageAdapter.java +69 -0
- package/android/src/main/java/com/ahmedadeltito/photoeditor/ImageFragment.java +93 -0
- package/android/src/main/java/com/ahmedadeltito/photoeditor/MainActivity.java +29 -0
- package/android/src/main/java/com/ahmedadeltito/photoeditor/MediaActivity.java +95 -0
- package/android/src/main/java/com/ahmedadeltito/photoeditor/PhotoEditorActivity.java +1055 -0
- package/android/src/main/java/com/ahmedadeltito/photoeditor/TranslationService.java +31 -0
- package/android/src/main/java/com/ahmedadeltito/photoeditor/UtilFunctions.java +176 -0
- package/android/src/main/java/com/ahmedadeltito/photoeditor/widget/RealtimeBlurView.java +359 -0
- package/android/src/main/java/com/ahmedadeltito/photoeditor/widget/SlidingUpPanelLayout.java +1527 -0
- package/android/src/main/java/com/ahmedadeltito/photoeditor/widget/ViewDragHelper.java +1466 -0
- package/android/src/main/java/ui/photoeditor/RNPhotoEditorModule.java +108 -0
- package/android/src/main/java/ui/photoeditor/RNPhotoEditorPackage.java +29 -0
- package/android/src/main/res/drawable/above_shadow.xml +7 -0
- package/android/src/main/res/drawable/below_shadow.xml +7 -0
- package/android/src/main/res/drawable/fading_shadow.xml +7 -0
- package/android/src/main/res/drawable/rounded_border_text_view.xml +13 -0
- package/android/src/main/res/drawable-xhdpi/aa.png +0 -0
- package/android/src/main/res/drawable-xhdpi/bb.png +0 -0
- package/android/src/main/res/drawable-xhdpi/c.png +0 -0
- package/android/src/main/res/drawable-xhdpi/cc.png +0 -0
- package/android/src/main/res/drawable-xhdpi/d.png +0 -0
- package/android/src/main/res/drawable-xhdpi/dd.png +0 -0
- package/android/src/main/res/drawable-xhdpi/e.png +0 -0
- package/android/src/main/res/drawable-xhdpi/ee.png +0 -0
- package/android/src/main/res/drawable-xhdpi/i.png +0 -0
- package/android/src/main/res/drawable-xhdpi/j.png +0 -0
- package/android/src/main/res/drawable-xhdpi/k.png +0 -0
- package/android/src/main/res/drawable-xhdpi/l.png +0 -0
- package/android/src/main/res/drawable-xhdpi/m.png +0 -0
- package/android/src/main/res/drawable-xhdpi/n.png +0 -0
- package/android/src/main/res/drawable-xhdpi/o.png +0 -0
- package/android/src/main/res/drawable-xhdpi/p.png +0 -0
- package/android/src/main/res/drawable-xhdpi/q.png +0 -0
- package/android/src/main/res/drawable-xhdpi/r.png +0 -0
- package/android/src/main/res/drawable-xhdpi/s.png +0 -0
- package/android/src/main/res/drawable-xhdpi/t.png +0 -0
- package/android/src/main/res/drawable-xhdpi/u.png +0 -0
- package/android/src/main/res/drawable-xhdpi/v.png +0 -0
- package/android/src/main/res/drawable-xhdpi/w.png +0 -0
- package/android/src/main/res/drawable-xhdpi/x.png +0 -0
- package/android/src/main/res/drawable-xhdpi/y.png +0 -0
- package/android/src/main/res/drawable-xhdpi/z.png +0 -0
- package/android/src/main/res/layout/activity_main.xml +28 -0
- package/android/src/main/res/layout/activity_photo_editor.xml +316 -0
- package/android/src/main/res/layout/add_text_popup_window.xml +47 -0
- package/android/src/main/res/layout/color_picker_item_list.xml +15 -0
- package/android/src/main/res/layout/fragment_main_photo_edit_emoji.xml +11 -0
- package/android/src/main/res/layout/fragment_main_photo_edit_image.xml +10 -0
- package/android/src/main/res/layout/fragment_photo_edit_emoji_item_list.xml +17 -0
- package/android/src/main/res/layout/fragment_photo_edit_image_item_list.xml +16 -0
- package/android/src/main/res/raw/emojioneandroid +0 -0
- package/android/src/main/res/raw/eventtusicons +0 -0
- package/android/src/main/res/raw/font_awesome_solid.otf +0 -0
- package/android/src/main/res/raw/icomoon.ttf +0 -0
- package/android/src/main/res/values/arrays.xml +447 -0
- package/android/src/main/res/values/attrs.xml +30 -0
- package/android/src/main/res/values/colors.xml +21 -0
- package/android/src/main/res/values/dimens.xml +4 -0
- package/android/src/main/res/values/strings.xml +33 -0
- package/android/src/main/res/values/styles.xml +27 -0
- package/ios/RNImageEditor.podspec +26 -0
- package/ios/RNPhotoEditor.h +13 -0
- package/ios/RNPhotoEditor.m +163 -0
- package/ios/RNPhotoEditor.xcodeproj/project.pbxproj +362 -0
- package/lib/index.d.ts +125 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +114 -0
- package/lib/index.js.map +1 -0
- package/package.json +73 -0
- package/react-native-image-editor.podspec +39 -0
- package/src/index.ts +232 -0
|
@@ -0,0 +1,1527 @@
|
|
|
1
|
+
package com.ahmedadeltito.photoeditor.widget;
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint;
|
|
4
|
+
import android.content.Context;
|
|
5
|
+
import android.content.res.TypedArray;
|
|
6
|
+
import android.graphics.Canvas;
|
|
7
|
+
import android.graphics.Paint;
|
|
8
|
+
import android.graphics.PixelFormat;
|
|
9
|
+
import android.graphics.Rect;
|
|
10
|
+
import android.graphics.drawable.Drawable;
|
|
11
|
+
import android.os.Build;
|
|
12
|
+
import android.os.Parcel;
|
|
13
|
+
import android.os.Parcelable;
|
|
14
|
+
import androidx.annotation.NonNull;
|
|
15
|
+
import androidx.core.view.MotionEventCompat;
|
|
16
|
+
import androidx.core.view.ViewCompat;
|
|
17
|
+
import androidx.recyclerview.widget.RecyclerView;
|
|
18
|
+
import android.util.AttributeSet;
|
|
19
|
+
import android.view.Gravity;
|
|
20
|
+
import android.view.MotionEvent;
|
|
21
|
+
import android.view.View;
|
|
22
|
+
import android.view.ViewGroup;
|
|
23
|
+
import android.view.accessibility.AccessibilityEvent;
|
|
24
|
+
import android.widget.ListView;
|
|
25
|
+
import android.widget.ScrollView;
|
|
26
|
+
|
|
27
|
+
import ui.photoeditor.R;
|
|
28
|
+
import com.nineoldandroids.view.animation.AnimatorProxy;
|
|
29
|
+
|
|
30
|
+
public class SlidingUpPanelLayout extends ViewGroup {
|
|
31
|
+
|
|
32
|
+
private static final String TAG = SlidingUpPanelLayout.class.getSimpleName();
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Default peeking out panel height
|
|
36
|
+
*/
|
|
37
|
+
private static final int DEFAULT_PANEL_HEIGHT = 0; // dp;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Default anchor point height
|
|
41
|
+
*/
|
|
42
|
+
private static final float DEFAULT_ANCHOR_POINT = 1.0f; // In relative %
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Default initial state for the component
|
|
46
|
+
*/
|
|
47
|
+
private static PanelState DEFAULT_SLIDE_STATE = PanelState.COLLAPSED;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Default height of the shadow above the peeking out panel
|
|
51
|
+
*/
|
|
52
|
+
private static final int DEFAULT_SHADOW_HEIGHT = 4; // dp;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* If no fade color is given by default it will fade to 80% gray.
|
|
56
|
+
*/
|
|
57
|
+
private static final int DEFAULT_FADE_COLOR = 0x99000000;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Default Minimum velocity that will be detected as a fling
|
|
61
|
+
*/
|
|
62
|
+
private static final int DEFAULT_MIN_FLING_VELOCITY = 400; // dips per second
|
|
63
|
+
/**
|
|
64
|
+
* Default is set to false because that is how it was written
|
|
65
|
+
*/
|
|
66
|
+
private static final boolean DEFAULT_OVERLAY_FLAG = false;
|
|
67
|
+
/**
|
|
68
|
+
* Default is set to true for clip panel for performance reasons
|
|
69
|
+
*/
|
|
70
|
+
private static final boolean DEFAULT_CLIP_PANEL_FLAG = true;
|
|
71
|
+
/**
|
|
72
|
+
* Default attributes for layout
|
|
73
|
+
*/
|
|
74
|
+
private static final int[] DEFAULT_ATTRS = new int[]{
|
|
75
|
+
android.R.attr.gravity
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Minimum velocity that will be detected as a fling
|
|
80
|
+
*/
|
|
81
|
+
private int mMinFlingVelocity = DEFAULT_MIN_FLING_VELOCITY;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The fade color used for the panel covered by the slider. 0 = no fading.
|
|
85
|
+
*/
|
|
86
|
+
private int mCoveredFadeColor = DEFAULT_FADE_COLOR;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Default parallax length of the main view
|
|
90
|
+
*/
|
|
91
|
+
private static final int DEFAULT_PARALLAX_OFFSET = 0;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The paint used to dim the main layout when sliding
|
|
95
|
+
*/
|
|
96
|
+
private final Paint mCoveredFadePaint = new Paint();
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Drawable used to draw the shadow between panes.
|
|
100
|
+
*/
|
|
101
|
+
private final Drawable mShadowDrawable;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The size of the overhang in pixels.
|
|
105
|
+
*/
|
|
106
|
+
private int mPanelHeight = -1;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The size of the shadow in pixels.
|
|
110
|
+
*/
|
|
111
|
+
private int mShadowHeight = -1;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Parallax offset
|
|
115
|
+
*/
|
|
116
|
+
private int mParallaxOffset = -1;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* True if the collapsed panel should be dragged up.
|
|
120
|
+
*/
|
|
121
|
+
private boolean mIsSlidingUp;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Panel overlays the windows instead of putting it underneath it.
|
|
125
|
+
*/
|
|
126
|
+
private boolean mOverlayContent = DEFAULT_OVERLAY_FLAG;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The main view is clipped to the main top border
|
|
130
|
+
*/
|
|
131
|
+
private boolean mClipPanel = DEFAULT_CLIP_PANEL_FLAG;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* If provided, the panel can be dragged by only this view. Otherwise, the entire panel can be
|
|
135
|
+
* used for dragging.
|
|
136
|
+
*/
|
|
137
|
+
private View mDragView;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* If provided, the panel can be dragged by only this view. Otherwise, the entire panel can be
|
|
141
|
+
* used for dragging.
|
|
142
|
+
*/
|
|
143
|
+
private int mDragViewResId = -1;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* If provided, the panel will transfer the scroll from this view to itself when needed.
|
|
147
|
+
*/
|
|
148
|
+
private View mScrollableView;
|
|
149
|
+
private int mScrollableViewResId;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The child view that can slide, if any.
|
|
153
|
+
*/
|
|
154
|
+
private View mSlideableView;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* The main view
|
|
158
|
+
*/
|
|
159
|
+
private View mMainView;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Current state of the slideable view.
|
|
163
|
+
*/
|
|
164
|
+
public enum PanelState {
|
|
165
|
+
EXPANDED,
|
|
166
|
+
COLLAPSED,
|
|
167
|
+
ANCHORED,
|
|
168
|
+
HIDDEN,
|
|
169
|
+
DRAGGING
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private PanelState mSlideState = DEFAULT_SLIDE_STATE;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* If the current slide state is DRAGGING, this will store the last non dragging state
|
|
176
|
+
*/
|
|
177
|
+
private PanelState mLastNotDraggingSlideState = null;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* How far the panel is offset from its expanded position.
|
|
181
|
+
* range [0, 1] where 0 = collapsed, 1 = expanded.
|
|
182
|
+
*/
|
|
183
|
+
private float mSlideOffset;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* How far in pixels the slideable panel may move.
|
|
187
|
+
*/
|
|
188
|
+
private int mSlideRange;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* An anchor point where the panel can stop during sliding
|
|
192
|
+
*/
|
|
193
|
+
private float mAnchorPoint = 1.f;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* A panel view is locked into internal scrolling or another condition that
|
|
197
|
+
* is preventing a drag.
|
|
198
|
+
*/
|
|
199
|
+
private boolean mIsUnableToDrag;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Flag indicating that sliding feature is enabled\disabled
|
|
203
|
+
*/
|
|
204
|
+
private boolean mIsTouchEnabled;
|
|
205
|
+
|
|
206
|
+
private float mPrevMotionY;
|
|
207
|
+
private float mInitialMotionX;
|
|
208
|
+
private float mInitialMotionY;
|
|
209
|
+
private boolean mIsScrollableViewHandlingTouch = false;
|
|
210
|
+
|
|
211
|
+
private PanelSlideListener mPanelSlideListener;
|
|
212
|
+
|
|
213
|
+
private final ViewDragHelper mDragHelper;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Stores whether or not the pane was expanded the last time it was slideable.
|
|
217
|
+
* If expand/collapse operations are invoked this state is modified. Used by
|
|
218
|
+
* instance state save/restore.
|
|
219
|
+
*/
|
|
220
|
+
private boolean mFirstLayout = true;
|
|
221
|
+
|
|
222
|
+
private final Rect mTmpRect = new Rect();
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Listener for monitoring events about sliding panes.
|
|
226
|
+
*/
|
|
227
|
+
public interface PanelSlideListener {
|
|
228
|
+
/**
|
|
229
|
+
* Called when a sliding pane's position changes.
|
|
230
|
+
*
|
|
231
|
+
* @param panel The child view that was moved
|
|
232
|
+
* @param slideOffset The new offset of this sliding pane within its range, from 0-1
|
|
233
|
+
*/
|
|
234
|
+
public void onPanelSlide(View panel, float slideOffset);
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Called when a sliding panel becomes slid completely collapsed.
|
|
238
|
+
*
|
|
239
|
+
* @param panel The child view that was slid to an collapsed position
|
|
240
|
+
*/
|
|
241
|
+
public void onPanelCollapsed(View panel);
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Called when a sliding panel becomes slid completely expanded.
|
|
245
|
+
*
|
|
246
|
+
* @param panel The child view that was slid to a expanded position
|
|
247
|
+
*/
|
|
248
|
+
public void onPanelExpanded(View panel);
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Called when a sliding panel becomes anchored.
|
|
252
|
+
*
|
|
253
|
+
* @param panel The child view that was slid to a anchored position
|
|
254
|
+
*/
|
|
255
|
+
public void onPanelAnchored(View panel);
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Called when a sliding panel becomes completely hidden.
|
|
259
|
+
*
|
|
260
|
+
* @param panel The child view that was slid to a hidden position
|
|
261
|
+
*/
|
|
262
|
+
public void onPanelHidden(View panel);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* No-op stubs for {@link PanelSlideListener}. If you only want to implement a subset
|
|
267
|
+
* of the listener methods you can extend this instead of implement the full interface.
|
|
268
|
+
*/
|
|
269
|
+
public static class SimplePanelSlideListener implements PanelSlideListener {
|
|
270
|
+
@Override
|
|
271
|
+
public void onPanelSlide(View panel, float slideOffset) {
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
@Override
|
|
275
|
+
public void onPanelCollapsed(View panel) {
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
@Override
|
|
279
|
+
public void onPanelExpanded(View panel) {
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
@Override
|
|
283
|
+
public void onPanelAnchored(View panel) {
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
@Override
|
|
287
|
+
public void onPanelHidden(View panel) {
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
public SlidingUpPanelLayout(Context context) {
|
|
292
|
+
this(context, null);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
public SlidingUpPanelLayout(Context context, AttributeSet attrs) {
|
|
296
|
+
this(context, attrs, 0);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
public SlidingUpPanelLayout(Context context, AttributeSet attrs, int defStyle) {
|
|
300
|
+
super(context, attrs, defStyle);
|
|
301
|
+
|
|
302
|
+
if (isInEditMode()) {
|
|
303
|
+
mShadowDrawable = null;
|
|
304
|
+
mDragHelper = null;
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (attrs != null) {
|
|
309
|
+
TypedArray defAttrs = context.obtainStyledAttributes(attrs, DEFAULT_ATTRS);
|
|
310
|
+
|
|
311
|
+
if (defAttrs != null) {
|
|
312
|
+
int gravity = defAttrs.getInt(0, Gravity.NO_GRAVITY);
|
|
313
|
+
setGravity(gravity);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
defAttrs.recycle();
|
|
317
|
+
|
|
318
|
+
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlidingUpPanelLayout);
|
|
319
|
+
|
|
320
|
+
if (ta != null) {
|
|
321
|
+
mPanelHeight = ta.getDimensionPixelSize(R.styleable.SlidingUpPanelLayout_umanoPanelHeight, -1);
|
|
322
|
+
mShadowHeight = ta.getDimensionPixelSize(R.styleable.SlidingUpPanelLayout_umanoShadowHeight, -1);
|
|
323
|
+
mParallaxOffset = ta.getDimensionPixelSize(R.styleable.SlidingUpPanelLayout_umanoParallaxOffset, -1);
|
|
324
|
+
|
|
325
|
+
mMinFlingVelocity = ta.getInt(R.styleable.SlidingUpPanelLayout_umanoFlingVelocity, DEFAULT_MIN_FLING_VELOCITY);
|
|
326
|
+
mCoveredFadeColor = ta.getColor(R.styleable.SlidingUpPanelLayout_umanoFadeColor, DEFAULT_FADE_COLOR);
|
|
327
|
+
|
|
328
|
+
mDragViewResId = ta.getResourceId(R.styleable.SlidingUpPanelLayout_umanoDragView, -1);
|
|
329
|
+
mScrollableViewResId = ta.getResourceId(R.styleable.SlidingUpPanelLayout_umanoScrollableView, -1);
|
|
330
|
+
|
|
331
|
+
mOverlayContent = ta.getBoolean(R.styleable.SlidingUpPanelLayout_umanoOverlay, DEFAULT_OVERLAY_FLAG);
|
|
332
|
+
mClipPanel = ta.getBoolean(R.styleable.SlidingUpPanelLayout_umanoClipPanel, DEFAULT_CLIP_PANEL_FLAG);
|
|
333
|
+
|
|
334
|
+
mAnchorPoint = ta.getFloat(R.styleable.SlidingUpPanelLayout_umanoAnchorPoint, DEFAULT_ANCHOR_POINT);
|
|
335
|
+
|
|
336
|
+
mSlideState = PanelState.values()[ta.getInt(R.styleable.SlidingUpPanelLayout_umanoInitialState, DEFAULT_SLIDE_STATE.ordinal())];
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
ta.recycle();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
final float density = context.getResources().getDisplayMetrics().density;
|
|
343
|
+
if (mPanelHeight == -1) {
|
|
344
|
+
mPanelHeight = (int) (DEFAULT_PANEL_HEIGHT * density + 0.5f);
|
|
345
|
+
}
|
|
346
|
+
if (mShadowHeight == -1) {
|
|
347
|
+
mShadowHeight = (int) (DEFAULT_SHADOW_HEIGHT * density + 0.5f);
|
|
348
|
+
}
|
|
349
|
+
if (mParallaxOffset == -1) {
|
|
350
|
+
mParallaxOffset = (int) (DEFAULT_PARALLAX_OFFSET * density);
|
|
351
|
+
}
|
|
352
|
+
// If the shadow height is zero, don't show the shadow
|
|
353
|
+
if (mShadowHeight > 0) {
|
|
354
|
+
if (mIsSlidingUp) {
|
|
355
|
+
mShadowDrawable = getResources().getDrawable(R.drawable.above_shadow);
|
|
356
|
+
} else {
|
|
357
|
+
mShadowDrawable = getResources().getDrawable(R.drawable.below_shadow);
|
|
358
|
+
}
|
|
359
|
+
} else {
|
|
360
|
+
mShadowDrawable = null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
setWillNotDraw(false);
|
|
364
|
+
|
|
365
|
+
mDragHelper = ViewDragHelper.create(this, 0.5f, new DragHelperCallback());
|
|
366
|
+
mDragHelper.setMinVelocity(mMinFlingVelocity * density);
|
|
367
|
+
|
|
368
|
+
mIsTouchEnabled = true;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Set the Drag View after the view is inflated
|
|
373
|
+
*/
|
|
374
|
+
@Override
|
|
375
|
+
protected void onFinishInflate() {
|
|
376
|
+
super.onFinishInflate();
|
|
377
|
+
if (mDragViewResId != -1) {
|
|
378
|
+
setDragView(findViewById(mDragViewResId));
|
|
379
|
+
}
|
|
380
|
+
if (mScrollableViewResId != -1) {
|
|
381
|
+
setScrollableView(findViewById(mScrollableViewResId));
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
public void setGravity(int gravity) {
|
|
386
|
+
if (gravity != Gravity.TOP && gravity != Gravity.BOTTOM) {
|
|
387
|
+
throw new IllegalArgumentException("gravity must be set to either top or bottom");
|
|
388
|
+
}
|
|
389
|
+
mIsSlidingUp = gravity == Gravity.BOTTOM;
|
|
390
|
+
if (!mFirstLayout) {
|
|
391
|
+
requestLayout();
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Set the color used to fade the pane covered by the sliding pane out when the pane
|
|
397
|
+
* will become fully covered in the expanded state.
|
|
398
|
+
*
|
|
399
|
+
* @param color An ARGB-packed color value
|
|
400
|
+
*/
|
|
401
|
+
public void setCoveredFadeColor(int color) {
|
|
402
|
+
mCoveredFadeColor = color;
|
|
403
|
+
invalidate();
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* @return The ARGB-packed color value used to fade the fixed pane
|
|
408
|
+
*/
|
|
409
|
+
public int getCoveredFadeColor() {
|
|
410
|
+
return mCoveredFadeColor;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Set sliding enabled flag
|
|
415
|
+
*
|
|
416
|
+
* @param enabled flag value
|
|
417
|
+
*/
|
|
418
|
+
public void setTouchEnabled(boolean enabled) {
|
|
419
|
+
mIsTouchEnabled = enabled;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
public boolean isTouchEnabled() {
|
|
423
|
+
return mIsTouchEnabled && mSlideableView != null && mSlideState != PanelState.HIDDEN;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Set the collapsed panel height in pixels
|
|
428
|
+
*
|
|
429
|
+
* @param val A height in pixels
|
|
430
|
+
*/
|
|
431
|
+
public void setPanelHeight(int val) {
|
|
432
|
+
if (getPanelHeight() == val) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
mPanelHeight = val;
|
|
437
|
+
if (!mFirstLayout) {
|
|
438
|
+
requestLayout();
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (getPanelState() == PanelState.COLLAPSED) {
|
|
442
|
+
smoothToBottom();
|
|
443
|
+
invalidate();
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
protected void smoothToBottom() {
|
|
449
|
+
smoothSlideTo(0, 0);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* @return The current shadow height
|
|
454
|
+
*/
|
|
455
|
+
public int getShadowHeight() {
|
|
456
|
+
return mShadowHeight;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Set the shadow height
|
|
461
|
+
*
|
|
462
|
+
* @param val A height in pixels
|
|
463
|
+
*/
|
|
464
|
+
public void setShadowHeight(int val) {
|
|
465
|
+
mShadowHeight = val;
|
|
466
|
+
if (!mFirstLayout) {
|
|
467
|
+
invalidate();
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* @return The current collapsed panel height
|
|
473
|
+
*/
|
|
474
|
+
public int getPanelHeight() {
|
|
475
|
+
return mPanelHeight;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* @return The current parallax offset
|
|
480
|
+
*/
|
|
481
|
+
public int getCurrentParallaxOffset() {
|
|
482
|
+
// Clamp slide offset at zero for parallax computation;
|
|
483
|
+
int offset = (int) (mParallaxOffset * Math.max(mSlideOffset, 0));
|
|
484
|
+
return mIsSlidingUp ? -offset : offset;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Set parallax offset for the panel
|
|
489
|
+
*
|
|
490
|
+
* @param val A height in pixels
|
|
491
|
+
*/
|
|
492
|
+
public void setParallaxOffset(int val) {
|
|
493
|
+
mParallaxOffset = val;
|
|
494
|
+
if (!mFirstLayout) {
|
|
495
|
+
requestLayout();
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* @return The current minimin fling velocity
|
|
501
|
+
*/
|
|
502
|
+
public int getMinFlingVelocity() {
|
|
503
|
+
return mMinFlingVelocity;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Sets the minimum fling velocity for the panel
|
|
508
|
+
*
|
|
509
|
+
* @param val the new value
|
|
510
|
+
*/
|
|
511
|
+
public void setMinFlingVelocity(int val) {
|
|
512
|
+
mMinFlingVelocity = val;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Sets the panel slide listener
|
|
517
|
+
*
|
|
518
|
+
* @param listener
|
|
519
|
+
*/
|
|
520
|
+
public void setPanelSlideListener(PanelSlideListener listener) {
|
|
521
|
+
mPanelSlideListener = listener;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Set the draggable view portion. Use to null, to allow the whole panel to be draggable
|
|
526
|
+
*
|
|
527
|
+
* @param dragView A view that will be used to drag the panel.
|
|
528
|
+
*/
|
|
529
|
+
public void setDragView(View dragView) {
|
|
530
|
+
if (mDragView != null) {
|
|
531
|
+
mDragView.setOnClickListener(null);
|
|
532
|
+
}
|
|
533
|
+
mDragView = dragView;
|
|
534
|
+
if (mDragView != null) {
|
|
535
|
+
mDragView.setClickable(true);
|
|
536
|
+
mDragView.setFocusable(false);
|
|
537
|
+
mDragView.setFocusableInTouchMode(false);
|
|
538
|
+
mDragView.setOnClickListener(new OnClickListener() {
|
|
539
|
+
@Override
|
|
540
|
+
public void onClick(View v) {
|
|
541
|
+
if (!isEnabled() || !isTouchEnabled()) return;
|
|
542
|
+
if (mSlideState != PanelState.EXPANDED && mSlideState != PanelState.ANCHORED) {
|
|
543
|
+
if (mAnchorPoint < 1.0f) {
|
|
544
|
+
setPanelState(PanelState.ANCHORED);
|
|
545
|
+
} else {
|
|
546
|
+
setPanelState(PanelState.EXPANDED);
|
|
547
|
+
}
|
|
548
|
+
} else {
|
|
549
|
+
setPanelState(PanelState.COLLAPSED);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
});
|
|
553
|
+
;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Set the draggable view portion. Use to null, to allow the whole panel to be draggable
|
|
559
|
+
*
|
|
560
|
+
* @param dragViewResId The resource ID of the new drag view
|
|
561
|
+
*/
|
|
562
|
+
public void setDragView(int dragViewResId) {
|
|
563
|
+
mDragViewResId = dragViewResId;
|
|
564
|
+
setDragView(findViewById(dragViewResId));
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Set the scrollable child of the sliding layout. If set, scrolling will be transfered between
|
|
569
|
+
* the panel and the view when necessary
|
|
570
|
+
*
|
|
571
|
+
* @param scrollableView The scrollable view
|
|
572
|
+
*/
|
|
573
|
+
public void setScrollableView(View scrollableView) {
|
|
574
|
+
mScrollableView = scrollableView;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Set an anchor point where the panel can stop during sliding
|
|
579
|
+
*
|
|
580
|
+
* @param anchorPoint A value between 0 and 1, determining the position of the anchor point
|
|
581
|
+
* starting from the top of the layout.
|
|
582
|
+
*/
|
|
583
|
+
public void setAnchorPoint(float anchorPoint) {
|
|
584
|
+
if (anchorPoint > 0 && anchorPoint <= 1) {
|
|
585
|
+
mAnchorPoint = anchorPoint;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Gets the currently set anchor point
|
|
591
|
+
*
|
|
592
|
+
* @return the currently set anchor point
|
|
593
|
+
*/
|
|
594
|
+
public float getAnchorPoint() {
|
|
595
|
+
return mAnchorPoint;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Sets whether or not the panel overlays the content
|
|
600
|
+
*
|
|
601
|
+
* @param overlayed
|
|
602
|
+
*/
|
|
603
|
+
public void setOverlayed(boolean overlayed) {
|
|
604
|
+
mOverlayContent = overlayed;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Check if the panel is set as an overlay.
|
|
609
|
+
*/
|
|
610
|
+
public boolean isOverlayed() {
|
|
611
|
+
return mOverlayContent;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Sets whether or not the main content is clipped to the top of the panel
|
|
616
|
+
*
|
|
617
|
+
* @param clip
|
|
618
|
+
*/
|
|
619
|
+
public void setClipPanel(boolean clip) {
|
|
620
|
+
mClipPanel = clip;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Check whether or not the main content is clipped to the top of the panel
|
|
625
|
+
*/
|
|
626
|
+
public boolean isClipPanel() {
|
|
627
|
+
return mClipPanel;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
void dispatchOnPanelSlide(View panel) {
|
|
631
|
+
if (mPanelSlideListener != null) {
|
|
632
|
+
mPanelSlideListener.onPanelSlide(panel, mSlideOffset);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
void dispatchOnPanelExpanded(View panel) {
|
|
637
|
+
if (mPanelSlideListener != null) {
|
|
638
|
+
mPanelSlideListener.onPanelExpanded(panel);
|
|
639
|
+
}
|
|
640
|
+
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
void dispatchOnPanelCollapsed(View panel) {
|
|
644
|
+
if (mPanelSlideListener != null) {
|
|
645
|
+
mPanelSlideListener.onPanelCollapsed(panel);
|
|
646
|
+
}
|
|
647
|
+
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
void dispatchOnPanelAnchored(View panel) {
|
|
651
|
+
if (mPanelSlideListener != null) {
|
|
652
|
+
mPanelSlideListener.onPanelAnchored(panel);
|
|
653
|
+
}
|
|
654
|
+
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
void dispatchOnPanelHidden(View panel) {
|
|
658
|
+
if (mPanelSlideListener != null) {
|
|
659
|
+
mPanelSlideListener.onPanelHidden(panel);
|
|
660
|
+
}
|
|
661
|
+
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
void updateObscuredViewVisibility() {
|
|
665
|
+
if (getChildCount() == 0) {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
final int leftBound = getPaddingLeft();
|
|
669
|
+
final int rightBound = getWidth() - getPaddingRight();
|
|
670
|
+
final int topBound = getPaddingTop();
|
|
671
|
+
final int bottomBound = getHeight() - getPaddingBottom();
|
|
672
|
+
final int left;
|
|
673
|
+
final int right;
|
|
674
|
+
final int top;
|
|
675
|
+
final int bottom;
|
|
676
|
+
if (mSlideableView != null && hasOpaqueBackground(mSlideableView)) {
|
|
677
|
+
left = mSlideableView.getLeft();
|
|
678
|
+
right = mSlideableView.getRight();
|
|
679
|
+
top = mSlideableView.getTop();
|
|
680
|
+
bottom = mSlideableView.getBottom();
|
|
681
|
+
} else {
|
|
682
|
+
left = right = top = bottom = 0;
|
|
683
|
+
}
|
|
684
|
+
View child = getChildAt(0);
|
|
685
|
+
final int clampedChildLeft = Math.max(leftBound, child.getLeft());
|
|
686
|
+
final int clampedChildTop = Math.max(topBound, child.getTop());
|
|
687
|
+
final int clampedChildRight = Math.min(rightBound, child.getRight());
|
|
688
|
+
final int clampedChildBottom = Math.min(bottomBound, child.getBottom());
|
|
689
|
+
final int vis;
|
|
690
|
+
if (clampedChildLeft >= left && clampedChildTop >= top &&
|
|
691
|
+
clampedChildRight <= right && clampedChildBottom <= bottom) {
|
|
692
|
+
vis = INVISIBLE;
|
|
693
|
+
} else {
|
|
694
|
+
vis = VISIBLE;
|
|
695
|
+
}
|
|
696
|
+
child.setVisibility(vis);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
void setAllChildrenVisible() {
|
|
700
|
+
for (int i = 0, childCount = getChildCount(); i < childCount; i++) {
|
|
701
|
+
final View child = getChildAt(i);
|
|
702
|
+
if (child.getVisibility() == INVISIBLE) {
|
|
703
|
+
child.setVisibility(VISIBLE);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
private static boolean hasOpaqueBackground(View v) {
|
|
709
|
+
final Drawable bg = v.getBackground();
|
|
710
|
+
return bg != null && bg.getOpacity() == PixelFormat.OPAQUE;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
@Override
|
|
714
|
+
protected void onAttachedToWindow() {
|
|
715
|
+
super.onAttachedToWindow();
|
|
716
|
+
mFirstLayout = true;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
@Override
|
|
720
|
+
protected void onDetachedFromWindow() {
|
|
721
|
+
super.onDetachedFromWindow();
|
|
722
|
+
mFirstLayout = true;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
@Override
|
|
726
|
+
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
727
|
+
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
|
728
|
+
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
|
729
|
+
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
|
730
|
+
final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
|
|
731
|
+
|
|
732
|
+
if (widthMode != MeasureSpec.EXACTLY) {
|
|
733
|
+
throw new IllegalStateException("Width must have an exact value or MATCH_PARENT");
|
|
734
|
+
} else if (heightMode != MeasureSpec.EXACTLY) {
|
|
735
|
+
throw new IllegalStateException("Height must have an exact value or MATCH_PARENT");
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
final int childCount = getChildCount();
|
|
739
|
+
|
|
740
|
+
if (childCount != 2) {
|
|
741
|
+
throw new IllegalStateException("Sliding up panel layout must have exactly 2 children!");
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
mMainView = getChildAt(0);
|
|
745
|
+
mSlideableView = getChildAt(1);
|
|
746
|
+
if (mDragView == null) {
|
|
747
|
+
setDragView(mSlideableView);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// If the sliding panel is not visible, then put the whole view in the hidden state
|
|
751
|
+
if (mSlideableView.getVisibility() != VISIBLE) {
|
|
752
|
+
mSlideState = PanelState.HIDDEN;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
int layoutHeight = heightSize - getPaddingTop() - getPaddingBottom();
|
|
756
|
+
int layoutWidth = widthSize - getPaddingLeft() - getPaddingRight();
|
|
757
|
+
|
|
758
|
+
// First pass. Measure based on child LayoutParams width/height.
|
|
759
|
+
for (int i = 0; i < childCount; i++) {
|
|
760
|
+
final View child = getChildAt(i);
|
|
761
|
+
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
|
762
|
+
|
|
763
|
+
// We always measure the sliding panel in order to know it's height (needed for show panel)
|
|
764
|
+
if (child.getVisibility() == GONE && i == 0) {
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
int height = layoutHeight;
|
|
769
|
+
int width = layoutWidth;
|
|
770
|
+
if (child == mMainView) {
|
|
771
|
+
if (!mOverlayContent && mSlideState != PanelState.HIDDEN) {
|
|
772
|
+
height -= mPanelHeight;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
width -= lp.leftMargin + lp.rightMargin;
|
|
776
|
+
} else if (child == mSlideableView) {
|
|
777
|
+
// The slideable view should be aware of its top margin.
|
|
778
|
+
// See https://github.com/umano/AndroidSlidingUpPanel/issues/412.
|
|
779
|
+
height -= lp.topMargin;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
int childWidthSpec;
|
|
783
|
+
if (lp.width == LayoutParams.WRAP_CONTENT) {
|
|
784
|
+
childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST);
|
|
785
|
+
} else if (lp.width == LayoutParams.MATCH_PARENT) {
|
|
786
|
+
childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
|
|
787
|
+
} else {
|
|
788
|
+
childWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
int childHeightSpec;
|
|
792
|
+
if (lp.height == LayoutParams.WRAP_CONTENT) {
|
|
793
|
+
childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
|
|
794
|
+
} else if (lp.height == LayoutParams.MATCH_PARENT) {
|
|
795
|
+
childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
|
|
796
|
+
} else {
|
|
797
|
+
childHeightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
child.measure(childWidthSpec, childHeightSpec);
|
|
801
|
+
|
|
802
|
+
if (child == mSlideableView) {
|
|
803
|
+
mSlideRange = mSlideableView.getMeasuredHeight() - mPanelHeight;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
setMeasuredDimension(widthSize, heightSize);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
@Override
|
|
811
|
+
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
|
812
|
+
final int paddingLeft = getPaddingLeft();
|
|
813
|
+
final int paddingTop = getPaddingTop();
|
|
814
|
+
|
|
815
|
+
final int childCount = getChildCount();
|
|
816
|
+
|
|
817
|
+
if (mFirstLayout) {
|
|
818
|
+
switch (mSlideState) {
|
|
819
|
+
case EXPANDED:
|
|
820
|
+
mSlideOffset = 1.0f;
|
|
821
|
+
break;
|
|
822
|
+
case ANCHORED:
|
|
823
|
+
mSlideOffset = mAnchorPoint;
|
|
824
|
+
break;
|
|
825
|
+
case HIDDEN:
|
|
826
|
+
int newTop = computePanelTopPosition(0.0f) + (mIsSlidingUp ? +mPanelHeight : -mPanelHeight);
|
|
827
|
+
mSlideOffset = computeSlideOffset(newTop);
|
|
828
|
+
break;
|
|
829
|
+
default:
|
|
830
|
+
mSlideOffset = 0.f;
|
|
831
|
+
break;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
for (int i = 0; i < childCount; i++) {
|
|
836
|
+
final View child = getChildAt(i);
|
|
837
|
+
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
|
838
|
+
|
|
839
|
+
// Always layout the sliding view on the first layout
|
|
840
|
+
if (child.getVisibility() == GONE && (i == 0 || mFirstLayout)) {
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
final int childHeight = child.getMeasuredHeight();
|
|
845
|
+
int childTop = paddingTop;
|
|
846
|
+
|
|
847
|
+
if (child == mSlideableView) {
|
|
848
|
+
childTop = computePanelTopPosition(mSlideOffset);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
if (!mIsSlidingUp) {
|
|
852
|
+
if (child == mMainView && !mOverlayContent) {
|
|
853
|
+
childTop = computePanelTopPosition(mSlideOffset) + mSlideableView.getMeasuredHeight();
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
final int childBottom = childTop + childHeight;
|
|
857
|
+
final int childLeft = paddingLeft + lp.leftMargin;
|
|
858
|
+
final int childRight = childLeft + child.getMeasuredWidth();
|
|
859
|
+
|
|
860
|
+
child.layout(childLeft, childTop, childRight, childBottom);
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
if (mFirstLayout) {
|
|
864
|
+
updateObscuredViewVisibility();
|
|
865
|
+
}
|
|
866
|
+
applyParallaxForCurrentSlideOffset();
|
|
867
|
+
|
|
868
|
+
mFirstLayout = false;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
@Override
|
|
872
|
+
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
|
873
|
+
super.onSizeChanged(w, h, oldw, oldh);
|
|
874
|
+
// Recalculate sliding panes and their details
|
|
875
|
+
if (h != oldh) {
|
|
876
|
+
mFirstLayout = true;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
@Override
|
|
881
|
+
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
|
882
|
+
// If the scrollable view is handling touch, never intercept
|
|
883
|
+
if (mIsScrollableViewHandlingTouch) {
|
|
884
|
+
mDragHelper.cancel();
|
|
885
|
+
return false;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
final int action = MotionEventCompat.getActionMasked(ev);
|
|
889
|
+
final float x = ev.getX();
|
|
890
|
+
final float y = ev.getY();
|
|
891
|
+
|
|
892
|
+
switch (action) {
|
|
893
|
+
case MotionEvent.ACTION_DOWN: {
|
|
894
|
+
mIsUnableToDrag = false;
|
|
895
|
+
mInitialMotionX = x;
|
|
896
|
+
mInitialMotionY = y;
|
|
897
|
+
break;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
case MotionEvent.ACTION_MOVE: {
|
|
901
|
+
final float adx = Math.abs(x - mInitialMotionX);
|
|
902
|
+
final float ady = Math.abs(y - mInitialMotionY);
|
|
903
|
+
final int dragSlop = mDragHelper.getTouchSlop();
|
|
904
|
+
|
|
905
|
+
if ((ady > dragSlop && adx > ady) || !isViewUnder(mDragView, (int) mInitialMotionX, (int) mInitialMotionY)) {
|
|
906
|
+
mDragHelper.cancel();
|
|
907
|
+
mIsUnableToDrag = true;
|
|
908
|
+
return false;
|
|
909
|
+
}
|
|
910
|
+
break;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
case MotionEvent.ACTION_CANCEL:
|
|
914
|
+
case MotionEvent.ACTION_UP:
|
|
915
|
+
// If the dragView is still dragging when we get here, we need to call processTouchEvent
|
|
916
|
+
// so that the view is settled
|
|
917
|
+
// Added to make scrollable views work (tokudu)
|
|
918
|
+
if (mDragHelper.isDragging()) {
|
|
919
|
+
mDragHelper.processTouchEvent(ev);
|
|
920
|
+
return true;
|
|
921
|
+
}
|
|
922
|
+
break;
|
|
923
|
+
}
|
|
924
|
+
return mDragHelper.shouldInterceptTouchEvent(ev);
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
@Override
|
|
928
|
+
public boolean onTouchEvent(@NonNull MotionEvent ev) {
|
|
929
|
+
if (!isEnabled() || !isTouchEnabled()) {
|
|
930
|
+
return super.onTouchEvent(ev);
|
|
931
|
+
}
|
|
932
|
+
try {
|
|
933
|
+
mDragHelper.processTouchEvent(ev);
|
|
934
|
+
return true;
|
|
935
|
+
} catch (Exception ex) {
|
|
936
|
+
// Ignore the pointer out of range exception
|
|
937
|
+
return false;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
@Override
|
|
942
|
+
public boolean dispatchTouchEvent(@NonNull MotionEvent ev) {
|
|
943
|
+
final int action = MotionEventCompat.getActionMasked(ev);
|
|
944
|
+
|
|
945
|
+
if (!isEnabled() || !isTouchEnabled() || (mIsUnableToDrag && action != MotionEvent.ACTION_DOWN)) {
|
|
946
|
+
mDragHelper.cancel();
|
|
947
|
+
return super.dispatchTouchEvent(ev);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
final float y = ev.getY();
|
|
951
|
+
|
|
952
|
+
if (action == MotionEvent.ACTION_DOWN) {
|
|
953
|
+
mIsScrollableViewHandlingTouch = false;
|
|
954
|
+
mPrevMotionY = y;
|
|
955
|
+
} else if (action == MotionEvent.ACTION_MOVE) {
|
|
956
|
+
float dy = y - mPrevMotionY;
|
|
957
|
+
mPrevMotionY = y;
|
|
958
|
+
|
|
959
|
+
// If the scroll view isn't under the touch, pass the
|
|
960
|
+
// event along to the dragView.
|
|
961
|
+
if (!isViewUnder(mScrollableView, (int) mInitialMotionX, (int) mInitialMotionY)) {
|
|
962
|
+
return super.dispatchTouchEvent(ev);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// Which direction (up or down) is the drag moving?
|
|
966
|
+
if (dy * (mIsSlidingUp ? 1 : -1) > 0) { // Collapsing
|
|
967
|
+
// Is the child less than fully scrolled?
|
|
968
|
+
// Then let the child handle it.
|
|
969
|
+
if (getScrollableViewScrollPosition() > 0) {
|
|
970
|
+
mIsScrollableViewHandlingTouch = true;
|
|
971
|
+
return super.dispatchTouchEvent(ev);
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// Was the child handling the touch previously?
|
|
975
|
+
// Then we need to rejigger things so that the
|
|
976
|
+
// drag panel gets a proper down event.
|
|
977
|
+
if (mIsScrollableViewHandlingTouch) {
|
|
978
|
+
// Send an 'UP' event to the child.
|
|
979
|
+
MotionEvent up = MotionEvent.obtain(ev);
|
|
980
|
+
up.setAction(MotionEvent.ACTION_CANCEL);
|
|
981
|
+
super.dispatchTouchEvent(up);
|
|
982
|
+
up.recycle();
|
|
983
|
+
|
|
984
|
+
// Send a 'DOWN' event to the panel. (We'll cheat
|
|
985
|
+
// and hijack this one)
|
|
986
|
+
ev.setAction(MotionEvent.ACTION_DOWN);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
mIsScrollableViewHandlingTouch = false;
|
|
990
|
+
return this.onTouchEvent(ev);
|
|
991
|
+
} else if (dy * (mIsSlidingUp ? 1 : -1) < 0) { // Expanding
|
|
992
|
+
// Is the panel less than fully expanded?
|
|
993
|
+
// Then we'll handle the drag here.
|
|
994
|
+
if (mSlideOffset < 1.0f) {
|
|
995
|
+
mIsScrollableViewHandlingTouch = false;
|
|
996
|
+
return this.onTouchEvent(ev);
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// Was the panel handling the touch previously?
|
|
1000
|
+
// Then we need to rejigger things so that the
|
|
1001
|
+
// child gets a proper down event.
|
|
1002
|
+
if (!mIsScrollableViewHandlingTouch && mDragHelper.isDragging()) {
|
|
1003
|
+
mDragHelper.cancel();
|
|
1004
|
+
ev.setAction(MotionEvent.ACTION_DOWN);
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
mIsScrollableViewHandlingTouch = true;
|
|
1008
|
+
return super.dispatchTouchEvent(ev);
|
|
1009
|
+
}
|
|
1010
|
+
} else if (action == MotionEvent.ACTION_UP && mIsScrollableViewHandlingTouch) {
|
|
1011
|
+
// If the scrollable view was handling the touch and we receive an up
|
|
1012
|
+
// we want to clear any previous dragging state so we don't intercept a touch stream accidentally
|
|
1013
|
+
mDragHelper.setDragState(ViewDragHelper.STATE_IDLE);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// In all other cases, just let the default behavior take over.
|
|
1017
|
+
return super.dispatchTouchEvent(ev);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
private boolean isViewUnder(View view, int x, int y) {
|
|
1021
|
+
if (view == null) return false;
|
|
1022
|
+
int[] viewLocation = new int[2];
|
|
1023
|
+
view.getLocationOnScreen(viewLocation);
|
|
1024
|
+
int[] parentLocation = new int[2];
|
|
1025
|
+
this.getLocationOnScreen(parentLocation);
|
|
1026
|
+
int screenX = parentLocation[0] + x;
|
|
1027
|
+
int screenY = parentLocation[1] + y;
|
|
1028
|
+
return screenX >= viewLocation[0] && screenX < viewLocation[0] + view.getWidth() &&
|
|
1029
|
+
screenY >= viewLocation[1] && screenY < viewLocation[1] + view.getHeight();
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
protected int getScrollableViewScrollPosition() {
|
|
1033
|
+
if (mScrollableView == null) return 0;
|
|
1034
|
+
if (mScrollableView instanceof ScrollView) {
|
|
1035
|
+
if (mIsSlidingUp) {
|
|
1036
|
+
return mScrollableView.getScrollY();
|
|
1037
|
+
} else {
|
|
1038
|
+
ScrollView sv = ((ScrollView) mScrollableView);
|
|
1039
|
+
View child = sv.getChildAt(0);
|
|
1040
|
+
return (child.getBottom() - (sv.getHeight() + sv.getScrollY()));
|
|
1041
|
+
}
|
|
1042
|
+
} else if (mScrollableView instanceof ListView && ((ListView) mScrollableView).getChildCount() > 0) {
|
|
1043
|
+
ListView lv = ((ListView) mScrollableView);
|
|
1044
|
+
if (lv.getAdapter() == null) return 0;
|
|
1045
|
+
if (mIsSlidingUp) {
|
|
1046
|
+
View firstChild = lv.getChildAt(0);
|
|
1047
|
+
// Approximate the scroll position based on the top child and the first visible item
|
|
1048
|
+
return lv.getFirstVisiblePosition() * firstChild.getHeight() - firstChild.getTop();
|
|
1049
|
+
} else {
|
|
1050
|
+
View lastChild = lv.getChildAt(lv.getChildCount() - 1);
|
|
1051
|
+
// Approximate the scroll position based on the bottom child and the last visible item
|
|
1052
|
+
return (lv.getAdapter().getCount() - lv.getLastVisiblePosition() - 1) * lastChild.getHeight() + lastChild.getBottom() - lv.getBottom();
|
|
1053
|
+
}
|
|
1054
|
+
} else if (mScrollableView instanceof RecyclerView && ((RecyclerView) mScrollableView).getChildCount() > 0) {
|
|
1055
|
+
RecyclerView rv = ((RecyclerView) mScrollableView);
|
|
1056
|
+
RecyclerView.LayoutManager lm = rv.getLayoutManager();
|
|
1057
|
+
if (rv.getAdapter() == null) return 0;
|
|
1058
|
+
if (mIsSlidingUp) {
|
|
1059
|
+
View firstChild = rv.getChildAt(0);
|
|
1060
|
+
// Approximate the scroll position based on the top child and the first visible item
|
|
1061
|
+
return rv.getChildLayoutPosition(firstChild) * lm.getDecoratedMeasuredHeight(firstChild) - lm.getDecoratedTop(firstChild);
|
|
1062
|
+
} else {
|
|
1063
|
+
View lastChild = rv.getChildAt(rv.getChildCount() - 1);
|
|
1064
|
+
// Approximate the scroll position based on the bottom child and the last visible item
|
|
1065
|
+
return (rv.getAdapter().getItemCount() - 1) * lm.getDecoratedMeasuredHeight(lastChild) + lm.getDecoratedBottom(lastChild) - rv.getBottom();
|
|
1066
|
+
}
|
|
1067
|
+
} else {
|
|
1068
|
+
return 0;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/*
|
|
1073
|
+
* Computes the top position of the panel based on the slide offset.
|
|
1074
|
+
*/
|
|
1075
|
+
private int computePanelTopPosition(float slideOffset) {
|
|
1076
|
+
int slidingViewHeight = mSlideableView != null ? mSlideableView.getMeasuredHeight() : 0;
|
|
1077
|
+
int slidePixelOffset = (int) (slideOffset * mSlideRange);
|
|
1078
|
+
// Compute the top of the panel if its collapsed
|
|
1079
|
+
return mIsSlidingUp
|
|
1080
|
+
? getMeasuredHeight() - getPaddingBottom() - mPanelHeight - slidePixelOffset
|
|
1081
|
+
: getPaddingTop() - slidingViewHeight + mPanelHeight + slidePixelOffset;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
/*
|
|
1085
|
+
* Computes the slide offset based on the top position of the panel
|
|
1086
|
+
*/
|
|
1087
|
+
private float computeSlideOffset(int topPosition) {
|
|
1088
|
+
// Compute the panel top position if the panel is collapsed (offset 0)
|
|
1089
|
+
final int topBoundCollapsed = computePanelTopPosition(0);
|
|
1090
|
+
|
|
1091
|
+
// Determine the new slide offset based on the collapsed top position and the new required
|
|
1092
|
+
// top position
|
|
1093
|
+
return (mIsSlidingUp
|
|
1094
|
+
? (float) (topBoundCollapsed - topPosition) / mSlideRange
|
|
1095
|
+
: (float) (topPosition - topBoundCollapsed) / mSlideRange);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
/**
|
|
1099
|
+
* Returns the current state of the panel as an enum.
|
|
1100
|
+
*
|
|
1101
|
+
* @return the current panel state
|
|
1102
|
+
*/
|
|
1103
|
+
public PanelState getPanelState() {
|
|
1104
|
+
return mSlideState;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
/**
|
|
1108
|
+
* Change panel state to the given state with
|
|
1109
|
+
*
|
|
1110
|
+
* @param state - new panel state
|
|
1111
|
+
*/
|
|
1112
|
+
public void setPanelState(PanelState state) {
|
|
1113
|
+
if (state == null || state == PanelState.DRAGGING) {
|
|
1114
|
+
throw new IllegalArgumentException("Panel state cannot be null or DRAGGING.");
|
|
1115
|
+
}
|
|
1116
|
+
if (!isEnabled()
|
|
1117
|
+
|| (!mFirstLayout && mSlideableView == null)
|
|
1118
|
+
|| state == mSlideState
|
|
1119
|
+
|| mSlideState == PanelState.DRAGGING) return;
|
|
1120
|
+
|
|
1121
|
+
if (mFirstLayout) {
|
|
1122
|
+
mSlideState = state;
|
|
1123
|
+
} else {
|
|
1124
|
+
if (mSlideState == PanelState.HIDDEN) {
|
|
1125
|
+
mSlideableView.setVisibility(View.VISIBLE);
|
|
1126
|
+
requestLayout();
|
|
1127
|
+
}
|
|
1128
|
+
switch (state) {
|
|
1129
|
+
case ANCHORED:
|
|
1130
|
+
smoothSlideTo(mAnchorPoint, 0);
|
|
1131
|
+
break;
|
|
1132
|
+
case COLLAPSED:
|
|
1133
|
+
smoothSlideTo(0, 0);
|
|
1134
|
+
break;
|
|
1135
|
+
case EXPANDED:
|
|
1136
|
+
smoothSlideTo(1.0f, 0);
|
|
1137
|
+
break;
|
|
1138
|
+
case HIDDEN:
|
|
1139
|
+
int newTop = computePanelTopPosition(0.0f) + (mIsSlidingUp ? +mPanelHeight : -mPanelHeight);
|
|
1140
|
+
smoothSlideTo(computeSlideOffset(newTop), 0);
|
|
1141
|
+
break;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/**
|
|
1147
|
+
* Update the parallax based on the current slide offset.
|
|
1148
|
+
*/
|
|
1149
|
+
@SuppressLint("NewApi")
|
|
1150
|
+
private void applyParallaxForCurrentSlideOffset() {
|
|
1151
|
+
if (mParallaxOffset > 0) {
|
|
1152
|
+
int mainViewOffset = getCurrentParallaxOffset();
|
|
1153
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
|
1154
|
+
mMainView.setTranslationY(mainViewOffset);
|
|
1155
|
+
} else {
|
|
1156
|
+
AnimatorProxy.wrap(mMainView).setTranslationY(mainViewOffset);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
private void onPanelDragged(int newTop) {
|
|
1162
|
+
mLastNotDraggingSlideState = mSlideState;
|
|
1163
|
+
mSlideState = PanelState.DRAGGING;
|
|
1164
|
+
// Recompute the slide offset based on the new top position
|
|
1165
|
+
mSlideOffset = computeSlideOffset(newTop);
|
|
1166
|
+
applyParallaxForCurrentSlideOffset();
|
|
1167
|
+
// Dispatch the slide event
|
|
1168
|
+
dispatchOnPanelSlide(mSlideableView);
|
|
1169
|
+
// If the slide offset is negative, and overlay is not on, we need to increase the
|
|
1170
|
+
// height of the main content
|
|
1171
|
+
LayoutParams lp = (LayoutParams) mMainView.getLayoutParams();
|
|
1172
|
+
int defaultHeight = getHeight() - getPaddingBottom() - getPaddingTop() - mPanelHeight;
|
|
1173
|
+
|
|
1174
|
+
if (mSlideOffset <= 0 && !mOverlayContent) {
|
|
1175
|
+
// expand the main view
|
|
1176
|
+
lp.height = mIsSlidingUp ? (newTop - getPaddingBottom()) : (getHeight() - getPaddingBottom() - mSlideableView.getMeasuredHeight() - newTop);
|
|
1177
|
+
mMainView.requestLayout();
|
|
1178
|
+
} else if (lp.height != defaultHeight && !mOverlayContent) {
|
|
1179
|
+
lp.height = defaultHeight;
|
|
1180
|
+
mMainView.requestLayout();
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
@Override
|
|
1185
|
+
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
|
1186
|
+
boolean result;
|
|
1187
|
+
|
|
1188
|
+
int save = 0;
|
|
1189
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
1190
|
+
save = canvas.saveLayer(null, null);
|
|
1191
|
+
} else {
|
|
1192
|
+
save = canvas.save();
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
if (mSlideableView != child) { // if main view
|
|
1196
|
+
// Clip against the slider; no sense drawing what will immediately be covered,
|
|
1197
|
+
// Unless the panel is set to overlay content
|
|
1198
|
+
canvas.getClipBounds(mTmpRect);
|
|
1199
|
+
if (!mOverlayContent) {
|
|
1200
|
+
if (mIsSlidingUp) {
|
|
1201
|
+
mTmpRect.bottom = Math.min(mTmpRect.bottom, mSlideableView.getTop());
|
|
1202
|
+
} else {
|
|
1203
|
+
mTmpRect.top = Math.max(mTmpRect.top, mSlideableView.getBottom());
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
if (mClipPanel) {
|
|
1207
|
+
canvas.clipRect(mTmpRect);
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
result = super.drawChild(canvas, child, drawingTime);
|
|
1211
|
+
|
|
1212
|
+
if (mCoveredFadeColor != 0 && mSlideOffset > 0) {
|
|
1213
|
+
final int baseAlpha = (mCoveredFadeColor & 0xff000000) >>> 24;
|
|
1214
|
+
final int imag = (int) (baseAlpha * mSlideOffset);
|
|
1215
|
+
final int color = imag << 24 | (mCoveredFadeColor & 0xffffff);
|
|
1216
|
+
mCoveredFadePaint.setColor(color);
|
|
1217
|
+
canvas.drawRect(mTmpRect, mCoveredFadePaint);
|
|
1218
|
+
}
|
|
1219
|
+
} else {
|
|
1220
|
+
result = super.drawChild(canvas, child, drawingTime);
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
canvas.restoreToCount(save);
|
|
1224
|
+
|
|
1225
|
+
return result;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
/**
|
|
1229
|
+
* Smoothly animate mDraggingPane to the target X position within its range.
|
|
1230
|
+
*
|
|
1231
|
+
* @param slideOffset position to animate to
|
|
1232
|
+
* @param velocity initial velocity in case of fling, or 0.
|
|
1233
|
+
*/
|
|
1234
|
+
boolean smoothSlideTo(float slideOffset, int velocity) {
|
|
1235
|
+
if (!isEnabled() || mSlideableView == null) {
|
|
1236
|
+
// Nothing to do.
|
|
1237
|
+
return false;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
int panelTop = computePanelTopPosition(slideOffset);
|
|
1241
|
+
if (mDragHelper.smoothSlideViewTo(mSlideableView, mSlideableView.getLeft(), panelTop)) {
|
|
1242
|
+
setAllChildrenVisible();
|
|
1243
|
+
ViewCompat.postInvalidateOnAnimation(this);
|
|
1244
|
+
return true;
|
|
1245
|
+
}
|
|
1246
|
+
return false;
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
@Override
|
|
1250
|
+
public void computeScroll() {
|
|
1251
|
+
if (mDragHelper != null && mDragHelper.continueSettling(true)) {
|
|
1252
|
+
if (!isEnabled()) {
|
|
1253
|
+
mDragHelper.abort();
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
ViewCompat.postInvalidateOnAnimation(this);
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
@Override
|
|
1262
|
+
public void draw(Canvas c) {
|
|
1263
|
+
super.draw(c);
|
|
1264
|
+
|
|
1265
|
+
// draw the shadow
|
|
1266
|
+
if (mShadowDrawable != null) {
|
|
1267
|
+
final int right = mSlideableView.getRight();
|
|
1268
|
+
final int top;
|
|
1269
|
+
final int bottom;
|
|
1270
|
+
if (mIsSlidingUp) {
|
|
1271
|
+
top = mSlideableView.getTop() - mShadowHeight;
|
|
1272
|
+
bottom = mSlideableView.getTop();
|
|
1273
|
+
} else {
|
|
1274
|
+
top = mSlideableView.getBottom();
|
|
1275
|
+
bottom = mSlideableView.getBottom() + mShadowHeight;
|
|
1276
|
+
}
|
|
1277
|
+
final int left = mSlideableView.getLeft();
|
|
1278
|
+
mShadowDrawable.setBounds(left, top, right, bottom);
|
|
1279
|
+
mShadowDrawable.draw(c);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
/**
|
|
1284
|
+
* Tests scrollability within child views of v given a delta of dx.
|
|
1285
|
+
*
|
|
1286
|
+
* @param v View to test for horizontal scrollability
|
|
1287
|
+
* @param checkV Whether the view v passed should itself be checked for scrollability (true),
|
|
1288
|
+
* or just its children (false).
|
|
1289
|
+
* @param dx Delta scrolled in pixels
|
|
1290
|
+
* @param x X coordinate of the active touch point
|
|
1291
|
+
* @param y Y coordinate of the active touch point
|
|
1292
|
+
* @return true if child views of v can be scrolled by delta of dx.
|
|
1293
|
+
*/
|
|
1294
|
+
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
|
|
1295
|
+
if (v instanceof ViewGroup) {
|
|
1296
|
+
final ViewGroup group = (ViewGroup) v;
|
|
1297
|
+
final int scrollX = v.getScrollX();
|
|
1298
|
+
final int scrollY = v.getScrollY();
|
|
1299
|
+
final int count = group.getChildCount();
|
|
1300
|
+
// Count backwards - let topmost views consume scroll distance first.
|
|
1301
|
+
for (int i = count - 1; i >= 0; i--) {
|
|
1302
|
+
final View child = group.getChildAt(i);
|
|
1303
|
+
if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
|
|
1304
|
+
y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
|
|
1305
|
+
canScroll(child, true, dx, x + scrollX - child.getLeft(),
|
|
1306
|
+
y + scrollY - child.getTop())) {
|
|
1307
|
+
return true;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
return checkV && ViewCompat.canScrollHorizontally(v, -dx);
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
|
|
1315
|
+
@Override
|
|
1316
|
+
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
|
|
1317
|
+
return new LayoutParams();
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
@Override
|
|
1321
|
+
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
|
|
1322
|
+
return p instanceof MarginLayoutParams
|
|
1323
|
+
? new LayoutParams((MarginLayoutParams) p)
|
|
1324
|
+
: new LayoutParams(p);
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
@Override
|
|
1328
|
+
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
|
|
1329
|
+
return p instanceof LayoutParams && super.checkLayoutParams(p);
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
@Override
|
|
1333
|
+
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
|
|
1334
|
+
return new LayoutParams(getContext(), attrs);
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
@Override
|
|
1338
|
+
public Parcelable onSaveInstanceState() {
|
|
1339
|
+
Parcelable superState = super.onSaveInstanceState();
|
|
1340
|
+
|
|
1341
|
+
SavedState ss = new SavedState(superState);
|
|
1342
|
+
if (mSlideState != PanelState.DRAGGING) {
|
|
1343
|
+
ss.mSlideState = mSlideState;
|
|
1344
|
+
} else {
|
|
1345
|
+
ss.mSlideState = mLastNotDraggingSlideState;
|
|
1346
|
+
}
|
|
1347
|
+
return ss;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
@Override
|
|
1351
|
+
public void onRestoreInstanceState(Parcelable state) {
|
|
1352
|
+
SavedState ss = (SavedState) state;
|
|
1353
|
+
super.onRestoreInstanceState(ss.getSuperState());
|
|
1354
|
+
mSlideState = ss.mSlideState != null ? ss.mSlideState : DEFAULT_SLIDE_STATE;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
private class DragHelperCallback extends ViewDragHelper.Callback {
|
|
1358
|
+
|
|
1359
|
+
@Override
|
|
1360
|
+
public boolean tryCaptureView(View child, int pointerId) {
|
|
1361
|
+
if (mIsUnableToDrag) {
|
|
1362
|
+
return false;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
return child == mSlideableView;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
@Override
|
|
1369
|
+
public void onViewDragStateChanged(int state) {
|
|
1370
|
+
if (mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE) {
|
|
1371
|
+
mSlideOffset = computeSlideOffset(mSlideableView.getTop());
|
|
1372
|
+
applyParallaxForCurrentSlideOffset();
|
|
1373
|
+
|
|
1374
|
+
if (mSlideOffset == 1) {
|
|
1375
|
+
if (mSlideState != PanelState.EXPANDED) {
|
|
1376
|
+
updateObscuredViewVisibility();
|
|
1377
|
+
mSlideState = PanelState.EXPANDED;
|
|
1378
|
+
dispatchOnPanelExpanded(mSlideableView);
|
|
1379
|
+
}
|
|
1380
|
+
} else if (mSlideOffset == 0) {
|
|
1381
|
+
if (mSlideState != PanelState.COLLAPSED) {
|
|
1382
|
+
mSlideState = PanelState.COLLAPSED;
|
|
1383
|
+
dispatchOnPanelCollapsed(mSlideableView);
|
|
1384
|
+
}
|
|
1385
|
+
} else if (mSlideOffset < 0) {
|
|
1386
|
+
mSlideState = PanelState.HIDDEN;
|
|
1387
|
+
mSlideableView.setVisibility(View.INVISIBLE);
|
|
1388
|
+
dispatchOnPanelHidden(mSlideableView);
|
|
1389
|
+
} else if (mSlideState != PanelState.ANCHORED) {
|
|
1390
|
+
updateObscuredViewVisibility();
|
|
1391
|
+
mSlideState = PanelState.ANCHORED;
|
|
1392
|
+
dispatchOnPanelAnchored(mSlideableView);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
@Override
|
|
1398
|
+
public void onViewCaptured(View capturedChild, int activePointerId) {
|
|
1399
|
+
setAllChildrenVisible();
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
@Override
|
|
1403
|
+
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
|
|
1404
|
+
onPanelDragged(top);
|
|
1405
|
+
invalidate();
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
@Override
|
|
1409
|
+
public void onViewReleased(View releasedChild, float xvel, float yvel) {
|
|
1410
|
+
int target = 0;
|
|
1411
|
+
|
|
1412
|
+
// direction is always positive if we are sliding in the expanded direction
|
|
1413
|
+
float direction = mIsSlidingUp ? -yvel : yvel;
|
|
1414
|
+
|
|
1415
|
+
if (direction > 0 && mSlideOffset <= mAnchorPoint) {
|
|
1416
|
+
// swipe up -> expand and stop at anchor point
|
|
1417
|
+
target = computePanelTopPosition(mAnchorPoint);
|
|
1418
|
+
} else if (direction > 0 && mSlideOffset > mAnchorPoint) {
|
|
1419
|
+
// swipe up past anchor -> expand
|
|
1420
|
+
target = computePanelTopPosition(1.0f);
|
|
1421
|
+
} else if (direction < 0 && mSlideOffset >= mAnchorPoint) {
|
|
1422
|
+
// swipe down -> collapse and stop at anchor point
|
|
1423
|
+
target = computePanelTopPosition(mAnchorPoint);
|
|
1424
|
+
} else if (direction < 0 && mSlideOffset < mAnchorPoint) {
|
|
1425
|
+
// swipe down past anchor -> collapse
|
|
1426
|
+
target = computePanelTopPosition(0.0f);
|
|
1427
|
+
} else if (mSlideOffset >= (1.f + mAnchorPoint) / 2) {
|
|
1428
|
+
// zero velocity, and far enough from anchor point => expand to the top
|
|
1429
|
+
target = computePanelTopPosition(1.0f);
|
|
1430
|
+
} else if (mSlideOffset >= mAnchorPoint / 2) {
|
|
1431
|
+
// zero velocity, and close enough to anchor point => go to anchor
|
|
1432
|
+
target = computePanelTopPosition(mAnchorPoint);
|
|
1433
|
+
} else {
|
|
1434
|
+
// settle at the bottom
|
|
1435
|
+
target = computePanelTopPosition(0.0f);
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), target);
|
|
1439
|
+
invalidate();
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
@Override
|
|
1443
|
+
public int getViewVerticalDragRange(View child) {
|
|
1444
|
+
return mSlideRange;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
@Override
|
|
1448
|
+
public int clampViewPositionVertical(View child, int top, int dy) {
|
|
1449
|
+
final int collapsedTop = computePanelTopPosition(0.f);
|
|
1450
|
+
final int expandedTop = computePanelTopPosition(1.0f);
|
|
1451
|
+
if (mIsSlidingUp) {
|
|
1452
|
+
return Math.min(Math.max(top, expandedTop), collapsedTop);
|
|
1453
|
+
} else {
|
|
1454
|
+
return Math.min(Math.max(top, collapsedTop), expandedTop);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
public static class LayoutParams extends MarginLayoutParams {
|
|
1460
|
+
private static final int[] ATTRS = new int[]{
|
|
1461
|
+
android.R.attr.layout_weight
|
|
1462
|
+
};
|
|
1463
|
+
|
|
1464
|
+
public LayoutParams() {
|
|
1465
|
+
super(MATCH_PARENT, MATCH_PARENT);
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
public LayoutParams(int width, int height) {
|
|
1469
|
+
super(width, height);
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
public LayoutParams(ViewGroup.LayoutParams source) {
|
|
1473
|
+
super(source);
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
public LayoutParams(MarginLayoutParams source) {
|
|
1477
|
+
super(source);
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
public LayoutParams(LayoutParams source) {
|
|
1481
|
+
super(source);
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
public LayoutParams(Context c, AttributeSet attrs) {
|
|
1485
|
+
super(c, attrs);
|
|
1486
|
+
|
|
1487
|
+
final TypedArray a = c.obtainStyledAttributes(attrs, ATTRS);
|
|
1488
|
+
a.recycle();
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
static class SavedState extends BaseSavedState {
|
|
1493
|
+
PanelState mSlideState;
|
|
1494
|
+
|
|
1495
|
+
SavedState(Parcelable superState) {
|
|
1496
|
+
super(superState);
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
private SavedState(Parcel in) {
|
|
1500
|
+
super(in);
|
|
1501
|
+
try {
|
|
1502
|
+
mSlideState = Enum.valueOf(PanelState.class, in.readString());
|
|
1503
|
+
} catch (IllegalArgumentException e) {
|
|
1504
|
+
mSlideState = PanelState.COLLAPSED;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
@Override
|
|
1509
|
+
public void writeToParcel(Parcel out, int flags) {
|
|
1510
|
+
super.writeToParcel(out, flags);
|
|
1511
|
+
out.writeString(mSlideState.toString());
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
public static final Creator<SavedState> CREATOR =
|
|
1515
|
+
new Creator<SavedState>() {
|
|
1516
|
+
@Override
|
|
1517
|
+
public SavedState createFromParcel(Parcel in) {
|
|
1518
|
+
return new SavedState(in);
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
@Override
|
|
1522
|
+
public SavedState[] newArray(int size) {
|
|
1523
|
+
return new SavedState[size];
|
|
1524
|
+
}
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1527
|
+
}
|