Browse Source

Merge branch 'develop' of https://github.com/swict/LifePlusAndroid into develop

Hasemi 7 years ago
parent
commit
c273eb7569

+ 4 - 2
app/src/main/java/kr/co/zumo/app/lifeplus/helper/ActionBarHelper.java

@@ -35,6 +35,8 @@ import kr.co.zumo.app.lifeplus.view.CustomHeaderBehavior;
  */
 public class ActionBarHelper extends Helper {
 
+  private static final int APP_BAR_LAYOUT_SCROLL_PARAM = AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP;
+
   private Context context;
   private ActionBar actionBar;
   private AppBarLayout appBarLayout;
@@ -262,7 +264,7 @@ public class ActionBarHelper extends Helper {
     else if (isTransparentBackground) {
       if (isScrollable) {
         // todo 스크롤되는 경우에는 appbar behavior 도 수정 필요;
-        params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS);
+        params.setScrollFlags(APP_BAR_LAYOUT_SCROLL_PARAM);
         appBarLayoutParams.setBehavior(new CustomHeaderBehavior());
 
         // todo 스크롤되는 경우에는 appbar behavior 도 수정 필요;
@@ -276,7 +278,7 @@ public class ActionBarHelper extends Helper {
     }
     else {
       if (isScrollable) {
-        params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS/* | AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP*/);
+        params.setScrollFlags(APP_BAR_LAYOUT_SCROLL_PARAM);
         appBarLayoutParams.setBehavior(new CustomHeaderBehavior());
 
         containerLayoutParams.setBehavior(null);

+ 3 - 0
app/src/main/java/kr/co/zumo/app/lifeplus/network/api/RetrofitService.java

@@ -60,6 +60,9 @@ class RetrofitService {
       builder.addInterceptor(logging);
     }
 
+    /**
+     * dev/product 별로 다르게 ssl 처리해줌.
+     */
     new SSLSocketFactoryAssign(builder);
 
     Gson gson = new GsonBuilder().create();

+ 0 - 1
app/src/main/java/kr/co/zumo/app/lifeplus/view/custom/Snapper.java

@@ -26,7 +26,6 @@ public class Snapper extends LinearSnapHelper {
 
   static final float MILLISECONDS_PER_INCH = 100f;
 
-
   private static final float INVALID_DISTANCE = 1f;
   @Nullable
   private OrientationHelper mVerticalHelper;

+ 581 - 0
app/src/main/java/kr/co/zumo/app/lifeplus/view/screen/main/MainContentsSnapper.java

@@ -0,0 +1,581 @@
+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.LinearSmoothScroller;
+import android.support.v7.widget.OrientationHelper;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.LayoutManager;
+import android.support.v7.widget.RecyclerView.SmoothScroller;
+import android.support.v7.widget.RecyclerView.SmoothScroller.ScrollVectorProvider;
+import android.support.v7.widget.SnapHelper;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.View;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.Scroller;
+
+/**
+ * MainContentsSnapper
+ * <pre>
+ * </pre>
+ *
+ * @author 하세미
+ * @version 1.0
+ * @history 하세미   [2018-11-27]   [최초 작성]
+ * @since 2018-11-27
+ */
+public class MainContentsSnapper extends RecyclerView.OnFlingListener {
+
+  private final int snapOffset;
+  private int minVelocity = 200;
+
+  private static final int TIME_MAX = 990;
+  private static final int TIME_MIN = 200;
+
+  static final float MILLISECONDS_PER_INCH = 100f;
+
+  RecyclerView mRecyclerView;
+  private Scroller mGravityScroller;
+
+  private static final float INVALID_DISTANCE = 1f;
+
+  // 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 =
+    new RecyclerView.OnScrollListener() {
+      boolean mScrolled = false;
+
+      @Override
+      public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+        super.onScrollStateChanged(recyclerView, newState);
+        if (newState == RecyclerView.SCROLL_STATE_IDLE && mScrolled) {
+          mScrolled = false;
+          snapToTargetExistingView();
+        }
+      }
+
+      @Override
+      public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+        if (dx != 0 || dy != 0) {
+          mScrolled = true;
+        }
+      }
+    };
+
+  /**
+   * 메인의 세로 스크롤으 스냅을 조절한다.
+   *
+   * @param minVelocity velocity 최소값, 이 이상의 속도에서만 fling 을 처리한다.
+   * @param snapOffset  snap 지점의 offset, - (음수) 이면 화면의 위쪽으로 이동.
+   */
+  public MainContentsSnapper(int minVelocity, int snapOffset) {
+    this.minVelocity = 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;
+    }
+    // fixme ------
+    int minFlingVelocity = 0; //mRecyclerView.getMinFlingVelocity();
+    return (Math.abs(velocityY) > minFlingVelocity || Math.abs(velocityX) > minFlingVelocity)
+      && snapFromFling(layoutManager, velocityX, velocityY);
+  }
+
+  /**
+   * Attaches the {@link SnapHelper} to the provided RecyclerView, by calling
+   * {@link RecyclerView#setOnFlingListener(RecyclerView.OnFlingListener)}.
+   * You can call this method with {@code null} to detach it from the current RecyclerView.
+   *
+   * @param recyclerView The RecyclerView instance to which you want to add this helper or
+   *                     {@code null} if you want to remove SnapHelper from the current
+   *                     RecyclerView.
+   * @throws IllegalArgumentException if there is already a {@link RecyclerView.OnFlingListener}
+   *                                  attached to the provided {@link RecyclerView}.
+   */
+  public void attachToRecyclerView(@Nullable RecyclerView recyclerView)
+    throws IllegalStateException {
+    if (mRecyclerView == recyclerView) {
+      return; // nothing to do
+    }
+    if (mRecyclerView != null) {
+      destroyCallbacks();
+    }
+    mRecyclerView = recyclerView;
+    if (mRecyclerView != null) {
+      setupCallbacks();
+      mGravityScroller = new Scroller(mRecyclerView.getContext(),
+        new DecelerateInterpolator());
+      snapToTargetExistingView();
+    }
+  }
+
+  /**
+   * Called when an instance of a {@link RecyclerView} is attached.
+   */
+  private void setupCallbacks() throws IllegalStateException {
+    if (mRecyclerView.getOnFlingListener() != null) {
+      throw new IllegalStateException("An instance of OnFlingListener already set.");
+    }
+    mRecyclerView.addOnScrollListener(mScrollListener);
+    mRecyclerView.setOnFlingListener(this);
+  }
+
+  /**
+   * Called when the instance of a {@link RecyclerView} is detached.
+   */
+  private void destroyCallbacks() {
+    mRecyclerView.removeOnScrollListener(mScrollListener);
+    mRecyclerView.setOnFlingListener(null);
+  }
+
+  /**
+   * Calculated the estimated scroll distance in each direction given velocities on both axes.
+   *
+   * @param velocityX Fling velocity on the horizontal axis.
+   * @param velocityY Fling velocity on the vertical axis.
+   * @return array holding the calculated distances in x and y directions
+   * respectively.
+   */
+  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);
+    outDist[0] = mGravityScroller.getFinalX();
+    outDist[1] = mGravityScroller.getFinalY();
+    return outDist;
+  }
+
+  /**
+   * Helper method to facilitate for snapping triggered by a fling.
+   *
+   * @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 true if it is handled, false otherwise.
+   */
+  private boolean snapFromFling(@NonNull LayoutManager layoutManager, int velocityX,
+                                int velocityY) {
+    if (!(layoutManager instanceof ScrollVectorProvider)) {
+      return false;
+    }
+
+    SmoothScroller smoothScroller = createScroller(layoutManager);
+    if (smoothScroller == null) {
+      return false;
+    }
+
+    int targetPosition = findTargetSnapPosition(layoutManager, velocityX, velocityY);
+//    Log.w("APP# Snapper | snapFromFling2", "|" + "targetPosition: " + targetPosition);
+    if (targetPosition == RecyclerView.NO_POSITION) {
+      return false;
+    }
+
+    smoothScroller.setTargetPosition(targetPosition);
+    layoutManager.startSmoothScroll(smoothScroller);
+    return true;
+  }
+
+  /**
+   * Snaps to a target view which currently exists in the attached {@link RecyclerView}. This
+   * method is used to snap the view when the {@link RecyclerView} is first attached; when
+   * snapping was triggered by a scroll and when the fling is at its final stages.
+   */
+  void snapToTargetExistingView() {
+    if (mRecyclerView == null) {
+      return;
+    }
+    LayoutManager layoutManager = mRecyclerView.getLayoutManager();
+    if (layoutManager == null) {
+      return;
+    }
+    View snapView = findSnapView(layoutManager);
+    if (snapView == null) {
+      return;
+    }
+
+    /**
+     * 현재 스크롤 포지션에 가장 가까운 뷰로 스크롤 시킨다.
+     * - recyclerView 의 스크롤을 이용하지않고 직접 스크롤시킨다.
+     * - recyclerView 는 너무 빠름.
+     */
+    int position = layoutManager.getPosition(snapView);
+
+    SmoothScroller smoothScroller = createScroller(layoutManager);
+    if (smoothScroller == null) {
+      return;
+    }
+    smoothScroller.setTargetPosition(position);
+    layoutManager.startSmoothScroll(smoothScroller);
+
+  }
+
+  /**
+   * Creates a scroller to be used in the snapping implementation.
+   *
+   * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
+   *                      {@link RecyclerView}.
+   * @return a {@link SmoothScroller} which will handle the scrolling.
+   */
+  @Nullable
+  protected SmoothScroller createScroller(LayoutManager layoutManager) {
+    return createSnapScroller(layoutManager);
+  }
+
+  /**
+   * Creates a scroller to be used in the snapping implementation.
+   *
+   * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
+   *                      {@link RecyclerView}.
+   * @return a {@link LinearSmoothScroller} which will handle the scrolling.
+   * @deprecated use {@link #createScroller(LayoutManager)} instead.
+   */
+  @Nullable
+  @Deprecated
+  protected LinearSmoothScroller createSnapScroller(LayoutManager layoutManager) {
+    if (!(layoutManager instanceof ScrollVectorProvider)) {
+      return null;
+    }
+    return new LinearSmoothScroller(mRecyclerView.getContext()) {
+      @Override
+      protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
+        if (mRecyclerView == null) {
+          // The associated RecyclerView has been removed so there is no action to take.
+          return;
+        }
+        int[] snapDistances = calculateDistanceToFinalSnap(mRecyclerView.getLayoutManager(), targetView);
+        final int dx = snapDistances[0] - snapOffset;
+        final int dy = snapDistances[1] - snapOffset;
+        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));
+          action.update(dx, dy, newTime, mDecelerateInterpolator);
+        }
+      }
+
+      @Override
+      protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
+        return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
+      }
+    };
+  }
+
+  /**
+   * Override this method to snap to a particular point within the target view or the container
+   * view on any axis.
+   * <p>
+   * 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
+   *                      {@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
+   * on horizontal axis and out[1] is the distance on vertical axis.
+   */
+  @SuppressWarnings("WeakerAccess")
+  @Nullable
+  public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView) {
+    int[] out = new int[2];
+    if (layoutManager.canScrollHorizontally()) {
+      out[0] = distanceToTop(layoutManager, targetView, getHorizontalHelper(layoutManager));
+    }
+    else {
+      out[0] = 0;
+    }
+
+    if (layoutManager.canScrollVertically()) {
+      out[1] = distanceToTop(layoutManager, targetView, getVerticalHelper(layoutManager));
+    }
+    else {
+      out[1] = 0;
+    }
+    return out;
+  }
+
+  /**
+   * Override this method to provide a particular target view for snapping.
+   * <p>
+   * This method is called when the {@link SnapHelper} is ready to start snapping and requires
+   * a target view to snap to. It will be explicitly called when the scroll state becomes idle
+   * after a scroll. It will also be called when the {@link SnapHelper} is preparing to snap
+   * after a fling and requires a reference view from the current set of child views.
+   * <p>
+   * If this method returns {@code null}, SnapHelper will not snap to any view.
+   *
+   * @param layoutManager the {@link RecyclerView.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));
+    }
+    return null;
+  }
+
+  /**
+   * Return the child view that is currently closest to the top of this parent.
+   *
+   * @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.
+   */
+  @Nullable
+  private View findTopView(RecyclerView.LayoutManager layoutManager, OrientationHelper helper) {
+    int childCount = layoutManager.getChildCount();
+    if (childCount == 0) {
+      return null;
+    }
+
+    View closestChild = null;
+    final int top;
+    if (layoutManager.getClipToPadding()) {
+      top = helper.getStartAfterPadding();
+    }
+    else {
+      top = 0;
+    }
+    int absClosest = Integer.MAX_VALUE;
+
+    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 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  **/
+      if (absDistance < absClosest) {
+        absClosest = absDistance;
+        closestChild = child;
+      }
+    }
+    return closestChild;
+  }
+
+  private int distanceToTop(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView, OrientationHelper helper) {
+    /**
+     * targetView 의 시작 위치(Top)를 반환한다.
+     */
+    return helper.getDecoratedStart(targetView);
+  }
+
+  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 {
+      containerCenter = helper.getEnd() / 2;
+    }
+    return childCenter - containerCenter;
+  }
+
+  /**
+   * 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
+   *                      {@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,
+                                               OrientationHelper helper, int velocityX, int velocityY) {
+    int[] distances = calculateScrollDistance(velocityX, velocityY);
+    float distancePerChild = computeDistancePerChild(layoutManager, helper);
+    if (distancePerChild <= 0) {
+      return 0;
+    }
+    int distance =
+      Math.abs(distances[0]) > Math.abs(distances[1]) ? distances[0] : distances[1];
+//    return (int) Math.round(distance / distancePerChild);
+
+    // fixme ------
+    int min = 0;
+    // 속도가 minVelocity 보다 크면 기본 1칸 이동
+    if (Math.abs(velocityX) > minVelocity || Math.abs(velocityY) > minVelocity) {
+      min = 1;
+    }
+    int result = Math.round(distance / distancePerChild);
+    if (result == 0 && min > 0) {
+      result = distance > 0 ? min : -min;
+    }
+
+    return result;
+  }
+
+  /**
+   * Override to provide a particular adapter target position for snapping.
+   *
+   * @param layoutManager the {@link RecyclerView.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,
+                                    int velocityY) {
+    if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
+      return RecyclerView.NO_POSITION;
+    }
+
+    final int itemCount = layoutManager.getItemCount();
+    if (itemCount == 0) {
+      return RecyclerView.NO_POSITION;
+    }
+
+    final View currentView = findSnapView(layoutManager);
+    if (currentView == null) {
+      return RecyclerView.NO_POSITION;
+    }
+
+    final int currentPosition = layoutManager.getPosition(currentView);
+    if (currentPosition == RecyclerView.NO_POSITION) {
+      return RecyclerView.NO_POSITION;
+    }
+
+    RecyclerView.SmoothScroller.ScrollVectorProvider vectorProvider =
+      (RecyclerView.SmoothScroller.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.
+    PointF vectorForEnd = vectorProvider.computeScrollVectorForPosition(itemCount - 1);
+    if (vectorForEnd == null) {
+      // cannot get a vector for the given position.
+      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 deltaJump = layoutManager.canScrollVertically() ? vDeltaJump : hDeltaJump;
+    if (deltaJump == 0) {
+      return RecyclerView.NO_POSITION;
+    }
+
+    int targetPos = currentPosition + deltaJump;
+    if (targetPos < 0) {
+      targetPos = 0;
+    }
+    if (targetPos >= itemCount) {
+      targetPos = itemCount - 1;
+    }
+    return targetPos;
+  }
+
+  /**
+   * Computes an average pixel value to pass a single child.
+   * <p>
+   * Returns a negative value if it cannot be calculated.
+   *
+   * @param layoutManager The {@link RecyclerView.LayoutManager} associated with the attached
+   *                      {@link RecyclerView}.
+   * @param helper        The relevant {@link OrientationHelper} for the attached
+   *                      {@link RecyclerView.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) {
+    View minPosView = null;
+    View maxPosView = null;
+    int minPos = Integer.MAX_VALUE;
+    int maxPos = Integer.MIN_VALUE;
+    int childCount = layoutManager.getChildCount();
+    if (childCount == 0) {
+      return INVALID_DISTANCE;
+    }
+
+    for (int i = 0; i < childCount; i++) {
+      View child = layoutManager.getChildAt(i);
+      final int pos = layoutManager.getPosition(child);
+      if (pos == RecyclerView.NO_POSITION) {
+        continue;
+      }
+      if (pos < minPos) {
+        minPos = pos;
+        minPosView = child;
+      }
+      if (pos > maxPos) {
+        maxPos = pos;
+        maxPosView = child;
+      }
+    }
+    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 distance = end - start;
+    if (distance == 0) {
+      return INVALID_DISTANCE;
+    }
+    return 1f * distance / ((maxPos - minPos) + 1);
+  }
+
+  @NonNull
+  private OrientationHelper getVerticalHelper(@NonNull RecyclerView.LayoutManager layoutManager) {
+    if (mVerticalHelper == null || mVerticalHelper.getLayoutManager() != layoutManager) {
+      mVerticalHelper = OrientationHelper.createVerticalHelper(layoutManager);
+    }
+    return mVerticalHelper;
+  }
+
+  @NonNull
+  private OrientationHelper getHorizontalHelper(
+    @NonNull RecyclerView.LayoutManager layoutManager) {
+    if (mHorizontalHelper == null || mHorizontalHelper.getLayoutManager() != layoutManager) {
+      mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
+    }
+    return mHorizontalHelper;
+  }
+}

+ 6 - 16
app/src/main/java/kr/co/zumo/app/lifeplus/view/screen/main/MainFragment.java

@@ -6,7 +6,6 @@ package kr.co.zumo.app.lifeplus.view.screen.main;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
-import android.support.design.widget.AppBarLayout;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.view.LayoutInflater;
@@ -20,10 +19,10 @@ import java.util.ArrayList;
 import kr.co.zumo.app.R;
 import kr.co.zumo.app.lifeplus.bean.api.MainContentsBean;
 import kr.co.zumo.app.lifeplus.helper.ActionBarHelper;
-import kr.co.zumo.app.lifeplus.helper.IActionBarListener;
 import kr.co.zumo.app.lifeplus.model.SuperModel;
 import kr.co.zumo.app.lifeplus.supervisor.ContentsFlagHelper;
 import kr.co.zumo.app.lifeplus.util.AppUtil;
+import kr.co.zumo.app.lifeplus.util.ResourceUtil;
 import kr.co.zumo.app.lifeplus.util.StringUtil;
 import kr.co.zumo.app.lifeplus.view.Event;
 import kr.co.zumo.app.lifeplus.view.screen.FragmentBase;
@@ -50,13 +49,6 @@ public class MainFragment extends FragmentBase<MainPresenter> implements IMainVi
     }
   };
 
-  private IActionBarListener.Scroll actionBarScrollListener = new IActionBarListener.Scroll() {
-    @Override
-    public void onScroll(AppBarLayout appBarLayout, int verticalOffset) {
-      adapter.onScrolled(contentsRecyclerView, verticalOffset, verticalOffset);
-    }
-  };
-
   @Nullable
   @Override
   protected View onAfterCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@@ -68,12 +60,10 @@ public class MainFragment extends FragmentBase<MainPresenter> implements IMainVi
 
     contentsRecyclerView = findViewById(R.id.recycler_view_main);
 
-//    if(contentsRecyclerView.getOnFlingListener() == null) {
-//      Snapper snapper = new Snapper(contentsRecyclerView, 1000);
-//      snapper.attachToRecyclerView(contentsRecyclerView);
-//    }
-
-//    actionBarManager.setScrollListener(actionBarScrollListener);
+    if (contentsRecyclerView.getOnFlingListener() == null) {
+      MainContentsSnapper snapper = new MainContentsSnapper(1000, -ResourceUtil.dpToPx(25));
+      snapper.attachToRecyclerView(contentsRecyclerView);
+    }
 
     if (AppUtil.isDebug()) {
 
@@ -217,7 +207,7 @@ public class MainFragment extends FragmentBase<MainPresenter> implements IMainVi
 
     if (null == adapter) {
       adapter = new MainContentsAdapter(getContext(), data, event -> {
-        if(event.getEventId() == Event.SCROLL) {
+        if (event.getEventId() == Event.SCROLL) {
           contentsRecyclerView.smoothScrollToPosition(1);
         }
         else {

+ 17 - 6
app/src/main/res/layout/main_contents_category_image.xml

@@ -34,26 +34,37 @@
     android:layout_height="@dimen/main_contents_image_height"
     android:background="@drawable/rectangle_dim_main_3"/>
 
+  <android.support.constraint.Guideline
+    android:id="@+id/guide_vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    app:layout_constraintGuide_begin="32dp"/>
+
+  <android.support.constraint.Guideline
+    android:id="@+id/guide_horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+    app:layout_constraintGuide_begin="218dp"/>
   <TextView
     android:id="@+id/text_big"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_marginStart="32dp"
-    android:layout_marginTop="218dp"
     android:lineSpacingExtra="4dp"
     android:textColor="#ffffff"
     android:textSize="19sp"
     android:textStyle="bold"
-    app:layout_constraintStart_toStartOf="parent"
-    app:layout_constraintTop_toTopOf="parent"
+    app:layout_constraintStart_toStartOf="@+id/guide_vertical"
+    app:layout_constraintTop_toTopOf="@+id/guide_horizontal"
     tools:text="며느리도 모르는\n신당동 핫플"/>
 
   <TextView
     android:id="@+id/text_small"
     android:layout_width="0dp"
     android:layout_height="wrap_content"
-    android:layout_marginStart="32dp"
     android:layout_marginBottom="28.5dp"
+    android:layout_marginEnd="20dp"
     android:lineSpacingExtra="9.5sp"
     android:maxLines="1"
     android:textColor="#ffffff"
@@ -62,6 +73,6 @@
     app:layout_constraintBottom_toBottomOf="parent"
     app:layout_constraintEnd_toEndOf="@+id/mask_2"
     app:layout_constraintHorizontal_bias="0"
-    app:layout_constraintStart_toStartOf="parent"
+    app:layout_constraintStart_toStartOf="@+id/guide_vertical"
     tools:text="신당동 JMT 리스트 신당동 \nJMT 리스트 dier8234 lkjsdf"/>
 </android.support.constraint.ConstraintLayout>

File diff suppressed because it is too large
+ 1 - 4
app/src/sandbox/java/kr/co/zumo/app/lifeplus/network/api/LifeplusAPIService.java