amp;& pointerId == mActivePointerId) {
|
|
1264
|
+ // Try to find another pointer that's still holding on to
|
|
|
1265
|
+ // the captured view.
|
|
|
1266
|
+ int newActivePointer = INVALID_POINTER;
|
|
|
1267
|
+ final int pointerCount = MotionEventCompat.getPointerCount(ev);
|
|
|
1268
|
+ for (int i = 0; i < pointerCount; i++) {
|
|
|
1269
|
+ final int id = MotionEventCompat.getPointerId(ev, i);
|
|
|
1270
|
+ if (id == mActivePointerId) {
|
|
|
1271
|
+ // This one's going away, skip.
|
|
|
1272
|
+ continue;
|
|
|
1273
|
+ }
|
|
|
1274
|
+
|
|
|
1275
|
+ final float x = MotionEventCompat.getX(ev, i);
|
|
|
1276
|
+ final float y = MotionEventCompat.getY(ev, i);
|
|
|
1277
|
+ if (findTopChildUnder((int) x, (int) y) == mCapturedView
|
|
|
1278
|
+ && tryCaptureViewForDrag(mCapturedView, id)) {
|
|
|
1279
|
+ newActivePointer = mActivePointerId;
|
|
|
1280
|
+ break;
|
|
|
1281
|
+ }
|
|
|
1282
|
+ }
|
|
|
1283
|
+
|
|
|
1284
|
+ if (newActivePointer == INVALID_POINTER) {
|
|
|
1285
|
+ // We didn't find another pointer still touching the
|
|
|
1286
|
+ // view, release it.
|
|
|
1287
|
+ releaseViewForPointerUp();
|
|
|
1288
|
+ }
|
|
|
1289
|
+ }
|
|
|
1290
|
+ clearMotionHistory(pointerId);
|
|
|
1291
|
+ break;
|
|
|
1292
|
+ }
|
|
|
1293
|
+
|
|
|
1294
|
+ case MotionEvent.ACTION_UP: {
|
|
|
1295
|
+ if (mDragState == STATE_DRAGGING) {
|
|
|
1296
|
+ releaseViewForPointerUp();
|
|
|
1297
|
+ }
|
|
|
1298
|
+ cancel();
|
|
|
1299
|
+ break;
|
|
|
1300
|
+ }
|
|
|
1301
|
+
|
|
|
1302
|
+ case MotionEvent.ACTION_CANCEL: {
|
|
|
1303
|
+ if (mDragState == STATE_DRAGGING) {
|
|
|
1304
|
+ dispatchViewReleased(0, 0);
|
|
|
1305
|
+ }
|
|
|
1306
|
+ cancel();
|
|
|
1307
|
+ break;
|
|
|
1308
|
+ }
|
|
|
1309
|
+ }
|
|
|
1310
|
+ }
|
|
|
1311
|
+
|
|
|
1312
|
+ private void reportNewEdgeDrags(float dx, float dy, int pointerId) {
|
|
|
1313
|
+ int dragsStarted = 0;
|
|
|
1314
|
+ if (checkNewEdgeDrag(dx, dy, pointerId, EDGE_LEFT)) {
|
|
|
1315
|
+ dragsStarted |= EDGE_LEFT;
|
|
|
1316
|
+ }
|
|
|
1317
|
+ if (checkNewEdgeDrag(dy, dx, pointerId, EDGE_TOP)) {
|
|
|
1318
|
+ dragsStarted |= EDGE_TOP;
|
|
|
1319
|
+ }
|
|
|
1320
|
+ if (checkNewEdgeDrag(dx, dy, pointerId, EDGE_RIGHT)) {
|
|
|
1321
|
+ dragsStarted |= EDGE_RIGHT;
|
|
|
1322
|
+ }
|
|
|
1323
|
+ if (checkNewEdgeDrag(dy, dx, pointerId, EDGE_BOTTOM)) {
|
|
|
1324
|
+ dragsStarted |= EDGE_BOTTOM;
|
|
|
1325
|
+ }
|
|
|
1326
|
+
|
|
|
1327
|
+ if (dragsStarted != 0) {
|
|
|
1328
|
+ mEdgeDragsInProgress[pointerId] |= dragsStarted;
|
|
|
1329
|
+ mCallback.onEdgeDragStarted(dragsStarted, pointerId);
|
|
|
1330
|
+ }
|
|
|
1331
|
+ }
|
|
|
1332
|
+
|
|
|
1333
|
+ private boolean checkNewEdgeDrag(float delta, float odelta, int pointerId, int edge) {
|
|
|
1334
|
+ final float absDelta = Math.abs(delta);
|
|
|
1335
|
+ final float absODelta = Math.abs(odelta);
|
|
|
1336
|
+
|
|
|
1337
|
+ if ((mInitialEdgeTouched[pointerId] & edge) != edge || (mTrackingEdges & edge) == 0
|
|
|
1338
|
+ || (mEdgeDragsLocked[pointerId] & edge) == edge
|
|
|
1339
|
+ || (mEdgeDragsInProgress[pointerId] & edge) == edge
|
|
|
1340
|
+ || (absDelta <= mTouchSlop && absODelta <= mTouchSlop)) {
|
|
|
1341
|
+ return false;
|
|
|
1342
|
+ }
|
|
|
1343
|
+ if (absDelta < absODelta * 0.5f && mCallback.onEdgeLock(edge)) {
|
|
|
1344
|
+ mEdgeDragsLocked[pointerId] |= edge;
|
|
|
1345
|
+ return false;
|
|
|
1346
|
+ }
|
|
|
1347
|
+ return (mEdgeDragsInProgress[pointerId] & edge) == 0 && absDelta > mTouchSlop;
|
|
|
1348
|
+ }
|
|
|
1349
|
+
|
|
|
1350
|
+ /**
|
|
|
1351
|
+ * Check if we've crossed a reasonable touch slop for the given child view.
|
|
|
1352
|
+ * If the child cannot be dragged along the horizontal or vertical axis,
|
|
|
1353
|
+ * motion along that axis will not count toward the slop check.
|
|
|
1354
|
+ *
|
|
|
1355
|
+ * @param child Child to check
|
|
|
1356
|
+ * @param dx Motion since initial position along X axis
|
|
|
1357
|
+ * @param dy Motion since initial position along Y axis
|
|
|
1358
|
+ * @return true if the touch slop has been crossed
|
|
|
1359
|
+ */
|
|
|
1360
|
+ private boolean checkTouchSlop(View child, float dx, float dy) {
|
|
|
1361
|
+ if (child == null) {
|
|
|
1362
|
+ return false;
|
|
|
1363
|
+ }
|
|
|
1364
|
+ final boolean checkHorizontal = mCallback.getViewHorizontalDragRange(child) > 0;
|
|
|
1365
|
+ final boolean checkVertical = mCallback.getViewVerticalDragRange(child) > 0;
|
|
|
1366
|
+
|
|
|
1367
|
+ if (checkHorizontal && checkVertical) {
|
|
|
1368
|
+ return dx * dx + dy * dy > mTouchSlop * mTouchSlop;
|
|
|
1369
|
+ } else if (checkHorizontal) {
|
|
|
1370
|
+ return Math.abs(dx) > mTouchSlop;
|
|
|
1371
|
+ } else if (checkVertical) {
|
|
|
1372
|
+ return Math.abs(dy) > mTouchSlop;
|
|
|
1373
|
+ }
|
|
|
1374
|
+ return false;
|
|
|
1375
|
+ }
|
|
|
1376
|
+
|
|
|
1377
|
+ /**
|
|
|
1378
|
+ * Check if any pointer tracked in the current gesture has crossed the
|
|
|
1379
|
+ * required slop threshold.
|
|
|
1380
|
+ * <p>
|
|
|
1381
|
+ * This depends on internal state populated by
|
|
|
1382
|
+ * {@link #shouldInterceptTouchEvent(MotionEvent)} or
|
|
|
1383
|
+ * {@link #processTouchEvent(MotionEvent)}. You should only
|
|
|
1384
|
+ * rely on the results of this method after all currently available touch
|
|
|
1385
|
+ * data has been provided to one of these two methods.
|
|
|
1386
|
+ * </p>
|
|
|
1387
|
+ *
|
|
|
1388
|
+ * @param directions Combination of direction flags, see
|
|
|
1389
|
+ * {@link #DIRECTION_HORIZONTAL}, {@link #DIRECTION_VERTICAL},
|
|
|
1390
|
+ * {@link #DIRECTION_ALL}
|
|
|
1391
|
+ * @return true if the slop threshold has been crossed, false otherwise
|
|
|
1392
|
+ */
|
|
|
1393
|
+ public boolean checkTouchSlop(int directions) {
|
|
|
1394
|
+ final int count = mInitialMotionX.length;
|
|
|
1395
|
+ for (int i = 0; i < count; i++) {
|
|
|
1396
|
+ if (checkTouchSlop(directions, i)) {
|
|
|
1397
|
+ return true;
|
|
|
1398
|
+ }
|
|
|
1399
|
+ }
|
|
|
1400
|
+ return false;
|
|
|
1401
|
+ }
|
|
|
1402
|
+
|
|
|
1403
|
+ /**
|
|
|
1404
|
+ * Check if the specified pointer tracked in the current gesture has crossed
|
|
|
1405
|
+ * the required slop threshold.
|
|
|
1406
|
+ * <p>
|
|
|
1407
|
+ * This depends on internal state populated by
|
|
|
1408
|
+ * {@link #shouldInterceptTouchEvent(MotionEvent)} or
|
|
|
1409
|
+ * {@link #processTouchEvent(MotionEvent)}. You should only
|
|
|
1410
|
+ * rely on the results of this method after all currently available touch
|
|
|
1411
|
+ * data has been provided to one of these two methods.
|
|
|
1412
|
+ * </p>
|
|
|
1413
|
+ *
|
|
|
1414
|
+ * @param directions Combination of direction flags, see
|
|
|
1415
|
+ * {@link #DIRECTION_HORIZONTAL}, {@link #DIRECTION_VERTICAL},
|
|
|
1416
|
+ * {@link #DIRECTION_ALL}
|
|
|
1417
|
+ * @param pointerId ID of the pointer to slop check as specified by
|
|
|
1418
|
+ * MotionEvent
|
|
|
1419
|
+ * @return true if the slop threshold has been crossed, false otherwise
|
|
|
1420
|
+ */
|
|
|
1421
|
+ public boolean checkTouchSlop(int directions, int pointerId) {
|
|
|
1422
|
+ if (!isPointerDown(pointerId)) {
|
|
|
1423
|
+ return false;
|
|
|
1424
|
+ }
|
|
|
1425
|
+
|
|
|
1426
|
+ final boolean checkHorizontal = (directions & DIRECTION_HORIZONTAL) == DIRECTION_HORIZONTAL;
|
|
|
1427
|
+ final boolean checkVertical = (directions & DIRECTION_VERTICAL) == DIRECTION_VERTICAL;
|
|
|
1428
|
+
|
|
|
1429
|
+ final float dx = mLastMotionX[pointerId] - mInitialMotionX[pointerId];
|
|
|
1430
|
+ final float dy = mLastMotionY[pointerId] - mInitialMotionY[pointerId];
|
|
|
1431
|
+
|
|
|
1432
|
+ if (checkHorizontal && checkVertical) {
|
|
|
1433
|
+ return dx * dx + dy * dy > mTouchSlop * mTouchSlop;
|
|
|
1434
|
+ } else if (checkHorizontal) {
|
|
|
1435
|
+ return Math.abs(dx) > mTouchSlop;
|
|
|
1436
|
+ } else if (checkVertical) {
|
|
|
1437
|
+ return Math.abs(dy) > mTouchSlop;
|
|
|
1438
|
+ }
|
|
|
1439
|
+ return false;
|
|
|
1440
|
+ }
|
|
|
1441
|
+
|
|
|
1442
|
+ /**
|
|
|
1443
|
+ * Check if any of the edges specified were initially touched in the
|
|
|
1444
|
+ * currently active gesture. If there is no currently active gesture this
|
|
|
1445
|
+ * method will return false.
|
|
|
1446
|
+ *
|
|
|
1447
|
+ * @param edges Edges to check for an initial edge touch. See
|
|
|
1448
|
+ * {@link #EDGE_LEFT}, {@link #EDGE_TOP}, {@link #EDGE_RIGHT},
|
|
|
1449
|
+ * {@link #EDGE_BOTTOM} and {@link #EDGE_ALL}
|
|
|
1450
|
+ * @return true if any of the edges specified were initially touched in the
|
|
|
1451
|
+ * current gesture
|
|
|
1452
|
+ */
|
|
|
1453
|
+ public boolean isEdgeTouched(int edges) {
|
|
|
1454
|
+ final int count = mInitialEdgeTouched.length;
|
|
|
1455
|
+ for (int i = 0; i < count; i++) {
|
|
|
1456
|
+ if (isEdgeTouched(edges, i)) {
|
|
|
1457
|
+ return true;
|
|
|
1458
|
+ }
|
|
|
1459
|
+ }
|
|
|
1460
|
+ return false;
|
|
|
1461
|
+ }
|
|
|
1462
|
+
|
|
|
1463
|
+ /**
|
|
|
1464
|
+ * Check if any of the edges specified were initially touched by the pointer
|
|
|
1465
|
+ * with the specified ID. If there is no currently active gesture or if
|
|
|
1466
|
+ * there is no pointer with the given ID currently down this method will
|
|
|
1467
|
+ * return false.
|
|
|
1468
|
+ *
|
|
|
1469
|
+ * @param edges Edges to check for an initial edge touch. See
|
|
|
1470
|
+ * {@link #EDGE_LEFT}, {@link #EDGE_TOP}, {@link #EDGE_RIGHT},
|
|
|
1471
|
+ * {@link #EDGE_BOTTOM} and {@link #EDGE_ALL}
|
|
|
1472
|
+ * @return true if any of the edges specified were initially touched in the
|
|
|
1473
|
+ * current gesture
|
|
|
1474
|
+ */
|
|
|
1475
|
+ public boolean isEdgeTouched(int edges, int pointerId) {
|
|
|
1476
|
+ return isPointerDown(pointerId) && (mInitialEdgeTouched[pointerId] & edges) != 0;
|
|
|
1477
|
+ }
|
|
|
1478
|
+
|
|
|
1479
|
+ private void releaseViewForPointerUp() {
|
|
|
1480
|
+ mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
|
|
|
1481
|
+ final float xvel = clampMag(
|
|
|
1482
|
+ VelocityTrackerCompat.getXVelocity(mVelocityTracker, mActivePointerId),
|
|
|
1483
|
+ mMinVelocity, mMaxVelocity);
|
|
|
1484
|
+ final float yvel = clampMag(
|
|
|
1485
|
+ VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId),
|
|
|
1486
|
+ mMinVelocity, mMaxVelocity);
|
|
|
1487
|
+ dispatchViewReleased(xvel, yvel);
|
|
|
1488
|
+ }
|
|
|
1489
|
+
|
|
|
1490
|
+ private void dragTo(int left, int top, int dx, int dy) {
|
|
|
1491
|
+ int clampedX = left;
|
|
|
1492
|
+ int clampedY = top;
|
|
|
1493
|
+ final int oldLeft = mCapturedView.getLeft();
|
|
|
1494
|
+ final int oldTop = mCapturedView.getTop();
|
|
|
1495
|
+ if (dx != 0) {
|
|
|
1496
|
+ clampedX = mCallback.clampViewPositionHorizontal(mCapturedView, left, dx);
|
|
|
1497
|
+ mCapturedView.offsetLeftAndRight(clampedX - oldLeft);
|
|
|
1498
|
+ }
|
|
|
1499
|
+ if (dy != 0) {
|
|
|
1500
|
+ clampedY = mCallback.clampViewPositionVertical(mCapturedView, top, dy);
|
|
|
1501
|
+ mCapturedView.offsetTopAndBottom(clampedY - oldTop);
|
|
|
1502
|
+ }
|
|
|
1503
|
+
|
|
|
1504
|
+ if (dx != 0 || dy != 0) {
|
|
|
1505
|
+ final int clampedDx = clampedX - oldLeft;
|
|
|
1506
|
+ final int clampedDy = clampedY - oldTop;
|
|
|
1507
|
+ mCallback
|
|
|
1508
|
+ .onViewPositionChanged(mCapturedView, clampedX, clampedY, clampedDx, clampedDy);
|
|
|
1509
|
+ }
|
|
|
1510
|
+ }
|
|
|
1511
|
+
|
|
|
1512
|
+ /**
|
|
|
1513
|
+ * Determine if the currently captured view is under the given point in the
|
|
|
1514
|
+ * parent view's coordinate system. If there is no captured view this method
|
|
|
1515
|
+ * will return false.
|
|
|
1516
|
+ *
|
|
|
1517
|
+ * @param x X position to test in the parent's coordinate system
|
|
|
1518
|
+ * @param y Y position to test in the parent's coordinate system
|
|
|
1519
|
+ * @return true if the captured view is under the given point, false
|
|
|
1520
|
+ * otherwise
|
|
|
1521
|
+ */
|
|
|
1522
|
+ public boolean isCapturedViewUnder(int x, int y) {
|
|
|
1523
|
+ return isViewUnder(mCapturedView, x, y);
|
|
|
1524
|
+ }
|
|
|
1525
|
+
|
|
|
1526
|
+ /**
|
|
|
1527
|
+ * Determine if the supplied view is under the given point in the parent
|
|
|
1528
|
+ * view's coordinate system.
|
|
|
1529
|
+ *
|
|
|
1530
|
+ * @param view Child view of the parent to hit test
|
|
|
1531
|
+ * @param x X position to test in the parent's coordinate system
|
|
|
1532
|
+ * @param y Y position to test in the parent's coordinate system
|
|
|
1533
|
+ * @return true if the supplied view is under the given point, false
|
|
|
1534
|
+ * otherwise
|
|
|
1535
|
+ */
|
|
|
1536
|
+ public boolean isViewUnder(View view, int x, int y) {
|
|
|
1537
|
+ if (view == null) {
|
|
|
1538
|
+ return false;
|
|
|
1539
|
+ }
|
|
|
1540
|
+ return x >= view.getLeft() && x < view.getRight() && y >= view.getTop()
|
|
|
1541
|
+ && y < view.getBottom();
|
|
|
1542
|
+ }
|
|
|
1543
|
+
|
|
|
1544
|
+ /**
|
|
|
1545
|
+ * Find the topmost child under the given point within the parent view's
|
|
|
1546
|
+ * coordinate system. The child order is determined using
|
|
|
1547
|
+ * {@link me.imid.swipebacklayout.lib.ViewDragHelper.Callback#getOrderedChildIndex(int)}
|
|
|
1548
|
+ * .
|
|
|
1549
|
+ *
|
|
|
1550
|
+ * @param x X position to test in the parent's coordinate system
|
|
|
1551
|
+ * @param y Y position to test in the parent's coordinate system
|
|
|
1552
|
+ * @return The topmost child view under (x, y) or null if none found.
|
|
|
1553
|
+ */
|
|
|
1554
|
+ public View findTopChildUnder(int x, int y) {
|
|
|
1555
|
+ final int childCount = mParentView.getChildCount();
|
|
|
1556
|
+ for (int i = childCount - 1; i >= 0; i--) {
|
|
|
1557
|
+ final View child = mParentView.getChildAt(mCallback.getOrderedChildIndex(i));
|
|
|
1558
|
+ if (x >= child.getLeft() && x < child.getRight() && y >= child.getTop()
|
|
|
1559
|
+ && y < child.getBottom()) {
|
|
|
1560
|
+ return child;
|
|
|
1561
|
+ }
|
|
|
1562
|
+ }
|
|
|
1563
|
+ return null;
|
|
|
1564
|
+ }
|
|
|
1565
|
+
|
|
|
1566
|
+ private int getEdgeTouched(int x, int y) {
|
|
|
1567
|
+ int result = 0;
|
|
|
1568
|
+
|
|
|
1569
|
+ if (x < mParentView.getLeft() + mEdgeSize)
|
|
|
1570
|
+ result = EDGE_LEFT;
|
|
|
1571
|
+ if (y < mParentView.getTop() + mEdgeSize)
|
|
|
1572
|
+ result = EDGE_TOP;
|
|
|
1573
|
+ if (x > mParentView.getRight() - mEdgeSize)
|
|
|
1574
|
+ result = EDGE_RIGHT;
|
|
|
1575
|
+ if (y > mParentView.getBottom() - mEdgeSize)
|
|
|
1576
|
+ result = EDGE_BOTTOM;
|
|
|
1577
|
+
|
|
|
1578
|
+ return result;
|
|
|
1579
|
+ }
|
|
|
1580
|
+}
|
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+<?xml version="1.0" encoding="utf-8"?>
|
|
|
2
|
+<com.android.views.swipebacklayout.SwipeBackLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
|
3
|
+ android:id="@+id/swipe"
|
|
|
4
|
+ android:layout_width="match_parent"
|
|
|
5
|
+ android:layout_height="match_parent" />
|
|
|
6
|
+
|
|
|
|
@@ -73,4 +73,18 @@
|
|
73
|
73
|
</attr>
|
|
74
|
74
|
</declare-styleable>
|
|
75
|
75
|
|
|
|
76
|
+ <declare-styleable name="SwipeBackLayout">
|
|
|
77
|
+ <attr name="edge_size" format="dimension"/>
|
|
|
78
|
+ <attr name="edge_flag">
|
|
|
79
|
+ <enum name="left" value="0" />
|
|
|
80
|
+ <enum name="right" value="1" />
|
|
|
81
|
+ <enum name="bottom" value="2" />
|
|
|
82
|
+ <enum name="all" value="3" />
|
|
|
83
|
+ </attr>
|
|
|
84
|
+ <attr name="shadow_left" format="reference"/>
|
|
|
85
|
+ <attr name="shadow_right" format="reference"/>
|
|
|
86
|
+ <attr name="shadow_bottom" format="reference"/>
|
|
|
87
|
+ </declare-styleable>
|
|
|
88
|
+
|
|
|
89
|
+ <attr name="SwipeBackLayoutStyle" format="reference"/>
|
|
76
|
90
|
</resources>
|
|
|
|
@@ -6,21 +6,12 @@
|
|
6
|
6
|
<item name="android:background">@color/line_bg</item>
|
|
7
|
7
|
<item name="android:layout_margin">2dp</item>
|
|
8
|
8
|
</style>
|
|
9
|
|
- <declare-styleable name="SwipeLayout">
|
|
10
|
|
- <attr name="drag_edge">
|
|
11
|
|
- <flag name="left" value="1" />
|
|
12
|
|
- <flag name="right" value="2" />
|
|
13
|
|
- <flag name="top" value="4" />
|
|
14
|
|
- <flag name="bottom" value="8" />
|
|
15
|
|
- </attr>
|
|
16
|
|
- <attr name="leftEdgeSwipeOffset" format="dimension" />
|
|
17
|
|
- <attr name="rightEdgeSwipeOffset" format="dimension" />
|
|
18
|
|
- <attr name="topEdgeSwipeOffset" format="dimension" />
|
|
19
|
|
- <attr name="bottomEdgeSwipeOffset" format="dimension" />
|
|
20
|
|
- <attr name="show_mode" format="enum">
|
|
21
|
|
- <enum name="lay_down" value="0" />
|
|
22
|
|
- <enum name="pull_out" value="1" />
|
|
23
|
|
- </attr>
|
|
24
|
|
- <attr name="clickToClose" format="boolean" />
|
|
25
|
|
- </declare-styleable>
|
|
|
9
|
+
|
|
|
10
|
+ <style name="SwipeBackLayout">
|
|
|
11
|
+ <item name="edge_size">50dip</item>
|
|
|
12
|
+ <item name="shadow_left">@drawable/shadow_left</item>
|
|
|
13
|
+ <item name="shadow_right">@drawable/shadow_right</item>
|
|
|
14
|
+ <item name="shadow_bottom">@drawable/shadow_bottom</item>
|
|
|
15
|
+ </style>
|
|
|
16
|
+
|
|
26
|
17
|
</resources>
|