|
|
@@ -3,6 +3,7 @@ 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;
|
|
|
@@ -15,6 +16,8 @@ import android.view.View;
|
|
|
import android.view.animation.DecelerateInterpolator;
|
|
|
import android.widget.Scroller;
|
|
|
|
|
|
+import kr.co.zumo.app.lifeplus.util.ResourceUtil;
|
|
|
+
|
|
|
/**
|
|
|
* MainContentsSnapper
|
|
|
* <pre>
|
|
|
@@ -31,7 +34,7 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
private int minVelocity = 200;
|
|
|
|
|
|
private static final int TIME_MAX = 990;
|
|
|
- private static final int TIME_MIN = 200;
|
|
|
+ private static final int TIME_MIN = 250;
|
|
|
|
|
|
static final float MILLISECONDS_PER_INCH = 100f;
|
|
|
|
|
|
@@ -211,6 +214,13 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+// int[] snapDistance = calculateDistanceToFinalSnap(layoutManager, snapView);
|
|
|
+// snapDistance[0] -= snapOffset; // 마지막 뷰일 경우 snapOffset 을 적용하면 부자연스럽게 스크롤 됨
|
|
|
+// snapDistance[1] -= snapOffset;
|
|
|
+// if (snapDistance[0] != 0 || snapDistance[1] != 0) {
|
|
|
+// mRecyclerView.smoothScrollBy(snapDistance[0], snapDistance[1], new DecelerateInterpolator());
|
|
|
+// }
|
|
|
+
|
|
|
/**
|
|
|
* 현재 스크롤 포지션에 가장 가까운 뷰로 스크롤 시킨다.
|
|
|
* - recyclerView 의 스크롤을 이용하지않고 직접 스크롤시킨다.
|
|
|
@@ -230,7 +240,7 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
/**
|
|
|
* Creates a scroller to be used in the snapping implementation.
|
|
|
*
|
|
|
- * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
|
|
|
+ * @param layoutManager The {@link LayoutManager} associated with the attached
|
|
|
* {@link RecyclerView}.
|
|
|
* @return a {@link SmoothScroller} which will handle the scrolling.
|
|
|
*/
|
|
|
@@ -242,7 +252,7 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
/**
|
|
|
* Creates a scroller to be used in the snapping implementation.
|
|
|
*
|
|
|
- * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
|
|
|
+ * @param layoutManager The {@link LayoutManager} associated with the attached
|
|
|
* {@link RecyclerView}.
|
|
|
* @return a {@link LinearSmoothScroller} which will handle the scrolling.
|
|
|
* @deprecated use {@link #createScroller(LayoutManager)} instead.
|
|
|
@@ -260,9 +270,46 @@ public class MainContentsSnapper 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 = screenHeight - 마지막 뷰 사이즈
|
|
|
+ */
|
|
|
+ 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 = snapDistances[0] - snapOffset;
|
|
|
- final int dy = snapDistances[1] - snapOffset;
|
|
|
+ final int dx;
|
|
|
+ final int dy;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 방향을 고려해서 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];
|
|
|
+ }
|
|
|
+
|
|
|
final int time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy)));
|
|
|
if (time > 0) {
|
|
|
int newTime = Math.min(TIME_MAX, Math.max(time, TIME_MIN));
|
|
|
@@ -277,6 +324,16 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
+ private OrientationHelper getOrientationHelper(LayoutManager layoutManager) {
|
|
|
+ if (layoutManager.canScrollVertically()) {
|
|
|
+ return getVerticalHelper(layoutManager);
|
|
|
+ }
|
|
|
+ else if (layoutManager.canScrollHorizontally()) {
|
|
|
+ return getHorizontalHelper(layoutManager);
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Override this method to snap to a particular point within the target view or the container
|
|
|
* view on any axis.
|
|
|
@@ -284,7 +341,7 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
* This method is called when the {@link SnapHelper} has intercepted a fling and it needs
|
|
|
* to know the exact distance required to scroll by in order to snap to the target view.
|
|
|
*
|
|
|
- * @param layoutManager the {@link RecyclerView.LayoutManager} associated with the attached
|
|
|
+ * @param layoutManager the {@link LayoutManager} associated with the attached
|
|
|
* {@link RecyclerView}
|
|
|
* @param targetView the target view that is chosen as the view to snap
|
|
|
* @return the output coordinates the put the result into. out[0] is the distance
|
|
|
@@ -292,7 +349,7 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
*/
|
|
|
@SuppressWarnings("WeakerAccess")
|
|
|
@Nullable
|
|
|
- public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView) {
|
|
|
+ public int[] calculateDistanceToFinalSnap(@NonNull LayoutManager layoutManager, @NonNull View targetView) {
|
|
|
int[] out = new int[2];
|
|
|
if (layoutManager.canScrollHorizontally()) {
|
|
|
out[0] = distanceToTop(layoutManager, targetView, getHorizontalHelper(layoutManager));
|
|
|
@@ -320,18 +377,16 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
* <p>
|
|
|
* If this method returns {@code null}, SnapHelper will not snap to any view.
|
|
|
*
|
|
|
- * @param layoutManager the {@link RecyclerView.LayoutManager} associated with the attached
|
|
|
+ * @param layoutManager the {@link LayoutManager} associated with the attached
|
|
|
* {@link RecyclerView}
|
|
|
* @return the target view to which to snap on fling or end of scroll
|
|
|
*/
|
|
|
@SuppressWarnings("WeakerAccess")
|
|
|
@Nullable
|
|
|
- public View findSnapView(RecyclerView.LayoutManager layoutManager) {
|
|
|
- if (layoutManager.canScrollVertically()) {
|
|
|
- return findTopView(layoutManager, getVerticalHelper(layoutManager));
|
|
|
- }
|
|
|
- else if (layoutManager.canScrollHorizontally()) {
|
|
|
- return findTopView(layoutManager, getHorizontalHelper(layoutManager));
|
|
|
+ public View findSnapView(LayoutManager layoutManager) {
|
|
|
+ OrientationHelper helper = getOrientationHelper(layoutManager);
|
|
|
+ if (null != helper) {
|
|
|
+ return findTopView(layoutManager, helper);
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
@@ -339,13 +394,13 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
/**
|
|
|
* Return the child view that is currently closest to the top of this parent.
|
|
|
*
|
|
|
- * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
|
|
|
+ * @param layoutManager The {@link 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.
|
|
|
*/
|
|
|
@Nullable
|
|
|
- private View findTopView(RecyclerView.LayoutManager layoutManager, OrientationHelper helper) {
|
|
|
+ private View findTopView(LayoutManager layoutManager, OrientationHelper helper) {
|
|
|
int childCount = layoutManager.getChildCount();
|
|
|
if (childCount == 0) {
|
|
|
return null;
|
|
|
@@ -360,12 +415,17 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
top = 0;
|
|
|
}
|
|
|
int absClosest = Integer.MAX_VALUE;
|
|
|
+ int dp100 = ResourceUtil.dpToPx(100);
|
|
|
+ int lastIndex = mRecyclerView.getAdapter().getItemCount() - 1;
|
|
|
|
|
|
- for (int i = 0; i < childCount; i++) {
|
|
|
+ int secondItemPositionMax = 0;
|
|
|
+
|
|
|
+ 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) + snapOffset;
|
|
|
+ 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)));
|
|
|
|
|
|
@@ -374,11 +434,32 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
absClosest = absDistance;
|
|
|
closestChild = child;
|
|
|
}
|
|
|
+
|
|
|
+ if (secondItemPositionMax < secondItemPosition) {
|
|
|
+ secondItemPositionMax = secondItemPosition;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * todo 화면에 2 개 이상 표시될 수 있을 경우 처리 필요
|
|
|
+ */
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 마지막이 -100dp 이상 보여질 경우 타겟으로 설정
|
|
|
+ */
|
|
|
+ if (mRecyclerView.getChildAdapterPosition(child) == lastIndex) {
|
|
|
+// Log.w("APP# MainContentsSnapper | findTopView", "|" + "secondItemPositionMax: " + secondItemPositionMax);
|
|
|
+// Log.w("APP# MainContentsSnapper | findTopView", "|" + "secondItemPosition: " + secondItemPosition);
|
|
|
+// Log.w("APP# MainContentsSnapper | findTopView", "|" + "snapOffset: " + snapOffset);
|
|
|
+// Log.w("APP# MainContentsSnapper | findTopView", "|" + "child.y: " + child.getY());
|
|
|
+ if (helper.getEnd() - (childSize - dp100) > child.getY()) {
|
|
|
+ closestChild = child;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
return closestChild;
|
|
|
}
|
|
|
|
|
|
- private int distanceToTop(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView, OrientationHelper helper) {
|
|
|
+ private int distanceToTop(@NonNull LayoutManager layoutManager, @NonNull View targetView, OrientationHelper helper) {
|
|
|
/**
|
|
|
* targetView 의 시작 위치(Top)를 반환한다.
|
|
|
*/
|
|
|
@@ -388,14 +469,14 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
/**
|
|
|
* Estimates a position to which SnapHelper will try to scroll to in response to a fling.
|
|
|
*
|
|
|
- * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
|
|
|
+ * @param layoutManager The {@link LayoutManager} associated with the attached
|
|
|
* {@link RecyclerView}.
|
|
|
* @param helper The {@link OrientationHelper} that is created from the LayoutManager.
|
|
|
* @param velocityX The velocity on the x axis.
|
|
|
* @param velocityY The velocity on the y axis.
|
|
|
* @return The diff between the target scroll position and the current position.
|
|
|
*/
|
|
|
- private int estimateNextPositionDiffForFling(RecyclerView.LayoutManager layoutManager,
|
|
|
+ private int estimateNextPositionDiffForFling(LayoutManager layoutManager,
|
|
|
OrientationHelper helper, int velocityX, int velocityY) {
|
|
|
int[] distances = calculateScrollDistance(velocityX, velocityY);
|
|
|
float distancePerChild = computeDistancePerChild(layoutManager, helper);
|
|
|
@@ -423,16 +504,16 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
/**
|
|
|
* Override to provide a particular adapter target position for snapping.
|
|
|
*
|
|
|
- * @param layoutManager the {@link RecyclerView.LayoutManager} associated with the attached
|
|
|
+ * @param layoutManager the {@link LayoutManager} associated with the attached
|
|
|
* {@link RecyclerView}
|
|
|
* @param velocityX fling velocity on the horizontal axis
|
|
|
* @param velocityY fling velocity on the vertical axis
|
|
|
* @return the target adapter position to you want to snap or {@link RecyclerView#NO_POSITION}
|
|
|
* if no snapping should happen
|
|
|
*/
|
|
|
- public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX,
|
|
|
+ public int findTargetSnapPosition(LayoutManager layoutManager, int velocityX,
|
|
|
int velocityY) {
|
|
|
- if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
|
|
|
+ if (!(layoutManager instanceof ScrollVectorProvider)) {
|
|
|
return RecyclerView.NO_POSITION;
|
|
|
}
|
|
|
|
|
|
@@ -451,8 +532,7 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
return RecyclerView.NO_POSITION;
|
|
|
}
|
|
|
|
|
|
- RecyclerView.SmoothScroller.ScrollVectorProvider vectorProvider =
|
|
|
- (RecyclerView.SmoothScroller.ScrollVectorProvider) layoutManager;
|
|
|
+ ScrollVectorProvider vectorProvider = (ScrollVectorProvider) layoutManager;
|
|
|
// deltaJumps sign comes from the velocity which may not match the order of children in
|
|
|
// the LayoutManager. To overcome this, we ask for a vector from the LayoutManager to
|
|
|
// get the direction.
|
|
|
@@ -504,15 +584,14 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
* <p>
|
|
|
* Returns a negative value if it cannot be calculated.
|
|
|
*
|
|
|
- * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
|
|
|
+ * @param layoutManager The {@link LayoutManager} associated with the attached
|
|
|
* {@link RecyclerView}.
|
|
|
* @param helper The relevant {@link OrientationHelper} for the attached
|
|
|
- * {@link RecyclerView.LayoutManager}.
|
|
|
+ * {@link LayoutManager}.
|
|
|
* @return A float value that is the average number of pixels needed to scroll by one view in
|
|
|
* the relevant direction.
|
|
|
*/
|
|
|
- private float computeDistancePerChild(RecyclerView.LayoutManager layoutManager,
|
|
|
- OrientationHelper helper) {
|
|
|
+ private float computeDistancePerChild(LayoutManager layoutManager, OrientationHelper helper) {
|
|
|
View minPosView = null;
|
|
|
View maxPosView = null;
|
|
|
int minPos = Integer.MAX_VALUE;
|
|
|
@@ -552,7 +631,7 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
}
|
|
|
|
|
|
@NonNull
|
|
|
- private OrientationHelper getVerticalHelper(@NonNull RecyclerView.LayoutManager layoutManager) {
|
|
|
+ private OrientationHelper getVerticalHelper(@NonNull LayoutManager layoutManager) {
|
|
|
if (mVerticalHelper == null || mVerticalHelper.getLayoutManager() != layoutManager) {
|
|
|
mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager);
|
|
|
}
|
|
|
@@ -561,7 +640,7 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
|
|
|
|
|
|
@NonNull
|
|
|
private OrientationHelper getHorizontalHelper(
|
|
|
- @NonNull RecyclerView.LayoutManager layoutManager) {
|
|
|
+ @NonNull LayoutManager layoutManager) {
|
|
|
if (mHorizontalHelper == null || mHorizontalHelper.getLayoutManager() != layoutManager) {
|
|
|
mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
|
|
|
}
|