|
|
@@ -3,7 +3,6 @@ package kr.co.zumo.app.lifeplus.view.screen.main;
|
|
|
import android.graphics.PointF;
|
|
|
import android.support.annotation.NonNull;
|
|
|
import android.support.annotation.Nullable;
|
|
|
-import android.support.v7.widget.LinearLayoutManager;
|
|
|
import android.support.v7.widget.LinearSmoothScroller;
|
|
|
import android.support.v7.widget.OrientationHelper;
|
|
|
import android.support.v7.widget.RecyclerView;
|
|
|
@@ -14,7 +13,6 @@ import android.support.v7.widget.SnapHelper;
|
|
|
import android.util.DisplayMetrics;
|
|
|
import android.util.Log;
|
|
|
import android.view.View;
|
|
|
-import android.view.ViewConfiguration;
|
|
|
import android.view.animation.DecelerateInterpolator;
|
|
|
import android.widget.Scroller;
|
|
|
|
|
|
@@ -37,6 +35,8 @@ public class MainCategorySnapper extends RecyclerView.OnFlingListener {
|
|
|
private static final int TIME_MIN = 250;
|
|
|
|
|
|
static final float MILLISECONDS_PER_INCH = 100f;
|
|
|
+ private static final float SCROLL_FRICTION = 5f; //1.5f // 마찰계수를 늘려 준다. 최대 이동 거리가 줄어듬.
|
|
|
+
|
|
|
|
|
|
RecyclerView mRecyclerView;
|
|
|
private Scroller mGravityScroller;
|
|
|
@@ -66,7 +66,7 @@ public class MainCategorySnapper extends RecyclerView.OnFlingListener {
|
|
|
@Override
|
|
|
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
|
|
// Log.i("APP# MainCategorySnapper | onScrolledVertical", "|" + "dx: " + dx);
|
|
|
- if (dx != 0 || dy != 0) {
|
|
|
+ if (dx != 0) {
|
|
|
mScrolled = true;
|
|
|
}
|
|
|
}
|
|
|
@@ -96,8 +96,7 @@ public class MainCategorySnapper extends RecyclerView.OnFlingListener {
|
|
|
return false;
|
|
|
}
|
|
|
int minFlingVelocity = 0; //mRecyclerView.getMinFlingVelocity();
|
|
|
- return (Math.abs(velocityY) > minFlingVelocity || Math.abs(velocityX) > minFlingVelocity)
|
|
|
- && snapFromFling(layoutManager, velocityX, velocityY);
|
|
|
+ return Math.abs(velocityX) > minFlingVelocity && snapFromFling(layoutManager, velocityX, velocityY);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -121,8 +120,8 @@ public class MainCategorySnapper extends RecyclerView.OnFlingListener {
|
|
|
mRecyclerView = recyclerView;
|
|
|
if (mRecyclerView != null) {
|
|
|
setupCallbacks();
|
|
|
- mGravityScroller = new Scroller(mRecyclerView.getContext(), new DecelerateInterpolator(10f));
|
|
|
- mGravityScroller.setFriction(ViewConfiguration.getScrollFriction() * 10); // 마찰계수를 늘려 줌
|
|
|
+ mGravityScroller = new Scroller(mRecyclerView.getContext(), new DecelerateInterpolator());
|
|
|
+ mGravityScroller.setFriction(SCROLL_FRICTION);
|
|
|
snapToTargetExistingView();
|
|
|
}
|
|
|
}
|
|
|
@@ -156,10 +155,9 @@ public class MainCategorySnapper extends RecyclerView.OnFlingListener {
|
|
|
*/
|
|
|
public int[] calculateScrollDistance(int velocityX, int velocityY) {
|
|
|
int[] outDist = new int[2];
|
|
|
- mGravityScroller.fling(0, 0, velocityX, velocityY,
|
|
|
- Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
|
|
|
+ mGravityScroller.fling(0, 0, velocityX, velocityY, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
|
|
|
outDist[0] = mGravityScroller.getFinalX();
|
|
|
- outDist[1] = mGravityScroller.getFinalY();
|
|
|
+ outDist[1] = 0; //mGravityScroller.getFinalY();
|
|
|
return outDist;
|
|
|
}
|
|
|
|
|
|
@@ -172,8 +170,7 @@ public class MainCategorySnapper extends RecyclerView.OnFlingListener {
|
|
|
* @param velocityY Fling velocity on the vertical axis.
|
|
|
* @return true if it is handled, false otherwise.
|
|
|
*/
|
|
|
- private boolean snapFromFling(@NonNull LayoutManager layoutManager, int velocityX,
|
|
|
- int velocityY) {
|
|
|
+ private boolean snapFromFling(@NonNull LayoutManager layoutManager, int velocityX, int velocityY) {
|
|
|
if (!(layoutManager instanceof ScrollVectorProvider)) {
|
|
|
return false;
|
|
|
}
|
|
|
@@ -277,23 +274,7 @@ public class MainCategorySnapper extends RecyclerView.OnFlingListener {
|
|
|
// The associated RecyclerView has been removed so there is no action to take.
|
|
|
return;
|
|
|
}
|
|
|
- int offset = snapOffset;
|
|
|
-// if (mRecyclerView.getChildAdapterPosition(targetView) == (mRecyclerView.getAdapter().getItemCount() - 1)) {
|
|
|
-// /**
|
|
|
-// * 보통 뷰일 때 스크롤 타겟 위치
|
|
|
-// * - 0 + offset
|
|
|
-// *
|
|
|
-// * 마지막 뷰일 때
|
|
|
-// * => offset = screenWidth - 마지막 뷰 사이즈
|
|
|
-// */
|
|
|
-// OrientationHelper helper = getOrientationHelper(layoutManager);
|
|
|
-// if (null != helper) {
|
|
|
-// offset = (helper.getEnd() - helper.getDecoratedMeasurement(targetView));
|
|
|
-// }
|
|
|
-//// Log.e("APP# MainCategorySnapper | onTargetFound", "|" + "layoutManager.getWidth(): " + layoutManager.getWidth());
|
|
|
-//// Log.e("APP# MainCategorySnapper | onTargetFound", "|" + "helper.getDecoratedMeasurement(targetView): " + helper.getDecoratedMeasurement(targetView));
|
|
|
-//// Log.e("APP# MainCategorySnapper | onTargetFound", "|" + "offset: " + offset);
|
|
|
-// }
|
|
|
+
|
|
|
int[] snapDistances = calculateDistanceToFinalSnap(mRecyclerView.getLayoutManager(), targetView);
|
|
|
final int dx;
|
|
|
final int dy;
|
|
|
@@ -301,21 +282,8 @@ public class MainCategorySnapper extends RecyclerView.OnFlingListener {
|
|
|
/**
|
|
|
* 방향을 고려해서 offset 적용한다. 항상 적용하면 긴쪽을 기준으로 time 이 계산되어서 이동 거리가 0일 경우 느리게 움직일 수 있다.
|
|
|
*/
|
|
|
- LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
|
|
|
- if (null != linearLayoutManager) {
|
|
|
- if (linearLayoutManager.getOrientation() == LinearLayoutManager.HORIZONTAL) {
|
|
|
- dx = snapDistances[0] - offset;
|
|
|
- dy = snapDistances[1];
|
|
|
- }
|
|
|
- else {
|
|
|
- dx = snapDistances[0];
|
|
|
- dy = snapDistances[1] - offset;
|
|
|
- }
|
|
|
- }
|
|
|
- else {
|
|
|
- dx = snapDistances[0];
|
|
|
- dy = snapDistances[1];
|
|
|
- }
|
|
|
+ dx = snapDistances[0];
|
|
|
+ dy = snapDistances[1]; // - offset;
|
|
|
|
|
|
final int time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy)));
|
|
|
if (time > 0) {
|
|
|
@@ -328,17 +296,12 @@ public class MainCategorySnapper extends RecyclerView.OnFlingListener {
|
|
|
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
|
|
|
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
|
|
|
}
|
|
|
+
|
|
|
};
|
|
|
}
|
|
|
|
|
|
private OrientationHelper getOrientationHelper(LayoutManager layoutManager) {
|
|
|
- if (layoutManager.canScrollVertically()) {
|
|
|
- return getVerticalHelper(layoutManager);
|
|
|
- }
|
|
|
- else if (layoutManager.canScrollHorizontally()) {
|
|
|
- return getHorizontalHelper(layoutManager);
|
|
|
- }
|
|
|
- return null;
|
|
|
+ return getHorizontalHelper(layoutManager);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -358,20 +321,23 @@ public class MainCategorySnapper extends RecyclerView.OnFlingListener {
|
|
|
@Nullable
|
|
|
public int[] calculateDistanceToFinalSnap(@NonNull LayoutManager layoutManager, @NonNull View targetView) {
|
|
|
int[] out = new int[2];
|
|
|
- if (layoutManager.canScrollHorizontally()) {
|
|
|
- out[0] = distanceToTop(layoutManager, targetView, getHorizontalHelper(layoutManager));
|
|
|
- }
|
|
|
- else {
|
|
|
- out[0] = 0;
|
|
|
- }
|
|
|
+ out[0] = distanceToCenter(layoutManager, targetView, getHorizontalHelper(layoutManager));
|
|
|
+ out[1] = 0;
|
|
|
|
|
|
- if (layoutManager.canScrollVertically()) {
|
|
|
- out[1] = distanceToTop(layoutManager, targetView, getVerticalHelper(layoutManager));
|
|
|
+ return out;
|
|
|
+ }
|
|
|
+
|
|
|
+ private int distanceToCenter(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView, OrientationHelper helper) {
|
|
|
+ final int childStart = helper.getDecoratedStart(targetView);
|
|
|
+ final int containerStart;
|
|
|
+ if (layoutManager.getClipToPadding()) {
|
|
|
+ containerStart = helper.getStartAfterPadding() + snapOffset;
|
|
|
}
|
|
|
else {
|
|
|
- out[1] = 0;
|
|
|
+ containerStart = snapOffset;
|
|
|
}
|
|
|
- return out;
|
|
|
+
|
|
|
+ return childStart - containerStart;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -393,82 +359,53 @@ public class MainCategorySnapper extends RecyclerView.OnFlingListener {
|
|
|
public View findSnapView(LayoutManager layoutManager) {
|
|
|
OrientationHelper helper = getOrientationHelper(layoutManager);
|
|
|
if (null != helper) {
|
|
|
- return findTopView(layoutManager, helper);
|
|
|
+ return findCenterView(layoutManager, helper);
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
/**
|
|
|
- * Return the child view that is currently closest to the top of this parent.
|
|
|
+ * Return the child view that is currently closest to the center of this parent.
|
|
|
*
|
|
|
- * @param layoutManager The {@link LayoutManager} associated with the attached
|
|
|
+ * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
|
|
|
* {@link RecyclerView}.
|
|
|
* @param helper The relevant {@link OrientationHelper} for the attached {@link RecyclerView}.
|
|
|
- * @return the child view that is currently closest to the top of this parent.
|
|
|
+ * @return the child view that is currently closest to the center of this parent.
|
|
|
*/
|
|
|
@Nullable
|
|
|
- private View findTopView(LayoutManager layoutManager, OrientationHelper helper) {
|
|
|
+ private View findCenterView(RecyclerView.LayoutManager layoutManager,
|
|
|
+ OrientationHelper helper) {
|
|
|
int childCount = layoutManager.getChildCount();
|
|
|
if (childCount == 0) {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
View closestChild = null;
|
|
|
- final int top;
|
|
|
+ final int center;
|
|
|
if (layoutManager.getClipToPadding()) {
|
|
|
- top = helper.getStartAfterPadding();
|
|
|
+ center = helper.getStartAfterPadding() + helper.getTotalSpace() / 2;
|
|
|
}
|
|
|
else {
|
|
|
- top = 0;
|
|
|
+ center = helper.getEnd() / 2;
|
|
|
}
|
|
|
int absClosest = Integer.MAX_VALUE;
|
|
|
- int lastIndex = mRecyclerView.getAdapter().getItemCount() - 1;
|
|
|
|
|
|
- int secondItemPositionMax = 0;
|
|
|
-
|
|
|
- for (int i = 0; i < childCount; ++i) {
|
|
|
+ for (int i = 0; i < childCount; i++) {
|
|
|
final View child = layoutManager.getChildAt(i);
|
|
|
- int childStart = helper.getDecoratedStart(child);
|
|
|
- int childEnd = helper.getDecoratedEnd(child);
|
|
|
- int childSize = Math.abs(childEnd - childStart);
|
|
|
- int secondItemPosition = childSize + snapOffset;
|
|
|
- int childCenter = childStart + (helper.getDecoratedMeasurement(child) >> 2);
|
|
|
- int absDistance = Math.abs(childCenter - (top + (childSize >> 2)));
|
|
|
-
|
|
|
- /** if child top is closer than previous closest, set it as closest **/
|
|
|
+ int childCenter = helper.getDecoratedStart(child)
|
|
|
+ + (helper.getDecoratedMeasurement(child) / 2);
|
|
|
+ int absDistance = Math.abs(childCenter - center);
|
|
|
+
|
|
|
+ /** if child center is closer than previous closest, set it as closest **/
|
|
|
if (absDistance < absClosest) {
|
|
|
absClosest = absDistance;
|
|
|
closestChild = child;
|
|
|
}
|
|
|
-
|
|
|
- if (secondItemPositionMax < secondItemPosition) {
|
|
|
- secondItemPositionMax = secondItemPosition;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 마지막이 기준 위치보다 왼쪽에 있으면 녀석을 타겟으로 본다.
|
|
|
- */
|
|
|
- if (mRecyclerView.getChildAdapterPosition(child) == lastIndex) {
|
|
|
-// Log.w("APP# MainCategorySnapper | findTopView", "|" + "padding: " + helper.getStartAfterPadding());
|
|
|
-// Log.w("APP# MainCategorySnapper | findTopView", "|" + "secondItemPositionMax: " + secondItemPositionMax);
|
|
|
-// Log.w("APP# MainCategorySnapper | findTopView", "|" + "secondItemPosition: " + secondItemPosition);
|
|
|
-// Log.w("APP# MainCategorySnapper | findTopView", "|" + "snapOffset: " + snapOffset);
|
|
|
-// Log.w("APP# MainCategorySnapper | findTopView", "|" + "child.x: " + child.getX());
|
|
|
- if (secondItemPositionMax /*- (childSize >> 1)*/ > child.getX()) {
|
|
|
- closestChild = child;
|
|
|
- }
|
|
|
- }
|
|
|
}
|
|
|
return closestChild;
|
|
|
}
|
|
|
|
|
|
- private int distanceToTop(@NonNull LayoutManager layoutManager, @NonNull View targetView, OrientationHelper helper) {
|
|
|
- /**
|
|
|
- * targetView 의 시작 위치(Top)를 반환한다.
|
|
|
- */
|
|
|
- return helper.getDecoratedStart(targetView);
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* Estimates a position to which SnapHelper will try to scroll to in response to a fling.
|
|
|
*
|
|
|
@@ -492,7 +429,7 @@ public class MainCategorySnapper extends RecyclerView.OnFlingListener {
|
|
|
|
|
|
int min = 0;
|
|
|
// 속도가 minVelocity 보다 크면 기본 1칸 이동
|
|
|
- if (Math.abs(velocityX) > minVelocity || Math.abs(velocityY) > minVelocity) {
|
|
|
+ if (Math.abs(velocityX) > minVelocity) {
|
|
|
min = 1;
|
|
|
}
|
|
|
int result = Math.round(distance / distancePerChild);
|
|
|
@@ -543,29 +480,13 @@ public class MainCategorySnapper extends RecyclerView.OnFlingListener {
|
|
|
return RecyclerView.NO_POSITION;
|
|
|
}
|
|
|
|
|
|
- int vDeltaJump, hDeltaJump;
|
|
|
- if (layoutManager.canScrollHorizontally()) {
|
|
|
- hDeltaJump = estimateNextPositionDiffForFling(layoutManager,
|
|
|
- getHorizontalHelper(layoutManager), velocityX, 0);
|
|
|
- if (vectorForEnd.x < 0) {
|
|
|
- hDeltaJump = -hDeltaJump;
|
|
|
- }
|
|
|
- }
|
|
|
- else {
|
|
|
- hDeltaJump = 0;
|
|
|
- }
|
|
|
- if (layoutManager.canScrollVertically()) {
|
|
|
- vDeltaJump = estimateNextPositionDiffForFling(layoutManager,
|
|
|
- getVerticalHelper(layoutManager), 0, velocityY);
|
|
|
- if (vectorForEnd.y < 0) {
|
|
|
- vDeltaJump = -vDeltaJump;
|
|
|
- }
|
|
|
- }
|
|
|
- else {
|
|
|
- vDeltaJump = 0;
|
|
|
+ int hDeltaJump;
|
|
|
+ hDeltaJump = estimateNextPositionDiffForFling(layoutManager, getHorizontalHelper(layoutManager), velocityX, 0);
|
|
|
+ if (vectorForEnd.x < 0) {
|
|
|
+ hDeltaJump = -hDeltaJump;
|
|
|
}
|
|
|
|
|
|
- int deltaJump = layoutManager.canScrollVertically() ? vDeltaJump : hDeltaJump;
|
|
|
+ int deltaJump = hDeltaJump;
|
|
|
if (deltaJump == 0) {
|
|
|
return RecyclerView.NO_POSITION;
|
|
|
}
|
|
|
@@ -620,10 +541,8 @@ public class MainCategorySnapper extends RecyclerView.OnFlingListener {
|
|
|
if (minPosView == null || maxPosView == null) {
|
|
|
return INVALID_DISTANCE;
|
|
|
}
|
|
|
- int start = Math.min(helper.getDecoratedStart(minPosView),
|
|
|
- helper.getDecoratedStart(maxPosView));
|
|
|
- int end = Math.max(helper.getDecoratedEnd(minPosView),
|
|
|
- helper.getDecoratedEnd(maxPosView));
|
|
|
+ int start = Math.min(helper.getDecoratedStart(minPosView), helper.getDecoratedStart(maxPosView));
|
|
|
+ int end = Math.max(helper.getDecoratedEnd(minPosView), helper.getDecoratedEnd(maxPosView));
|
|
|
int distance = end - start;
|
|
|
if (distance == 0) {
|
|
|
return INVALID_DISTANCE;
|
|
|
@@ -632,16 +551,7 @@ public class MainCategorySnapper extends RecyclerView.OnFlingListener {
|
|
|
}
|
|
|
|
|
|
@NonNull
|
|
|
- private OrientationHelper getVerticalHelper(@NonNull LayoutManager layoutManager) {
|
|
|
- if (mVerticalHelper == null || mVerticalHelper.getLayoutManager() != layoutManager) {
|
|
|
- mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager);
|
|
|
- }
|
|
|
- return mVerticalHelper;
|
|
|
- }
|
|
|
-
|
|
|
- @NonNull
|
|
|
- private OrientationHelper getHorizontalHelper(
|
|
|
- @NonNull LayoutManager layoutManager) {
|
|
|
+ private OrientationHelper getHorizontalHelper(@NonNull LayoutManager layoutManager) {
|
|
|
if (mHorizontalHelper == null || mHorizontalHelper.getLayoutManager() != layoutManager) {
|
|
|
mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
|
|
|
}
|