Blame view

footsafety/app/src/main/java/etelligens/com/foodsafety/utils/SlidingLayout.java 9.96 KB
f7a13682   “wangming”   项目初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
  package etelligens.com.foodsafety.utils;
  
  import android.content.Context;
  import android.os.Handler;
  import android.util.AttributeSet;
  import android.view.MotionEvent;
  import android.view.View;
  import android.view.animation.Interpolator;
  import android.widget.LinearLayout;
  import android.widget.Scroller;
  
  public class SlidingLayout extends LinearLayout {
  
      // Duration of sliding animation, in miliseconds
      private static final int SLIDING_DURATION = 400;
  
      // Query Scroller every 16 miliseconds
      private static final int QUERY_INTERVAL = 16;
  
      // Sliding width
      int sldingLayoutWidth;
  
      // Sliding menu
      private View menu;
  
      // Main content
      private View content;
  
      // menu does not occupy some right space
      // This should be updated correctly later in onMeasure
      private static int menuRightMargin = 0;
  
      // The state of menu
      private enum MenuState {
          HIDING,
          HIDDEN,
          SHOWING,
          SHOWN,
      }
  
      // content will be layouted based on this X offset
      // Normally, contentXOffset = menu.getLayoutParams().width = this.getWidth - menuRightMargin
      private int contentXOffset;
  
      // menu is hidden when initializing
      private MenuState currentMenuState = MenuState.HIDDEN;
  
      // Scroller is used to facilitate animation
      private Scroller menuScroller = new Scroller(this.getContext(),
              new EaseInInterpolator());
  
      // Used to query Scroller about scrolling position
      // Note: The 3rd paramter to startScroll is the distance
      private Runnable menuRunnable = new MenuRunnable();
      private Handler menuHandler = new Handler();
  
      // Previous touch position
      int prevX = 0;
  
      // Is user dragging the content
      boolean isDragging = false;
  
      // Used to facilitate ACTION_UP
      int lastDiffX = 0;
  
  
      public SlidingLayout(Context context, AttributeSet attrs) {
          super(context, attrs);
      }
  
      public SlidingLayout(Context context) {
          super(context);
      }
  
      // Overriding LinearLayout core methods
      // Ask all children to measure themselves and compute the measurement of this
      // layout based on the children
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  
          sldingLayoutWidth = MeasureSpec.getSize(widthMeasureSpec);
          //Sliding menu will take 85% screen size when completely shown
          menuRightMargin = sldingLayoutWidth * 30/ 100;
      }
  
      // This is called when SlidingLayout is attached to window
      // At this point it has a Surface and will start drawing.
      // Note that this function is guaranteed to be called before onDraw
      @Override
      protected void onAttachedToWindow() {
          super.onAttachedToWindow();
  
          // Get our 2 children views
          menu = this.getChildAt(0);
          content = this.getChildAt(1);
  
          // Attach View.OnTouchListener
          content.setOnTouchListener((v, event) -> onContentTouch(v, event));
  
          menu.setVisibility(View.GONE);
      }
  
      @Override
      protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
          // True if SlidingLayout's size and position has changed
          // If true, calculate child views size
          if (changed) {
              // Note: LayoutParams are used by views to tell their parents how they want to be laid out
              // content View occupies the full height and width
              LayoutParams contentLayoutParams = (LayoutParams) content.getLayoutParams();
              contentLayoutParams.height = this.getHeight();
              contentLayoutParams.width = this.getWidth();
  
              // menu View occupies the full height, but certain width
              LayoutParams menuLayoutParams = (LayoutParams) menu.getLayoutParams();
              menuLayoutParams.height = this.getHeight();
              menuLayoutParams.width = this.getWidth() - menuRightMargin;
          }
  
          // Layout the child views
          menu.layout(left, top, right - menuRightMargin, bottom);
          content.layout(left + contentXOffset, top, right + contentXOffset, bottom);
      }
  
      // Custom methods for SlidingLayout
      // Used to show/hide menu accordingly
      public void toggleMenu() {
          // Do nothing if sliding is in progress
          if (currentMenuState == MenuState.HIDING || currentMenuState == MenuState.SHOWING)
              return;
  
          switch (currentMenuState) {
              case HIDDEN:
                  currentMenuState = MenuState.SHOWING;
                  menu.setVisibility(View.VISIBLE);
                  menuScroller.startScroll(0, 0, menu.getLayoutParams().width,
                          0, SLIDING_DURATION);
                  break;
              case SHOWN:
                  currentMenuState = MenuState.HIDING;
                  menuScroller.startScroll(contentXOffset, 0, -contentXOffset,
                          0, SLIDING_DURATION);
                  break;
              default:
                  break;
          }
  
          // Begin querying
          menuHandler.postDelayed(menuRunnable, QUERY_INTERVAL);
  
          // Invalite this whole SlidingLayout, causing onLayout() to be called
          this.invalidate();
      }
  
      // Query Scroller
      protected class MenuRunnable implements Runnable {
          @Override
          public void run() {
              boolean isScrolling = menuScroller.computeScrollOffset();
              adjustContentPosition(isScrolling);
          }
      }
  
      // Adjust content View position to match sliding animation
      private void adjustContentPosition(boolean isScrolling) {
          int scrollerXOffset = menuScroller.getCurrX();
  
          // Translate content View accordingly
          content.offsetLeftAndRight(scrollerXOffset - contentXOffset);
  
          contentXOffset = scrollerXOffset;
  
          // Invalite this whole Slidinglayout, causing onLayout() to be called
          this.invalidate();
  
          // Check if animation is in progress
          if (isScrolling)
              menuHandler.postDelayed(menuRunnable, QUERY_INTERVAL);
          else
              this.onMenuSlidingComplete();
      }
  
      // Called when sliding is complete
      private void onMenuSlidingComplete() {
          switch (currentMenuState) {
              case SHOWING:
                  currentMenuState = MenuState.SHOWN;
                  break;
              case HIDING:
                  currentMenuState = MenuState.HIDDEN;
                  menu.setVisibility(View.GONE);
                  break;
              default:
                  return;
          }
      }
  
      // Make scrolling more natural. Move more quickly at the end
      protected class EaseInInterpolator implements Interpolator {
          @Override
          public float getInterpolation(float t) {
              return (float) Math.pow(t - 1, 5) + 1;
          }
      }
  
      // Is menu completely shown
      public boolean isMenuShown() {
          return currentMenuState == MenuState.SHOWN;
      }
  
      // Handle touch event on content View
      public boolean onContentTouch(View v, MotionEvent event) {
          // Do nothing if sliding is in progress
          if (currentMenuState == MenuState.HIDING || currentMenuState == MenuState.SHOWING)
              return false;
  
          // getRawX returns X touch point corresponding to screen
          // getX sometimes returns screen X, sometimes returns content View X
          int curX = (int) event.getRawX();
          int diffX = 0;
  
          switch (event.getAction()) {
              case MotionEvent.ACTION_DOWN:
  
                  prevX = curX;
                  return true;
  
              case MotionEvent.ACTION_MOVE:
                  // Set menu to Visible when user start dragging the content View
                  if (!isDragging) {
                      isDragging = true;
                      menu.setVisibility(View.VISIBLE);
                  }
  
                  // How far we have moved since the last position
                  diffX = curX - prevX;
  
                  // Prevent user from dragging beyond border
                  if (contentXOffset + diffX <= 0) {
                      // Don't allow dragging beyond left border
                      // Use diffX will make content cross the border, so only translate by -contentXOffset
                      diffX = -contentXOffset;
                  } else if (contentXOffset + diffX > sldingLayoutWidth - menuRightMargin) {
                      // Don't allow dragging beyond menu width
                      diffX = sldingLayoutWidth - menuRightMargin - contentXOffset;
                  }
  
                  // Translate content View accordingly
                  content.offsetLeftAndRight(diffX);
  
                  contentXOffset += diffX;
  
                  // Invalite this whole Slidinglayout, causing onLayout() to be called
                  this.invalidate();
  
                  prevX = curX;
                  lastDiffX = diffX;
                  return true;
  
              case MotionEvent.ACTION_UP:
                  // Start scrolling
                  // Remember that when content has a chance to cross left border, lastDiffX is set to 0
                  if (lastDiffX > 0) {
                      // User wants to show menu
                      currentMenuState = MenuState.SHOWING;
  
                      // Start scrolling from contentXOffset
                      menuScroller.startScroll(contentXOffset, 0, menu.getLayoutParams().width - contentXOffset,
                              0, SLIDING_DURATION);
                  } else if (lastDiffX < 0) {
                      // User wants to hide menu
                      currentMenuState = MenuState.HIDING;
                      menuScroller.startScroll(contentXOffset, 0, -contentXOffset,
                              0, SLIDING_DURATION);
                  }
  
                  // Begin querying
                  menuHandler.postDelayed(menuRunnable, QUERY_INTERVAL);
  
                  // Invalite this whole Slidinglayout, causing onLayout() to be called
                  this.invalidate();
  
                  // Done dragging
                  isDragging = false;
                  prevX = 0;
                  lastDiffX = 0;
                  return true;
  
              default:
                  break;
          }
          return false;
      }
  }