Kaynağa Gözat

[메인][New] 세로 스크롤 중앙으로 정렬

hyodong.min 7 yıl önce
ebeveyn
işleme
9989904759

+ 78 - 156
app/src/main/java/kr/co/zumo/app/lifeplus/view/screen/main/MainContentsSnapper.java

@@ -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,12 +13,10 @@ 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.view.animation.Interpolator;
 import android.widget.Scroller;
 
-import kr.co.zumo.app.lifeplus.util.ResourceUtil;
-
 /**
  * MainContentsSnapper
  * <pre>
@@ -33,24 +30,26 @@ import kr.co.zumo.app.lifeplus.util.ResourceUtil;
 public class MainContentsSnapper extends RecyclerView.OnFlingListener {
 
   private final int snapOffset;
-  private int minVelocity = 200;
-  private int maxVelocity = 5000;
+  private int minVelocity;
 
   private static final int TIME_MAX = 990;
   private static final int TIME_MIN = 250;
 
-  static final float MILLISECONDS_PER_INCH = 100f;
+  private static final float MILLISECONDS_PER_INCH = 500f;    // fling 속도
+  private static final float MILLISECONDS_PER_INCH_SCROLL = 200f;   // smoothScrollToPosition() 을 이용할 때 스크롤 속도
+  private static final float SCROLL_FRICTION = 5f; //1.5f  // 마찰계수를 늘려 준다. 최대 이동 거리가 줄어듬.
 
-  RecyclerView mRecyclerView;
+  private RecyclerView mRecyclerView;
   private Scroller mGravityScroller;
 
   private static final float INVALID_DISTANCE = 1f;
 
+  private static final float INTERPOLATOR_FACTOR = 1.5f; // 1.5f
+
+  protected Interpolator interpolator = new DecelerateInterpolator(INTERPOLATOR_FACTOR);
   // Orientation helpers are lazily created per LayoutManager.
   @Nullable
   private OrientationHelper mVerticalHelper;
-  @Nullable
-  private OrientationHelper mHorizontalHelper;
 
   // Handles the snap on scroll case.
   private final RecyclerView.OnScrollListener mScrollListener =
@@ -68,7 +67,6 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
 
       @Override
       public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
-//        Log.i("APP# MainContentsSnapper | onScrolledVertical", "|" + "dx: " + dx);
         if (dx != 0 || dy != 0) {
           mScrolled = true;
         }
@@ -83,25 +81,12 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
    * @param snapOffset  snap 지점의 offset, - (음수) 이면 화면의 위쪽으로 이동.
    */
   public MainContentsSnapper(int minVelocity, int snapOffset) {
-    this.minVelocity = minVelocity;
+    this.minVelocity = modifyVelocity(minVelocity);
     this.snapOffset = snapOffset;
   }
 
-  @Override
-  public boolean onFling(int velocityX, int velocityY) {
-    Log.w("APP# MainContentsSnapper | onFling", "|" + "velocityY: " + velocityY);
-
-    LayoutManager layoutManager = mRecyclerView.getLayoutManager();
-    if (layoutManager == null) {
-      return false;
-    }
-    RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
-    if (adapter == null) {
-      return false;
-    }
-    int minFlingVelocity = 0; //mRecyclerView.getMinFlingVelocity();
-    return (Math.abs(velocityY) > minFlingVelocity || Math.abs(velocityX) > minFlingVelocity)
-      && snapFromFling(layoutManager, velocityX, velocityY);
+  private int modifyVelocity(int velocity) {
+    return velocity; // >> 2;
   }
 
   /**
@@ -125,8 +110,8 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
     mRecyclerView = recyclerView;
     if (mRecyclerView != null) {
       setupCallbacks();
-      mGravityScroller = new Scroller(mRecyclerView.getContext(), new DecelerateInterpolator(20f));
-      mGravityScroller.setFriction(ViewConfiguration.getScrollFriction() * 10);    // 마찰계수를 늘려 줌
+      mGravityScroller = new Scroller(mRecyclerView.getContext(), new DecelerateInterpolator());
+      mGravityScroller.setFriction(SCROLL_FRICTION);
       snapToTargetExistingView();
     }
   }
@@ -150,6 +135,23 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
     mRecyclerView.setOnFlingListener(null);
   }
 
+  @Override
+  public boolean onFling(int velocityX, int velocityY) {
+//    Log.w("APP# MainContentsSnapper | onFling", "|" + "velocityY: ==============================> " + velocityY);
+    velocityY = modifyVelocity(velocityY);
+
+    LayoutManager layoutManager = mRecyclerView.getLayoutManager();
+    if (layoutManager == null) {
+      return false;
+    }
+    RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
+    if (adapter == null) {
+      return false;
+    }
+    int minFlingVelocity = 0; //mRecyclerView.getMinFlingVelocity();
+    return Math.abs(velocityY) > minFlingVelocity && snapFromFling(layoutManager, velocityX, velocityY);
+  }
+
   /**
    * Calculated the estimated scroll distance in each direction given velocities on both axes.
    *
@@ -186,7 +188,7 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
     }
 
     int targetPosition = findTargetSnapPosition(layoutManager, velocityX, velocityY);
-//    Log.w("APP# MainContentsSnapper | snapFromFling2", "|" + "targetPosition: " + targetPosition);
+//    Log.w("APP# MainContentsSnapper | snapFromFling2", "|" + "targetPosition: ----------------------------------> " + targetPosition);
     if (targetPosition == RecyclerView.NO_POSITION) {
       return false;
     }
@@ -244,7 +246,7 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
       return;
     }
 
-    SmoothScroller smoothScroller = createScroller(layoutManager, 200f);
+    SmoothScroller smoothScroller = createScroller(layoutManager, MILLISECONDS_PER_INCH_SCROLL);
     if (smoothScroller == null) {
       return;
     }
@@ -290,7 +292,7 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
           // The associated RecyclerView has been removed so there is no action to take.
           return;
         }
-        int offset = snapOffset;
+        int offset = 0; //snapOffset;
         int viewIndex = mRecyclerView.getChildAdapterPosition(targetView);
         if (viewIndex == (mRecyclerView.getAdapter().getItemCount() - 1)) {
           /**
@@ -308,6 +310,7 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
 //          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;
@@ -315,28 +318,15 @@ public class MainContentsSnapper 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)));
         Log.i("APP# MainContentsSnapper | onTargetFound", "|" + "time: ---> " + time);
         Log.i("APP# MainContentsSnapper | onTargetFound", "|" + "dy: ---> " + dy);
         if (time > 0) {
           int newTime = Math.min(TIME_MAX, Math.max(time, TIME_MIN));
-          action.update(dx, dy, newTime, mDecelerateInterpolator);
+          action.update(dx, dy, newTime, interpolator);
         }
       }
 
@@ -348,13 +338,7 @@ 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;
+    return getVerticalHelper(layoutManager);
   }
 
   /**
@@ -374,20 +358,21 @@ public class MainContentsSnapper 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] = 0;
+    out[1] = distanceToCenter(layoutManager, targetView, getVerticalHelper(layoutManager));
+    return out;
+  }
 
-    if (layoutManager.canScrollVertically()) {
-      out[1] = distanceToTop(layoutManager, targetView, getVerticalHelper(layoutManager));
+  private int distanceToCenter(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView, OrientationHelper helper) {
+    final int childCenter = helper.getDecoratedStart(targetView) + (helper.getDecoratedMeasurement(targetView) / 2);
+    final int containerCenter;
+    if (layoutManager.getClipToPadding()) {
+      containerCenter = helper.getStartAfterPadding() + helper.getTotalSpace() / 2;
     }
     else {
-      out[1] = 0;
+      containerCenter = helper.getEnd() / 2;
     }
-    return out;
+    return childCenter - containerCenter;
   }
 
   /**
@@ -409,86 +394,52 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
   public View findSnapView(LayoutManager layoutManager) {
     OrientationHelper helper = getOrientationHelper(layoutManager);
     if (null != helper) {
-      return findTopView(layoutManager, helper);
+      return findCenterView(layoutManager, getVerticalHelper(layoutManager));
     }
     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 dp100 = ResourceUtil.dpToPx(100);
-    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;
-      }
-
-      /**
-       * 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 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.
    *
@@ -503,15 +454,13 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
     int[] distances = calculateScrollDistance(velocityX, velocityY);
     float distancePerChild = computeDistancePerChild(layoutManager, helper);
     if (distancePerChild <= 0) {
-      return 0;
+      return 1;
     }
-    int distance =
-      Math.abs(distances[0]) > Math.abs(distances[1]) ? distances[0] : distances[1];
-//    return (int) Math.round(distance / distancePerChild);
+    int distance = Math.abs(distances[0]) > Math.abs(distances[1]) ? distances[0] : distances[1];
 
-    int min = 0;
+    int min = 1;
     // 속도가 minVelocity 보다 크면 기본 1칸 이동
-    if (Math.abs(velocityX) > minVelocity || Math.abs(velocityY) > minVelocity) {
+    if (Math.abs(velocityY) > minVelocity) {
       min = 1;
     }
     int result = Math.round(distance / distancePerChild);
@@ -532,8 +481,7 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
    * @return the target adapter position to you want to snap or {@link RecyclerView#NO_POSITION}
    * if no snapping should happen
    */
-  public int findTargetSnapPosition(LayoutManager layoutManager, int velocityX,
-                                    int velocityY) {
+  public int findTargetSnapPosition(LayoutManager layoutManager, int velocityX, int velocityY) {
     if (!(layoutManager instanceof ScrollVectorProvider)) {
       return RecyclerView.NO_POSITION;
     }
@@ -563,29 +511,14 @@ public class MainContentsSnapper 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 vDeltaJump;
+
+    vDeltaJump = estimateNextPositionDiffForFling(layoutManager, getVerticalHelper(layoutManager), 0, velocityY);
+    if (vectorForEnd.y < 0) {
+      vDeltaJump = -vDeltaJump;
     }
 
-    int deltaJump = layoutManager.canScrollVertically() ? vDeltaJump : hDeltaJump;
+    int deltaJump = vDeltaJump;
     if (deltaJump == 0) {
       return RecyclerView.NO_POSITION;
     }
@@ -640,10 +573,8 @@ public class MainContentsSnapper 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;
@@ -658,13 +589,4 @@ public class MainContentsSnapper extends RecyclerView.OnFlingListener {
     }
     return mVerticalHelper;
   }
-
-  @NonNull
-  private OrientationHelper getHorizontalHelper(
-    @NonNull LayoutManager layoutManager) {
-    if (mHorizontalHelper == null || mHorizontalHelper.getLayoutManager() != layoutManager) {
-      mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
-    }
-    return mHorizontalHelper;
-  }
 }

+ 4 - 2
app/src/main/java/kr/co/zumo/app/lifeplus/view/screen/main/MainFragment.java

@@ -92,7 +92,7 @@ public class MainFragment extends FragmentBase<MainPresenter> implements IMainVi
     indicator = findViewById(R.id.page_indicator);
     contentsRecyclerView = findViewById(R.id.recycler_view_main);
     contentsRecyclerView.addOnScrollListener(scrollListener);
-    
+
     indicator.setAlpha(0f);
     ObjectAnimator indicatorAnimator = ObjectAnimator.ofFloat(indicator, "alpha", 1f);
     indicatorAnimator.setStartDelay(600);
@@ -102,7 +102,9 @@ public class MainFragment extends FragmentBase<MainPresenter> implements IMainVi
     AnimatorManager.getInstance().add(indicatorAnimator);
 
     if (contentsRecyclerView.getOnFlingListener() == null) {
-      snapper = new MainContentsSnapper(1000, statusBarHeight + ResourceUtil.dpToPx(40));
+      int screenHeight = SuperModel.getInstance().getScreenHeight();
+      int categoryHeight = ResourceUtil.getDimension(R.dimen.main_contents_category_height);
+      snapper = new MainContentsSnapper(1000, statusBarHeight + (((screenHeight - statusBarHeight) - categoryHeight) >> 1));
       snapper.attachToRecyclerView(contentsRecyclerView);
     }