|
|
@@ -1,29 +1,20 @@
|
|
|
package kr.co.zumo.app.lifeplus.view.custom.category.banner;
|
|
|
|
|
|
import android.content.Context;
|
|
|
-import android.graphics.PointF;
|
|
|
-import android.support.annotation.NonNull;
|
|
|
-import android.support.annotation.Nullable;
|
|
|
import android.support.constraint.ConstraintLayout;
|
|
|
import android.support.v7.widget.LinearLayoutManager;
|
|
|
-import android.support.v7.widget.LinearSmoothScroller;
|
|
|
-import android.support.v7.widget.LinearSnapHelper;
|
|
|
-import android.support.v7.widget.OrientationHelper;
|
|
|
import android.support.v7.widget.RecyclerView;
|
|
|
import android.support.v7.widget.SnapHelper;
|
|
|
import android.util.AttributeSet;
|
|
|
-import android.util.DisplayMetrics;
|
|
|
import android.util.Log;
|
|
|
import android.view.LayoutInflater;
|
|
|
-import android.view.View;
|
|
|
-import android.view.animation.DecelerateInterpolator;
|
|
|
-import android.widget.Scroller;
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
import kr.co.zumo.app.R;
|
|
|
import kr.co.zumo.app.lifeplus.bean.api.CategoryBannerBean;
|
|
|
import kr.co.zumo.app.lifeplus.view.IEventListener;
|
|
|
+import kr.co.zumo.app.lifeplus.view.custom.Snapper;
|
|
|
|
|
|
/**
|
|
|
* MainBannerView
|
|
|
@@ -56,8 +47,8 @@ public class MainBannerView extends ConstraintLayout {
|
|
|
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
|
|
inflater.inflate(R.layout.main_banner_view, this);
|
|
|
|
|
|
- SnapHelper snapHelper = new Snapper(400);
|
|
|
recyclerView = findViewById(R.id.recycler_view_main_banner_view);
|
|
|
+ SnapHelper snapHelper = new Snapper(recyclerView, 400);
|
|
|
recyclerView.setNestedScrollingEnabled(false);
|
|
|
recyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
|
|
|
|
|
|
@@ -82,273 +73,4 @@ public class MainBannerView extends ConstraintLayout {
|
|
|
mainBannerAdapter = null;
|
|
|
}
|
|
|
|
|
|
- /***********************************
|
|
|
- * Snapper
|
|
|
- ***********************************/
|
|
|
- public class Snapper extends LinearSnapHelper {
|
|
|
-
|
|
|
- static final float MILLISECONDS_PER_INCH = 100f;
|
|
|
-
|
|
|
- private static final float INVALID_DISTANCE = 1f;
|
|
|
- @Nullable
|
|
|
- private OrientationHelper mVerticalHelper;
|
|
|
- @Nullable
|
|
|
- private OrientationHelper mHorizontalHelper;
|
|
|
-
|
|
|
- private int minVelocity = 200;
|
|
|
-
|
|
|
- public Snapper(int minVelocity) {
|
|
|
- if (minVelocity > this.minVelocity) {
|
|
|
- this.minVelocity = minVelocity;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public boolean onFling(int velocityX, int velocityY) {
|
|
|
- Log.w("APP# Snapper | onFling", "|" + "velociyX: " + velocityX);
|
|
|
- Scroller mGravityScroller = new Scroller(recyclerView.getContext(), new DecelerateInterpolator());
|
|
|
- mGravityScroller.fling(0, 0, velocityX, velocityY, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE);
|
|
|
-// Log.w("APP# MainBannerView | onFling", "| " + mGravityScroller.getFinalX());
|
|
|
-// boolean result = super.onFling(velocityX, velocityY);
|
|
|
-// Log.w("APP# Snapper | onFling", "|" + "result: " + result);
|
|
|
- RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
|
|
|
- if (layoutManager == null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- RecyclerView.Adapter adapter = recyclerView.getAdapter();
|
|
|
- if (adapter == null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- int minFlingVelocity = 0;
|
|
|
- return (Math.abs(velocityY) > minFlingVelocity || Math.abs(velocityX) > minFlingVelocity)
|
|
|
- && snapFromFling2(layoutManager, velocityX, velocityY);
|
|
|
- }
|
|
|
-
|
|
|
- boolean snapFromFling2(@NonNull RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) {
|
|
|
- if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- RecyclerView.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;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- 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;
|
|
|
- Log.i("APP# Snapper | findTargetSnapPosition", "|" + "deltaJump: " + deltaJump);
|
|
|
- if (deltaJump == 0) {
|
|
|
- return RecyclerView.NO_POSITION;
|
|
|
- }
|
|
|
-
|
|
|
- int targetPos = currentPosition + deltaJump;
|
|
|
- if (targetPos < 0) {
|
|
|
- targetPos = 0;
|
|
|
- }
|
|
|
- if (targetPos >= itemCount) {
|
|
|
- targetPos = itemCount - 1;
|
|
|
- }
|
|
|
- return targetPos;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 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];
|
|
|
-
|
|
|
- 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;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 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;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 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 RecyclerView.SmoothScroller} which will handle the scrolling.
|
|
|
- */
|
|
|
- @Nullable
|
|
|
- protected RecyclerView.SmoothScroller createScroller(RecyclerView.LayoutManager layoutManager) {
|
|
|
-
|
|
|
- if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- return new LinearSmoothScroller(recyclerView.getContext()) {
|
|
|
- @Override
|
|
|
- protected void onTargetFound(View targetView, RecyclerView.State state, RecyclerView.SmoothScroller.Action action) {
|
|
|
- if (recyclerView == null) {
|
|
|
- // The associated RecyclerView has been removed so there is no action to take.
|
|
|
- return;
|
|
|
- }
|
|
|
- int[] snapDistances = calculateDistanceToFinalSnap(recyclerView.getLayoutManager(), targetView);
|
|
|
- final int dx = snapDistances[0];
|
|
|
- final int dy = snapDistances[1];
|
|
|
- final int time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy)));
|
|
|
- if (time > 0) {
|
|
|
- action.update(dx, dy, time, mDecelerateInterpolator);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
|
|
|
- return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
|
|
|
- }
|
|
|
- };
|
|
|
- }
|
|
|
- }
|
|
|
}
|