() {
1014 | @Override
1015 | public SavedState createFromParcel(Parcel in, ClassLoader loader) {
1016 | return new SavedState(in, loader);
1017 | }
1018 |
1019 | @Override
1020 | public SavedState[] newArray(int size) {
1021 | return new SavedState[size];
1022 | }
1023 | });
1024 |
1025 | SavedState(Parcel in, ClassLoader loader) {
1026 | super(in);
1027 | if (loader == null) {
1028 | loader = getClass().getClassLoader();
1029 | } /* end of if */
1030 | position = in.readInt();
1031 | adapterState = in.readParcelable(loader);
1032 | this.loader = loader;
1033 | }
1034 | }
1035 |
1036 | @Override
1037 | public Parcelable onSaveInstanceState() {
1038 | Parcelable superState = super.onSaveInstanceState();
1039 | SavedState ss = new SavedState(superState);
1040 | ss.position = mCurItem;
1041 | if (mAdapter != null) {
1042 | ss.adapterState = mAdapter.saveState();
1043 | } /* end of if */
1044 | return ss;
1045 | }
1046 |
1047 | @Override
1048 | public void onRestoreInstanceState(Parcelable state) {
1049 | if (!(state instanceof SavedState)) {
1050 | super.onRestoreInstanceState(state);
1051 | return;
1052 | } /* end of if */
1053 |
1054 | SavedState ss = (SavedState) state;
1055 | super.onRestoreInstanceState(ss.getSuperState());
1056 |
1057 | if (mAdapter != null) {
1058 | mAdapter.restoreState(ss.adapterState, ss.loader);
1059 | setCurrentItemInternal(ss.position, false, true);
1060 | } else {
1061 | mRestoredCurItem = ss.position;
1062 | mRestoredAdapterState = ss.adapterState;
1063 | mRestoredClassLoader = ss.loader;
1064 | } /* end of if */
1065 | }
1066 |
1067 | @Override
1068 | public void addView(View child, int index, ViewGroup.LayoutParams params) {
1069 | if (!checkLayoutParams(params)) {
1070 | params = generateLayoutParams(params);
1071 | } /* end of if */
1072 | final LayoutParams lp = (LayoutParams) params;
1073 | lp.isDecor |= child instanceof Decor;
1074 | if (mInLayout) {
1075 | if (lp != null && lp.isDecor) {
1076 | throw new IllegalStateException("Cannot add pager decor view during layout");
1077 | } /* end of if */
1078 | addViewInLayout(child, index, params);
1079 | child.measure(mChildWidthMeasureSpec, mChildHeightMeasureSpec);
1080 | } else {
1081 | super.addView(child, index, params);
1082 | } /* end of if */
1083 |
1084 | if (USE_CACHE) {
1085 | if (child.getVisibility() != GONE) {
1086 | child.setDrawingCacheEnabled(mScrollingCacheEnabled);
1087 | } else {
1088 | child.setDrawingCacheEnabled(false);
1089 | } /* end of if */
1090 | } /* end of if */
1091 | }
1092 |
1093 | /**
1094 | * 鍙栧緱鐩墠瑭查爜鐨剓@link ItemInfo}
1095 | *
1096 | * @param child child {@link View} object
1097 | * @return return {@link ItemInfo} if is view from object, other return null
1098 | */
1099 | ItemInfo infoForChild(View child) {
1100 | for (int i = 0; i < mItems.size(); i++) {
1101 | ItemInfo ii = mItems.get(i);
1102 | if (mAdapter.isViewFromObject(child, ii.object)) {
1103 | return ii;
1104 | } /* end of if */
1105 | } /* end of for */
1106 | return null;
1107 | }
1108 |
1109 | ItemInfo infoForAnyChild(View child) {
1110 | ViewParent parent;
1111 | while ((parent = child.getParent()) != this) {
1112 | if (parent == null || !(parent instanceof View)) {
1113 | return null;
1114 | } /* end of if */
1115 | child = (View) parent;
1116 | } /* end of while */
1117 | return infoForChild(child);
1118 | }
1119 |
1120 | @Override
1121 | protected void onAttachedToWindow() {
1122 | super.onAttachedToWindow();
1123 | mFirstLayout = true;
1124 | }
1125 |
1126 | @Override
1127 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1128 | // For simple implementation, or internal size is always 0.
1129 | // We depend on the container to specify the layout size of
1130 | // our view. We can't really know what it is since we will be
1131 | // adding and removing different arbitrary views and do not
1132 | // want the layout to change as this happens.
1133 | setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),
1134 | getDefaultSize(0, heightMeasureSpec));
1135 |
1136 | // Children are just made to fill our space.
1137 | int childWidthSize = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
1138 | int childHeightSize = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
1139 |
1140 | /*
1141 | * Make sure all children have been properly measured. Decor views first.
1142 | * Right now we cheat and make this less complicated by assuming decor
1143 | * views won't intersect. We will pin to edges based on gravity.
1144 | */
1145 | int size = getChildCount();
1146 | for (int i = 0; i < size; ++i) {
1147 | final View child = getChildAt(i);
1148 | if (child.getVisibility() != GONE) {
1149 | final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1150 | if (lp != null && lp.isDecor) {
1151 | final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
1152 | final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
1153 | Log.d(TAG, "gravity: " + lp.gravity + " hgrav: " + hgrav + " vgrav: " + vgrav);
1154 | int widthMode = MeasureSpec.AT_MOST;
1155 | int heightMode = MeasureSpec.AT_MOST;
1156 | boolean consumeVertical = vgrav == Gravity.TOP || vgrav == Gravity.BOTTOM;
1157 | boolean consumeHorizontal = hgrav == Gravity.LEFT || hgrav == Gravity.RIGHT;
1158 |
1159 | if (consumeVertical) {
1160 | widthMode = MeasureSpec.EXACTLY;
1161 | } else if (consumeHorizontal) {
1162 | heightMode = MeasureSpec.EXACTLY;
1163 | } /* end of if */
1164 |
1165 | final int widthSpec = MeasureSpec.makeMeasureSpec(childWidthSize, widthMode);
1166 | final int heightSpec = MeasureSpec.makeMeasureSpec(childHeightSize, heightMode);
1167 | child.measure(widthSpec, heightSpec);
1168 |
1169 | if (consumeVertical) {
1170 | childHeightSize -= child.getMeasuredHeight();
1171 | } else if (consumeHorizontal) {
1172 | childWidthSize -= child.getMeasuredWidth();
1173 | } /* end of if */
1174 | } /* end of if */
1175 | } /* end of if */
1176 | } /* end of for */
1177 |
1178 | mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);
1179 | mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childHeightSize, MeasureSpec.EXACTLY);
1180 |
1181 | // Make sure we have created all fragments that we need to have shown.
1182 | mInLayout = true;
1183 | populate();
1184 | mInLayout = false;
1185 |
1186 | // Page views next.
1187 | size = getChildCount();
1188 | for (int i = 0; i < size; ++i) {
1189 | final View child = getChildAt(i);
1190 | if (child.getVisibility() != GONE) {
1191 | if (DEBUG) Log.v(TAG, "Measuring #" + i + " " + child
1192 | + ": " + mChildWidthMeasureSpec);
1193 |
1194 | final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1195 | if (lp == null || !lp.isDecor) {
1196 | child.measure(mChildWidthMeasureSpec, mChildHeightMeasureSpec);
1197 | } /* end of if */
1198 | } /* end of if */
1199 | } /* end of for */
1200 | }
1201 |
1202 | // XXX鍨傜洿
1203 | @Override
1204 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
1205 | super.onSizeChanged(w, h, oldw, oldh);
1206 |
1207 | // Make sure scroll position is set correctly.
1208 | if (h != oldh) {
1209 | recomputeScrollPosition(h, oldh, mPageMargin, mPageMargin);
1210 | } /* end of if */
1211 | }
1212 |
1213 | // XXX鍨傜洿
1214 |
1215 | /**
1216 | * 閲嶆柊瑷堢畻Scroll浣嶇疆
1217 | *
1218 | * @param height 楂樺害
1219 | * @param oldHeight 鑸婇珮搴�
1220 | * @param margin 閭婄晫
1221 | * @param oldMargin 鑸婇倞鐣�
1222 | */
1223 | private void recomputeScrollPosition(int height, int oldHeight, int margin, int oldMargin) {
1224 | final int heightWithMargin = height + margin;
1225 | if (oldHeight > 0) {
1226 | final int oldScrollPos = getScrollY();
1227 | final int oldwwm = oldHeight + oldMargin;
1228 | final int oldScrollItem = oldScrollPos / oldwwm;
1229 | final float scrollOffset = (float) (oldScrollPos % oldwwm) / oldwwm;
1230 | final int scrollPos = (int) ((oldScrollItem + scrollOffset) * heightWithMargin);
1231 | scrollTo(getScrollX(), scrollPos);
1232 | if (!mScroller.isFinished()) {
1233 | // We now return to your regularly scheduled scroll, already in progress.
1234 | final int newDuration = mScroller.getDuration() - mScroller.timePassed();
1235 | mScroller.startScroll(0, scrollPos, mCurItem * heightWithMargin, 0, newDuration);
1236 | } /* end of if */
1237 | } else {
1238 | int scrollPos = mCurItem * heightWithMargin;
1239 | if (scrollPos != getScrollY()) {
1240 | completeScroll();
1241 | scrollTo(getScrollX(), scrollPos);
1242 | } /* end of if */
1243 | } /* end of if */
1244 | }
1245 |
1246 | // XXX 鍨傜洿
1247 | @Override
1248 | protected void onLayout(boolean changed, int l, int t, int r, int b) {
1249 | mInLayout = true;
1250 | populate();
1251 | mInLayout = false;
1252 |
1253 | final int count = getChildCount();
1254 | int width = r - l;
1255 | int height = b - t;
1256 | int paddingLeft = getPaddingLeft();
1257 | int paddingTop = getPaddingTop();
1258 | int paddingRight = getPaddingRight();
1259 | int paddingBottom = getPaddingBottom();
1260 | final int scrollY = getScrollY();
1261 |
1262 | int decorCount = 0;
1263 |
1264 | for (int i = 0; i < count; i++) {
1265 | final View child = getChildAt(i);
1266 | if (child.getVisibility() != GONE) {
1267 | final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1268 | ItemInfo ii;
1269 | int childLeft = 0;
1270 | int childTop = 0;
1271 | if (lp.isDecor) {
1272 | //XXX isDecor鐐篺alse锛屾毇鏅傛矑鐢ㄥ埌
1273 | final int hgrav = lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
1274 | final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
1275 | switch (hgrav) {
1276 | default:
1277 | childLeft = paddingLeft;
1278 | break;
1279 | case Gravity.LEFT:
1280 | childLeft = paddingLeft;
1281 | paddingLeft += child.getMeasuredWidth();
1282 | break;
1283 | case Gravity.CENTER_HORIZONTAL:
1284 | childLeft = Math.max((width - child.getMeasuredWidth()) / 2,
1285 | paddingLeft);
1286 | break;
1287 | case Gravity.RIGHT:
1288 | childLeft = width - paddingRight - child.getMeasuredWidth();
1289 | paddingRight += child.getMeasuredWidth();
1290 | break;
1291 | } /* end of switch */
1292 | switch (vgrav) {
1293 | default:
1294 | childTop = paddingTop;
1295 | break;
1296 | case Gravity.TOP:
1297 | childTop = paddingTop;
1298 | paddingTop += child.getMeasuredHeight();
1299 | break;
1300 | case Gravity.CENTER_VERTICAL:
1301 | childTop = Math.max((height - child.getMeasuredHeight()) / 2,
1302 | paddingTop);
1303 | break;
1304 | case Gravity.BOTTOM:
1305 | childTop = height - paddingBottom - child.getMeasuredHeight();
1306 | paddingBottom += child.getMeasuredHeight();
1307 | break;
1308 | } /* end of switch */
1309 |
1310 | //XXX 绱�寗y杌哥Щ鍕曡窛闆�
1311 | childTop += scrollY;
1312 | decorCount++;
1313 | child.layout(childLeft, childTop,
1314 | childLeft + child.getMeasuredWidth(),
1315 | childTop + child.getMeasuredHeight());
1316 | } else if ((ii = infoForChild(child)) != null) {
1317 | //XXX 瑷堢畻ViewPager姣忎竴闋佺殑閭婄晫
1318 | int toff = (height + mPageMargin) * ii.position;
1319 | childLeft = paddingLeft;
1320 | childTop = paddingTop + toff;
1321 |
1322 | if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object
1323 | + ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth()
1324 | + "x" + child.getMeasuredHeight());
1325 | child.layout(childLeft, childTop,
1326 | childLeft + child.getMeasuredWidth(),
1327 | childTop + child.getMeasuredHeight());
1328 | } /* end of if */
1329 | } /* end of if */
1330 | } /* end of for */
1331 |
1332 | //XXX 瑷畾宸﹀彸閭婄晫
1333 | mLeftPageBounds = paddingLeft;
1334 | mRightPageBounds = width - paddingRight;
1335 | mDecorChildCount = decorCount;
1336 | mFirstLayout = false;
1337 | }
1338 |
1339 | @Override
1340 | public void computeScroll() {
1341 | if (DEBUG) Log.i(TAG, "computeScroll: finished=" + mScroller.isFinished());
1342 | if (!mScroller.isFinished()) {
1343 | if (mScroller.computeScrollOffset()) {
1344 | if (DEBUG) Log.i(TAG, "computeScroll: still scrolling");
1345 | int oldX = getScrollX();
1346 | int oldY = getScrollY();
1347 | int x = mScroller.getCurrX();
1348 | int y = mScroller.getCurrY();
1349 |
1350 | if (oldX != x || oldY != y) {
1351 | scrollTo(x, y);
1352 | pageScrolled(y);
1353 | } /* end of if */
1354 |
1355 | // Keep on drawing until the animation has finished.
1356 | invalidate();
1357 | return;
1358 | } /* end of if */
1359 | } /* end of if */
1360 |
1361 | // Done with scroll, clean up state.
1362 | completeScroll();
1363 | }
1364 |
1365 | /**
1366 | * page scrolled
1367 | *
1368 | * @param ypos
1369 | */
1370 | private void pageScrolled(int ypos) {
1371 | final int heightWithMargin = getHeight() + mPageMargin;
1372 | final int position = ypos / heightWithMargin;
1373 | final int offsetPixels = ypos % heightWithMargin;
1374 | final float offset = (float) offsetPixels / heightWithMargin;
1375 |
1376 | mCalledSuper = false;
1377 | onPageScrolled(position, offset, offsetPixels);
1378 | if (!mCalledSuper) {
1379 | throw new IllegalStateException("onPageScrolled did not call superclass implementation");
1380 | } /* end of if */
1381 | }
1382 |
1383 | //XXX
1384 |
1385 | /**
1386 | * This method will be invoked when the current page is scrolled, either as part
1387 | * of a programmatically initiated smooth scroll or a user initiated touch scroll.
1388 | * If you override this method you must call through to the superclass implementation
1389 | * (e.g. super.onPageScrolled(position, offset, offsetPixels)) before onPageScrolled
1390 | * returns.
1391 | *
1392 | * @param position Position index of the first page currently being displayed.
1393 | * Page position+1 will be visible if positionOffset is nonzero.
1394 | * @param offset Value from [0, 1) indicating the offset from the page at position.
1395 | * @param offsetPixels Value in pixels indicating the offset from position.
1396 | */
1397 | protected void onPageScrolled(int position, float offset, int offsetPixels) {
1398 | // Offset any decor views if needed - keep them on-screen at all times.
1399 | if (mDecorChildCount > 0) {
1400 | final int scrollY = getScrollY();
1401 | int paddingTop = getPaddingTop();
1402 | int paddingBottom = getPaddingBottom();
1403 | final int height = getHeight();
1404 | final int childCount = getChildCount();
1405 | for (int i = 0; i < childCount; i++) {
1406 | final View child = getChildAt(i);
1407 | final LayoutParams lp = (LayoutParams) child.getLayoutParams();
1408 | if (!lp.isDecor) continue;
1409 |
1410 | final int vgrav = lp.gravity & Gravity.VERTICAL_GRAVITY_MASK;
1411 | int childTop = 0;
1412 | switch (vgrav) {
1413 | default:
1414 | childTop = paddingTop;
1415 | break;
1416 | case Gravity.TOP:
1417 | childTop = paddingTop;
1418 | paddingTop += child.getHeight();
1419 | break;
1420 | case Gravity.CENTER_HORIZONTAL:
1421 | childTop = Math.max((height - child.getMeasuredHeight()) / 2,
1422 | paddingTop);
1423 | break;
1424 | case Gravity.BOTTOM:
1425 | childTop = height - paddingBottom - child.getMeasuredHeight();
1426 | paddingBottom += child.getMeasuredHeight();
1427 | break;
1428 | } /* end of switch */
1429 | childTop += scrollY;
1430 |
1431 | final int childOffset = childTop - child.getTop();
1432 | if (childOffset != 0) {
1433 | child.offsetTopAndBottom(childOffset);
1434 | } /* end of if */
1435 | } /* end of for */
1436 | } /* end of for */
1437 |
1438 | if (mOnPageChangeListener != null) {
1439 | mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);
1440 | } /* end of if */
1441 | if (mInternalPageChangeListener != null) {
1442 | mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);
1443 | } /* end of if */
1444 | mCalledSuper = true;
1445 | }
1446 |
1447 | /**
1448 | * ScrollScroll
1449 | * Scroll populate
1450 | */
1451 | private void completeScroll() {
1452 | boolean needPopulate = mScrolling;
1453 | if (needPopulate) {
1454 | // Done with scroll, no longer want to cache view drawing.
1455 | setScrollingCacheEnabled(false);
1456 | mScroller.abortAnimation();
1457 | int oldX = getScrollX();
1458 | int oldY = getScrollY();
1459 | int x = mScroller.getCurrX();
1460 | int y = mScroller.getCurrY();
1461 | if (oldX != x || oldY != y) {
1462 | scrollTo(x, y);
1463 | } /* end of if */
1464 | setScrollState(SCROLL_STATE_IDLE);
1465 | } /* end of if */
1466 | mPopulatePending = false;
1467 | mScrolling = false;
1468 | for (int i = 0; i < mItems.size(); i++) {
1469 | ItemInfo ii = mItems.get(i);
1470 | if (ii.scrolling) {
1471 | needPopulate = true;
1472 | ii.scrolling = false;
1473 | } /* end of if */
1474 | } /* end of for */
1475 | if (needPopulate) {
1476 | populate();
1477 | } /* end of if */
1478 | }
1479 |
1480 |
1481 | //
1482 | @Override
1483 | public boolean onInterceptTouchEvent(MotionEvent ev) {
1484 | /*
1485 | * This method JUST determines whether we want to intercept the motion.
1486 | * If we return true, onMotionEvent will be called and we do the actual
1487 | * scrolling there.
1488 | */
1489 | if (!isScroll) {
1490 | return false;
1491 | }
1492 |
1493 | final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
1494 |
1495 | // Always take care of the touch gesture being complete.
1496 | if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
1497 | // Release the drag.
1498 | if (DEBUG) Log.v(TAG, "Intercept done!");
1499 | mIsBeingDragged = false;
1500 | mIsUnableToDrag = false;
1501 | mActivePointerId = INVALID_POINTER;
1502 | if (mVelocityTracker != null) {
1503 | mVelocityTracker.recycle();
1504 | mVelocityTracker = null;
1505 | } /* end of if */
1506 | return false;
1507 | } /* end of if */
1508 |
1509 | // Nothing more to do here if we have decided whether or not we
1510 | // are dragging.
1511 | if (action != MotionEvent.ACTION_DOWN) {
1512 | if (mIsBeingDragged) {
1513 | if (DEBUG) Log.v(TAG, "Intercept returning true!");
1514 | return true;
1515 | } /* end of if */
1516 | if (mIsUnableToDrag) {
1517 | if (DEBUG) Log.v(TAG, "Intercept returning false!");
1518 | return false;
1519 | } /* end of if */
1520 | } /* end of if */
1521 |
1522 | switch (action) {
1523 | case MotionEvent.ACTION_MOVE: {
1524 | /*
1525 | * mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
1526 | * whether the user has moved far enough from his original down touch.
1527 | */
1528 |
1529 | /*
1530 | * Locally do absolute value. mLastMotionX is set to the x value
1531 | * of the down event.
1532 | */
1533 | final int activePointerId = mActivePointerId;
1534 | if (activePointerId == INVALID_POINTER) {
1535 | // If we don't have a valid id, the touch down wasn't on content.
1536 | break;
1537 | } /* end of if */
1538 |
1539 | final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);
1540 | final float x = MotionEventCompat.getX(ev, pointerIndex);
1541 | final float xDiff = Math.abs(x - mLastMotionX);
1542 | final float y = MotionEventCompat.getY(ev, pointerIndex);
1543 | final float dy = y - mLastMotionY;
1544 | final float yDiff = Math.abs(dy);
1545 |
1546 | if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
1547 |
1548 | if (canScroll(this, false, (int) dy, (int) x, (int) y)) {
1549 | // Nested view has scrollable area under this point. Let it be handled there.
1550 | mInitialMotionY = mLastMotionY = y;
1551 | mLastMotionX = x;
1552 | return false;
1553 | } /* end of if */
1554 | if (yDiff > mTouchSlop && yDiff > xDiff) {
1555 | if (DEBUG) Log.v(TAG, "Starting drag!");
1556 | mIsBeingDragged = true;
1557 | setScrollState(SCROLL_STATE_DRAGGING);
1558 | mLastMotionY = y;
1559 | setScrollingCacheEnabled(true);
1560 | } else {
1561 | if (xDiff > mTouchSlop) {
1562 | // The finger has moved enough in the horizontal
1563 | // direction to be counted as a drag... abort
1564 | // any attempt to drag vertically, to work correctly
1565 | // with children that have scrolling containers.
1566 | if (DEBUG) Log.v(TAG, "Starting unable to drag!");
1567 | mIsUnableToDrag = true;
1568 | } /* end of if */
1569 | } /* end of if */
1570 | break;
1571 | } /* end of case */
1572 |
1573 | case MotionEvent.ACTION_DOWN: {
1574 | /*
1575 | * Remember location of down touch.
1576 | * ACTION_DOWN always refers to pointer index 0.
1577 | */
1578 | mLastMotionX = ev.getX();
1579 | mLastMotionY = mInitialMotionY = ev.getY();
1580 | mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
1581 |
1582 | if (mScrollState == SCROLL_STATE_SETTLING) {
1583 | // Let the user 'catch' the pager as it animates.
1584 | mIsBeingDragged = true;
1585 | mIsUnableToDrag = false;
1586 | setScrollState(SCROLL_STATE_DRAGGING);
1587 | } else {
1588 | completeScroll();
1589 | mIsBeingDragged = false;
1590 | mIsUnableToDrag = false;
1591 | } /* end of if */
1592 |
1593 | if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY
1594 | + " mIsBeingDragged=" + mIsBeingDragged
1595 | + "mIsUnableToDrag=" + mIsUnableToDrag);
1596 | break;
1597 | } /* end of case */
1598 |
1599 | case MotionEventCompat.ACTION_POINTER_UP:
1600 | onSecondaryPointerUp(ev);
1601 | break;
1602 | } /* end of switch */
1603 |
1604 | if (!mIsBeingDragged) {
1605 | // Track the velocity as long as we aren't dragging.
1606 | // Once we start a real drag we will track in onTouchEvent.
1607 | if (mVelocityTracker == null) {
1608 | mVelocityTracker = VelocityTracker.obtain();
1609 | } /* end of if */
1610 | mVelocityTracker.addMovement(ev);
1611 | } /* end of if */
1612 |
1613 | /*
1614 | * The only time we want to intercept motion events is if we are in the
1615 | * drag mode.
1616 | */
1617 | return mIsBeingDragged;
1618 | }
1619 |
1620 |
1621 | // XXX 鍨傜洿
1622 | @Override
1623 | public boolean onTouchEvent(MotionEvent ev) {
1624 | if (mFakeDragging) {
1625 | // A fake drag is in progress already, ignore this real one
1626 | // but still eat the touch events.
1627 | // (It is likely that the user is multi-touching the screen.)
1628 | return true;
1629 | } /* end of if */
1630 |
1631 | if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
1632 | // Don't handle edge touches immediately -- they may actually belong to one of our
1633 | // descendants.
1634 | return false;
1635 | } /* end of if */
1636 |
1637 | if (mAdapter == null || mAdapter.getCount() == 0) {
1638 | // Nothing to present or scroll; nothing to touch.
1639 | return false;
1640 | } /* end of if */
1641 |
1642 | if (mVelocityTracker == null) {
1643 | mVelocityTracker = VelocityTracker.obtain();
1644 | } /* end of if */
1645 | mVelocityTracker.addMovement(ev);
1646 |
1647 | final int action = ev.getAction();
1648 | boolean needsInvalidate = false;
1649 |
1650 | switch (action & MotionEventCompat.ACTION_MASK) {
1651 | case MotionEvent.ACTION_DOWN: {
1652 | /*
1653 | * If being flinged and user touches, stop the fling. isFinished
1654 | * will be false if being flinged.
1655 | */
1656 | completeScroll();
1657 |
1658 | // Remember where the motion event started
1659 | mLastMotionY = mInitialMotionY = ev.getY();
1660 | mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
1661 | break;
1662 | } /* end of case */
1663 | case MotionEvent.ACTION_MOVE:
1664 | if (!mIsBeingDragged) {
1665 | final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
1666 | final float x = MotionEventCompat.getX(ev, pointerIndex);
1667 | final float xDiff = Math.abs(x - mLastMotionX);
1668 | final float y = MotionEventCompat.getY(ev, pointerIndex);
1669 | final float yDiff = Math.abs(y - mLastMotionY);
1670 | if (DEBUG)
1671 | Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
1672 | if (yDiff > mTouchSlop && yDiff > xDiff) {
1673 | if (DEBUG) Log.v(TAG, "Starting drag!");
1674 | mIsBeingDragged = true;
1675 | mLastMotionY = y;
1676 | setScrollState(SCROLL_STATE_DRAGGING);
1677 | setScrollingCacheEnabled(true);
1678 | } /* end of if */
1679 | } /* end of if */
1680 | if (mIsBeingDragged) {
1681 | // Scroll to follow the motion event
1682 | final int activePointerIndex = MotionEventCompat.findPointerIndex(
1683 | ev, mActivePointerId);
1684 | final float y = MotionEventCompat.getY(ev, activePointerIndex);
1685 | final float deltaY = mLastMotionY - y;
1686 | mLastMotionY = y;
1687 | float oldScrollY = getScrollY();
1688 | float scrollY = oldScrollY + deltaY;
1689 | final int height = getHeight();
1690 | final int heightWithMargin = height + mPageMargin;
1691 |
1692 | final int lastItemIndex = mAdapter.getCount() - 1;
1693 | final float topBound = Math.max(0, (mCurItem - 1) * heightWithMargin);
1694 | final float bottomBound =
1695 | Math.min(mCurItem + 1, lastItemIndex) * heightWithMargin;
1696 | if (scrollY < topBound) {
1697 | if (topBound == 0) {
1698 | float over = -scrollY;
1699 | needsInvalidate = mTopEdge.onPull(over / height);
1700 | } /* end of if */
1701 | scrollY = topBound;
1702 | } else if (scrollY > bottomBound) {
1703 | if (bottomBound == lastItemIndex * heightWithMargin) {
1704 | float over = scrollY - bottomBound;
1705 | needsInvalidate = mBottomEdge.onPull(over / height);
1706 | } /* end of if */
1707 | scrollY = bottomBound;
1708 | } /* end of if */
1709 | // Don't lose the rounded component
1710 | mLastMotionY += scrollY - (int) scrollY;
1711 | scrollTo(getScrollX(), (int) scrollY);
1712 | pageScrolled((int) scrollY);
1713 | } /* end of if */
1714 | break;
1715 | case MotionEvent.ACTION_UP:
1716 | if (mIsBeingDragged) {
1717 | final VelocityTracker velocityTracker = mVelocityTracker;
1718 | velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1719 | int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(
1720 | velocityTracker, mActivePointerId);
1721 | mPopulatePending = true;
1722 | final int heightWithMargin = getHeight() + mPageMargin;
1723 | final int scrollY = getScrollY();
1724 | final int currentPage = scrollY / heightWithMargin;
1725 | final float pageOffset = (float) (scrollY % heightWithMargin) / heightWithMargin;
1726 | final int activePointerIndex =
1727 | MotionEventCompat.findPointerIndex(ev, mActivePointerId);
1728 | final float y = MotionEventCompat.getY(ev, activePointerIndex);
1729 | final int totalDelta = (int) (y - mInitialMotionY);
1730 | int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
1731 | totalDelta);
1732 | setCurrentItemInternal(nextPage, true, true, initialVelocity);
1733 |
1734 | mActivePointerId = INVALID_POINTER;
1735 | endDrag();
1736 | needsInvalidate = mTopEdge.onRelease() | mBottomEdge.onRelease();
1737 | } /* end of if */
1738 | break;
1739 | case MotionEvent.ACTION_CANCEL:
1740 | if (mIsBeingDragged) {
1741 | setCurrentItemInternal(mCurItem, true, true);
1742 | mActivePointerId = INVALID_POINTER;
1743 | endDrag();
1744 | needsInvalidate = mTopEdge.onRelease() | mBottomEdge.onRelease();
1745 | } /* end of if */
1746 | break;
1747 | case MotionEventCompat.ACTION_POINTER_DOWN: {
1748 | final int index = MotionEventCompat.getActionIndex(ev);
1749 | final float y = MotionEventCompat.getY(ev, index);
1750 | mLastMotionY = y;
1751 | mActivePointerId = MotionEventCompat.getPointerId(ev, index);
1752 | break;
1753 | } /* end of case */
1754 | case MotionEventCompat.ACTION_POINTER_UP:
1755 | onSecondaryPointerUp(ev);
1756 | mLastMotionY = MotionEventCompat.getY(ev,
1757 | MotionEventCompat.findPointerIndex(ev, mActivePointerId));
1758 | break;
1759 | } /* end of switch */
1760 | if (needsInvalidate) {
1761 | invalidate();
1762 | } /* end of if */
1763 | return true;
1764 | }
1765 |
1766 | // XXX
1767 |
1768 | /**
1769 | * @param currentPage current page index
1770 | * @param pageOffset page
1771 | * @param velocity
1772 | * @param deltaY
1773 | * @return target page
1774 | */
1775 | private int determineTargetPage(int currentPage, float pageOffset, int velocity, int deltaY) {
1776 | int targetPage;
1777 | if (Math.abs(deltaY) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {
1778 | targetPage = velocity > 0 ? currentPage : currentPage + 1;
1779 | } else {
1780 | targetPage = (int) (currentPage + pageOffset + 0.5f);
1781 | } /* end of if */
1782 |
1783 | return targetPage;
1784 | }
1785 |
1786 | @Override
1787 | public void draw(Canvas canvas) {
1788 | // XXX
1789 | super.draw(canvas);
1790 | boolean needsInvalidate = false;
1791 |
1792 | final int overScrollMode = ViewCompat.getOverScrollMode(this);
1793 | if (overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS ||
1794 | (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS &&
1795 | mAdapter != null && mAdapter.getCount() > 1)) {
1796 | if (!mTopEdge.isFinished()) {
1797 | final int restoreCount = canvas.save();
1798 | final int width = getWidth() - getPaddingLeft() - getPaddingRight();
1799 |
1800 | canvas.rotate(270);
1801 | canvas.translate(-width + getPaddingLeft(), 0);
1802 | mTopEdge.setSize(width, getHeight());
1803 | needsInvalidate |= mTopEdge.draw(canvas);
1804 | canvas.restoreToCount(restoreCount);
1805 | } /* end of if */
1806 | if (!mBottomEdge.isFinished()) {
1807 | final int restoreCount = canvas.save();
1808 | final int width = getWidth() - getPaddingLeft() - getPaddingRight();
1809 | final int height = getHeight();
1810 | final int itemCount = mAdapter != null ? mAdapter.getCount() : 1;
1811 |
1812 | canvas.rotate(180);
1813 | canvas.translate(-width + getPaddingLeft(), -itemCount * (height + mPageMargin) + mPageMargin);
1814 | mBottomEdge.setSize(width, height);
1815 | needsInvalidate |= mBottomEdge.draw(canvas);
1816 | canvas.restoreToCount(restoreCount);
1817 | } /* end of if */
1818 | } else {
1819 | mTopEdge.finish();
1820 | mBottomEdge.finish();
1821 | } /* end of if */
1822 |
1823 | if (needsInvalidate) {
1824 | // Keep animating
1825 | invalidate();
1826 | } /* end of if */
1827 | }
1828 |
1829 | @Override
1830 | protected void onDraw(Canvas canvas) {
1831 | super.onDraw(canvas);
1832 |
1833 | //XXX
1834 | //Draw the margin drawable if needed.
1835 | if (mPageMargin > 0 && mMarginDrawable != null) {
1836 | final int scrollY = getScrollY();
1837 | final int height = getHeight();
1838 | final int offset = scrollY % (height + mPageMargin);
1839 | if (offset != 0) {
1840 | // Pages fit completely when settled; we only need to draw when in between
1841 | final int top = scrollY - offset + height;
1842 | mMarginDrawable.setBounds(mLeftPageBounds, top, mRightPageBounds, top + mPageMargin);
1843 | mMarginDrawable.draw(canvas);
1844 | } /* end of if */
1845 | } /* end of if */
1846 | }
1847 |
1848 | /**
1849 | * Start a fake drag of the pager.
1850 | *
1851 | *
A fake drag can be useful if you want to synchronize the motion of the ViewPager
1852 | * with the touch scrolling of another view, while still letting the ViewPager
1853 | * control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.)
1854 | * Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call
1855 | * {@link #endFakeDrag()} to complete the fake drag and fling as necessary.
1856 | *
1857 | *
During a fake drag the ViewPager will ignore all touch events. If a real drag
1858 | * is already in progress, this method will return false.
1859 | *
1860 | * @return true if the fake drag began successfully, false if it could not be started.
1861 | * @see #fakeDragBy(float)
1862 | * @see #endFakeDrag()
1863 | */
1864 | public boolean beginFakeDrag() {
1865 | if (mIsBeingDragged) {
1866 | return false;
1867 | } /* end of if */
1868 | mFakeDragging = true;
1869 | setScrollState(SCROLL_STATE_DRAGGING);
1870 | // XXX
1871 | mInitialMotionY = mLastMotionY = 0;
1872 | if (mVelocityTracker == null) {
1873 | mVelocityTracker = VelocityTracker.obtain();
1874 | } else {
1875 | mVelocityTracker.clear();
1876 | } /* end of if */
1877 | final long time = SystemClock.uptimeMillis();
1878 | final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0);
1879 | mVelocityTracker.addMovement(ev);
1880 | ev.recycle();
1881 | mFakeDragBeginTime = time;
1882 | return true;
1883 | }
1884 |
1885 | /**
1886 | * End a fake drag of the pager.
1887 | *
1888 | * @see #beginFakeDrag()
1889 | * @see #fakeDragBy(float)
1890 | */
1891 | public void endFakeDrag() {
1892 | //XXX
1893 | if (!mFakeDragging) {
1894 | throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
1895 | } /* end of if */
1896 |
1897 | final VelocityTracker velocityTracker = mVelocityTracker;
1898 | velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
1899 | int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
1900 | velocityTracker, mActivePointerId);
1901 | mPopulatePending = true;
1902 | final int totalDelta = (int) (mLastMotionY - mInitialMotionY);
1903 | final int scrollY = getScrollY();
1904 | final int heightWithMargin = getHeight() + mPageMargin;
1905 | final int currentPage = scrollY / heightWithMargin;
1906 | final float pageOffset = (float) (scrollY % heightWithMargin) / heightWithMargin;
1907 | int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity, totalDelta);
1908 | setCurrentItemInternal(nextPage, true, true, initialVelocity);
1909 | endDrag();
1910 |
1911 | mFakeDragging = false;
1912 | }
1913 |
1914 |
1915 | // XXX
1916 |
1917 | /**
1918 | * Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first.
1919 | *
1920 | * @param yOffset Offset in pixels to drag by.
1921 | * @see #beginFakeDrag()
1922 | * @see #endFakeDrag()
1923 | */
1924 | public void fakeDragBy(float yOffset) {
1925 | if (!mFakeDragging) {
1926 | throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");
1927 | } /* end of if */
1928 |
1929 | mLastMotionY += yOffset;
1930 | float scrollY = getScrollY() - yOffset;
1931 | final int height = getHeight();
1932 | final int heightWithMargin = height + mPageMargin;
1933 |
1934 | final float topBound = Math.max(0, (mCurItem - 1) * heightWithMargin);
1935 | final float bottomBound =
1936 | Math.min(mCurItem + 1, mAdapter.getCount() - 1) * heightWithMargin;
1937 | if (scrollY < topBound) {
1938 | scrollY = topBound;
1939 | } else if (scrollY > bottomBound) {
1940 | scrollY = bottomBound;
1941 | } /* end of if */
1942 | // Don't lose the rounded component
1943 | mLastMotionY += scrollY - (int) scrollY;
1944 | scrollTo(getScrollX(), (int) scrollY);
1945 | pageScrolled((int) scrollY);
1946 |
1947 | // Synthesize an event for the VelocityTracker.
1948 | final long time = SystemClock.uptimeMillis();
1949 | final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,
1950 | 0, mLastMotionY, 0);
1951 | mVelocityTracker.addMovement(ev);
1952 | ev.recycle();
1953 | }
1954 |
1955 | /**
1956 | * Returns true if a fake drag is in progress.
1957 | *
1958 | * @return true if currently in a fake drag, false otherwise.
1959 | * @see #beginFakeDrag()
1960 | * @see #fakeDragBy(float)
1961 | * @see #endFakeDrag()
1962 | */
1963 | public boolean isFakeDragging() {
1964 | return mFakeDragging;
1965 | }
1966 |
1967 | // XXX
1968 |
1969 | /**
1970 | * on secondary pointer up
1971 | * This was our active pointer going up. Choose a new active pointer and adjust accordingly.
1972 | *
1973 | * @param ev MotionEvent
1974 | */
1975 | private void onSecondaryPointerUp(MotionEvent ev) {
1976 | final int pointerIndex = MotionEventCompat.getActionIndex(ev);
1977 | final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
1978 | if (pointerId == mActivePointerId) {
1979 | // This was our active pointer going up. Choose a new
1980 | // active pointer and adjust accordingly.
1981 | final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
1982 | mLastMotionY = MotionEventCompat.getY(ev, newPointerIndex);
1983 | mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
1984 | if (mVelocityTracker != null) {
1985 | mVelocityTracker.clear();
1986 | } /* end of if */
1987 | } /* end of if */
1988 | }
1989 |
1990 | /**
1991 | * end drag
1992 | */
1993 | private void endDrag() {
1994 | mIsBeingDragged = false;
1995 | mIsUnableToDrag = false;
1996 |
1997 | if (mVelocityTracker != null) {
1998 | mVelocityTracker.recycle();
1999 | mVelocityTracker = null;
2000 | } /* end of if */
2001 | }
2002 |
2003 | /**
2004 | * ScrollingCacheEnabled
2005 | *
2006 | * @param enabled enabled or disabled
2007 | */
2008 | private void setScrollingCacheEnabled(boolean enabled) {
2009 | if (mScrollingCacheEnabled != enabled) {
2010 | mScrollingCacheEnabled = enabled;
2011 | if (USE_CACHE) {
2012 | final int size = getChildCount();
2013 | for (int i = 0; i < size; ++i) {
2014 | final View child = getChildAt(i);
2015 | if (child.getVisibility() != GONE) {
2016 | child.setDrawingCacheEnabled(enabled);
2017 | } /* end of if */
2018 | } /* end of for */
2019 | } /* end of if */
2020 | } /* end of if */
2021 | }
2022 |
2023 | //XXX
2024 |
2025 | /**
2026 | * Tests scrollability within child views of v given a delta of dy.
2027 | *
2028 | * @param v View to test for vertical scrollability
2029 | * @param checkV Whether the view v passed should itself be checked for scrollability (true),
2030 | * or just its children (false).
2031 | * @param dy Delta scrolled in pixels
2032 | * @param x X coordinate of the active touch point
2033 | * @param y Y coordinate of the active touch point
2034 | * @return true if child views of v can be scrolled by delta of dx.
2035 | */
2036 | protected boolean canScroll(View v, boolean checkV, int dy, int x, int y) {
2037 | if (v instanceof ViewGroup) {
2038 | final ViewGroup group = (ViewGroup) v;
2039 | final int scrollX = v.getScrollX();
2040 | final int scrollY = v.getScrollY();
2041 | final int count = group.getChildCount();
2042 | // Count backwards - let topmost views consume scroll distance first.
2043 | for (int i = count - 1; i >= 0; i--) {
2044 | // This will not work for transformed views in Honeycomb+
2045 | final View child = group.getChildAt(i);
2046 | if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
2047 | y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
2048 | canScroll(child, true, dy, x + scrollX - child.getLeft(),
2049 | y + scrollY - child.getTop())) {
2050 | return true;
2051 | } /* end of if */
2052 | } /* end of for */
2053 | } /* end of if */
2054 |
2055 | return checkV && ViewCompat.canScrollVertically(v, -dy);
2056 | }
2057 |
2058 | @Override
2059 | public boolean dispatchKeyEvent(KeyEvent event) {
2060 | // Let the focused view and/or our descendants get the key first
2061 | return super.dispatchKeyEvent(event) || executeKeyEvent(event);
2062 | }
2063 |
2064 |
2065 | //XXX
2066 |
2067 | /**
2068 | * You can call this function yourself to have the scroll view perform
2069 | * scrolling from a key event, just as if the event had been dispatched to
2070 | * it by the view hierarchy.
2071 | *
2072 | * @param event The key event to execute.
2073 | * @return Return true if the event was handled, else false.
2074 | */
2075 | public boolean executeKeyEvent(KeyEvent event) {
2076 | boolean handled = false;
2077 | if (event.getAction() == KeyEvent.ACTION_DOWN) {
2078 | switch (event.getKeyCode()) {
2079 | case KeyEvent.KEYCODE_DPAD_UP:
2080 | handled = arrowScroll(FOCUS_UP);
2081 | break;
2082 | case KeyEvent.KEYCODE_DPAD_DOWN:
2083 | handled = arrowScroll(FOCUS_DOWN);
2084 | break;
2085 | case KeyEvent.KEYCODE_TAB:
2086 | if (Build.VERSION.SDK_INT >= 11) {
2087 | // The focus finder had a bug handling FOCUS_FORWARD and FOCUS_BACKWARD
2088 | // before Android 3.0. Ignore the tab key on those devices.
2089 | if (KeyEventCompat.hasNoModifiers(event)) {
2090 | handled = arrowScroll(FOCUS_FORWARD);
2091 | } else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) {
2092 | handled = arrowScroll(FOCUS_BACKWARD);
2093 | } /* end of if */
2094 | } /* end of if */
2095 | break;
2096 | } /* end of switch */
2097 | } /* end of if */
2098 | return handled;
2099 | }
2100 |
2101 | //XXX
2102 |
2103 | /**
2104 | * Page keypad
2105 | *
2106 | * @param direction
2107 | * @return handled
2108 | */
2109 | public boolean arrowScroll(int direction) {
2110 | View currentFocused = findFocus();
2111 | if (currentFocused == this) currentFocused = null;
2112 |
2113 | boolean handled = false;
2114 |
2115 | View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused,
2116 | direction);
2117 | if (nextFocused != null && nextFocused != currentFocused) {
2118 | if (direction == View.FOCUS_UP) {
2119 | // If there is nothing to the left, or this is causing us to
2120 | // jump to the down, then what we really want to do is page up.
2121 | if (currentFocused != null && nextFocused.getTop() >= currentFocused.getTop()) {
2122 | handled = pageUp();
2123 | } else {
2124 | handled = nextFocused.requestFocus();
2125 | } /* end of if */
2126 | } else if (direction == View.FOCUS_DOWN) {
2127 | // If there is nothing to the right, or this is causing us to
2128 | // jump to the left, then what we really want to do is page right.
2129 | if (currentFocused != null && nextFocused.getTop() <= currentFocused.getTop()) {
2130 | handled = pageDown();
2131 | } else {
2132 | handled = nextFocused.requestFocus();
2133 | } /* end of if */
2134 | } /* end of if */
2135 | } else if (direction == FOCUS_UP || direction == FOCUS_BACKWARD) {
2136 | // Trying to move left and nothing there; try to page.
2137 | handled = pageUp();
2138 | } else if (direction == FOCUS_DOWN || direction == FOCUS_FORWARD) {
2139 | // Trying to move right and nothing there; try to page.
2140 | handled = pageDown();
2141 | } /* end of if */
2142 | if (handled) {
2143 | playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));
2144 | } /* end of if */
2145 | return handled;
2146 | }
2147 |
2148 | //XXX
2149 | boolean pageUp() {
2150 | if (mCurItem > 0) {
2151 | setCurrentItem(mCurItem - 1, true);
2152 | return true;
2153 | } /* end of if */
2154 | return false;
2155 | }
2156 |
2157 | //XXX
2158 | boolean pageDown() {
2159 | if (mAdapter != null && mCurItem < (mAdapter.getCount() - 1)) {
2160 | setCurrentItem(mCurItem + 1, true);
2161 | return true;
2162 | } /* end of if */
2163 | return false;
2164 | }
2165 |
2166 | /**
2167 | * We only want the current page that is being shown to be focusable.
2168 | */
2169 | @Override
2170 | public void addFocusables(ArrayList views, int direction, int focusableMode) {
2171 | final int focusableCount = views.size();
2172 |
2173 | final int descendantFocusability = getDescendantFocusability();
2174 |
2175 | if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
2176 | for (int i = 0; i < getChildCount(); i++) {
2177 | final View child = getChildAt(i);
2178 | if (child.getVisibility() == VISIBLE) {
2179 | ItemInfo ii = infoForChild(child);
2180 | if (ii != null && ii.position == mCurItem) {
2181 | child.addFocusables(views, direction, focusableMode);
2182 | } /* end of if */
2183 | } /* end of if */
2184 | } /* end of for */
2185 | } /* end of if */
2186 |
2187 | // we add ourselves (if focusable) in all cases except for when we are
2188 | // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is
2189 | // to avoid the focus search finding layouts when a more precise search
2190 | // among the focusable children would be more interesting.
2191 | if (
2192 | descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
2193 | // No focusable descendants
2194 | (focusableCount == views.size())) {
2195 | // Note that we can't call the superclass here, because it will
2196 | // add all views in. So we need to do the same thing View does.
2197 | if (!isFocusable()) {
2198 | return;
2199 | } /* end of if */
2200 | if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE &&
2201 | isInTouchMode() && !isFocusableInTouchMode()) {
2202 | return;
2203 | } /* end of if */
2204 | if (views != null) {
2205 | views.add(this);
2206 | } /* end of if */
2207 | } /* end of if */
2208 | }
2209 |
2210 | /**
2211 | * We only want the current page that is being shown to be touchable.
2212 | */
2213 | @Override
2214 | public void addTouchables(ArrayList views) {
2215 | // Note that we don't call super.addTouchables(), which means that
2216 | // we don't call View.addTouchables(). This is okay because a ViewPager
2217 | // is itself not touchable.
2218 | for (int i = 0; i < getChildCount(); i++) {
2219 | final View child = getChildAt(i);
2220 | if (child.getVisibility() == VISIBLE) {
2221 | ItemInfo ii = infoForChild(child);
2222 | if (ii != null && ii.position == mCurItem) {
2223 | child.addTouchables(views);
2224 | } /* end of if */
2225 | } /* end of if */
2226 | } /* end of for */
2227 | }
2228 |
2229 | /**
2230 | * We only want the current page that is being shown to be focusable.
2231 | */
2232 | @Override
2233 | protected boolean onRequestFocusInDescendants(int direction,
2234 | Rect previouslyFocusedRect) {
2235 | int index;
2236 | int increment;
2237 | int end;
2238 | int count = getChildCount();
2239 | if ((direction & FOCUS_FORWARD) != 0) {
2240 | index = 0;
2241 | increment = 1;
2242 | end = count;
2243 | } else {
2244 | index = count - 1;
2245 | increment = -1;
2246 | end = -1;
2247 | } /* end of if */
2248 | for (int i = index; i != end; i += increment) {
2249 | View child = getChildAt(i);
2250 | if (child.getVisibility() == VISIBLE) {
2251 | ItemInfo ii = infoForChild(child);
2252 | if (ii != null && ii.position == mCurItem) {
2253 | if (child.requestFocus(direction, previouslyFocusedRect)) {
2254 | return true;
2255 | } /* end of if */
2256 | } /* end of if */
2257 | } /* end of if */
2258 | } /* end of for */
2259 | return false;
2260 | }
2261 |
2262 | @Override
2263 | public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
2264 | // ViewPagers should only report accessibility info for the current page,
2265 | // otherwise things get very confusing.
2266 |
2267 | // TODO: Should this note something about the paging container?
2268 |
2269 | final int childCount = getChildCount();
2270 | for (int i = 0; i < childCount; i++) {
2271 | final View child = getChildAt(i);
2272 | if (child.getVisibility() == VISIBLE) {
2273 | final ItemInfo ii = infoForChild(child);
2274 | if (ii != null && ii.position == mCurItem &&
2275 | child.dispatchPopulateAccessibilityEvent(event)) {
2276 | return true;
2277 | } /* end of if */
2278 | } /* end of if */
2279 | } /* end of for */
2280 |
2281 | return false;
2282 | }
2283 |
2284 | @Override
2285 | protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
2286 | return new LayoutParams();
2287 | }
2288 |
2289 | @Override
2290 | protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
2291 | return generateDefaultLayoutParams();
2292 | }
2293 |
2294 | @Override
2295 | protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
2296 | return p instanceof LayoutParams && super.checkLayoutParams(p);
2297 | }
2298 |
2299 | @Override
2300 | public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
2301 | return new LayoutParams(getContext(), attrs);
2302 | }
2303 |
2304 | private class PagerObserver extends DataSetObserver {
2305 | @Override
2306 | public void onChanged() {
2307 | dataSetChanged();
2308 | }
2309 |
2310 | @Override
2311 | public void onInvalidated() {
2312 | dataSetChanged();
2313 | }
2314 | }
2315 |
2316 | /**
2317 | * Layout parameters that should be supplied for views added to a
2318 | * ViewPager.
2319 | */
2320 | public static class LayoutParams extends ViewGroup.LayoutParams {
2321 | /**
2322 | * true if this view is a decoration on the pager itself and not
2323 | * a view supplied by the adapter.
2324 | */
2325 | public boolean isDecor;
2326 |
2327 | /**
2328 | * Where to position the view page within the overall ViewPager
2329 | * container; constants are defined in {@link Gravity}.
2330 | */
2331 | public int gravity;
2332 |
2333 | public LayoutParams() {
2334 | super(FILL_PARENT, FILL_PARENT);
2335 | }
2336 |
2337 | public LayoutParams(Context context, AttributeSet attrs) {
2338 | super(context, attrs);
2339 |
2340 | final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
2341 | gravity = a.getInteger(0, Gravity.NO_GRAVITY);
2342 | a.recycle();
2343 | }
2344 | }
2345 | }
2346 |
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/layout/fragment_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/layout/fragment_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
21 |
22 |
23 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/layout/fragment_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
15 |
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/layout/fragment_pager.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/layout/fragment_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/bg_bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/bg_bottom.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_bottom_line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_bottom_line.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_home.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_home_line_bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_home_line_bottom.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_home_line_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_home_line_left.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_home_line_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_home_line_right.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_left_line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_left_line.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_right_line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xhdpi/icon_right_line.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/bg_bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/bg_bottom.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/bg_home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/bg_home.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_bottom_line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_bottom_line.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_home_line_bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_home_line_bottom.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_home_line_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_home_line_left.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_home_line_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_home_line_right.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_left_line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_left_line.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_right_line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxhdpi/icon_right_line.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #00000000
4 | #FF000000
5 | #FFF9F9F9
6 | #FFE9E9E9
7 | #FF666666
8 | #99333333
9 | #FF999999
10 | #FFCCCCCC
11 |
12 |
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | BgMoveViewPager
3 |
4 |
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/BgMoveViewPager/app/src/test/java/com/zheblog/bgmoveviewpager/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.zheblog.bgmoveviewpager;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/BgMoveViewPager/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.1.2'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/BgMoveViewPager/gradle.properties:
--------------------------------------------------------------------------------
1 | # IDE (e.g. Android Studio) users:
2 | # Gradle settings configured through the IDE *will override*
3 | # any settings specified in this file.
4 |
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 |
8 | # Specifies the JVM arguments used for the daemon process.
9 | # The setting is particularly useful for tweaking memory settings.
10 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
11 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
12 |
13 | # When configured, Gradle will run in incubating parallel mode.
14 | # This option should only be used with decoupled projects. More details, visit
15 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
16 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/BgMoveViewPager/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/BgMoveViewPager/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/BgMoveViewPager/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/BgMoveViewPager/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/BgMoveViewPager/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/BgMoveViewPager/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BgMoveViewPager #
2 | 背景图片跟随手势滑动的ViewPager,可各方向滑动或点击切换页面。
3 |
4 | 原效果参考 [ANA Portuguese Airports](https://play.google.com/store/apps/details?id=com.innovagency.ana)(google play地址)
5 |
6 | ### 效果图 ###
7 |
8 |
9 | ### 组成 ###
10 | 项目由一个Activity和五个fragment组成。
11 |
12 | ### 主要代码介绍 ###
13 | 1、横向背景移动的ViewPager中,重写dispatchDraw方法:
14 |
15 | ````
16 | @Override
17 | protected void dispatchDraw(Canvas canvas) {
18 | if (this.bg != null) {
19 | int width = this.bg.getWidth();
20 | int height = this.bg.getHeight();
21 | int count = getAdapter().getCount();
22 | int x = getScrollX();
23 | //子View中背景图片需要显示的宽度,放大背景图或缩小背景图。
24 | int n = height * getWidth() / getHeight();
25 | //(width - n) / (count - 1)表示除去显示第一个ViewPager页面用去的背景宽度,剩余的ViewPager需要显示的背景图片的宽度。
26 | //getWidth()等于ViewPager一个页面的宽度,即手机屏幕宽度。在该计算中可以理解为滑动一个ViewPager页面需要滑动的像素值。
27 | //((width - n) / (count - 1)) / getWidth()也就表示ViewPager滑动一个像素时,背景图片滑动的宽度。
28 | //x * ((width - n) / (count - 1)) / getWidth()也就表示ViewPager滑动x个像素时,背景图片滑动的宽度。
29 | //背景图片滑动的宽度的宽度可以理解为背景图片滑动到达的位置。
30 | int w = (x+getWidth()) * ((width - n) / (count - 1)) / getWidth();
31 | canvas.drawBitmap(this.bg, new Rect(w, 0, n + w, height), new Rect(x, 0, x + getWidth(), getHeight()), this.b);
32 | }
33 | super.dispatchDraw(canvas);
34 | }
35 | ````
36 | 2、IScrollListener中控制是否可以方向滚动
37 |
38 | ````
39 | void canScrollView(boolean isCanScroll);
40 | ````
41 | 3、添加FixedSpeedScroller类(继承Scroller),控制ViewPager调用setCurrentItem方法时的滚动速度。
42 |
43 | ````
44 | private int mDuration = 800; // 默认为800ms
45 | @Override
46 | public void startScroll(int startX, int startY, int dx, int dy, int duration) {
47 | super.startScroll(startX, startY, dx, dy, mDuration);
48 | }
49 |
50 | @Override
51 | public void startScroll(int startX, int startY, int dx, int dy) {
52 | super.startScroll(startX, startY, dx, dy, mDuration);
53 | }
54 | ````
55 |
56 | ### 最后 ###
57 |
58 | 如果对您有帮助请Star,有问题随时联系我,谢谢.
59 |
60 | ### 关于我 ###
61 | QQ交流群: 496946393
62 |
63 | 邮箱: nh_zhe@163.com
64 |
65 | [简书](http://www.jianshu.com/users/550d52af9d72/latest_articles)
66 |
67 | [个人博客](http://www.zheblog.com)
--------------------------------------------------------------------------------
/gif/BgMove.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zhe525069676/BgMoveViewPager/7e3623fdc553eae3b07310ca7ce3224fae354479/gif/BgMove.gif
--------------------------------------------------------------------------------