[android] Navigation buttons animation.

This commit is contained in:
Roman Romanov 2016-11-25 11:00:55 +04:00
parent c5890ce4cd
commit 1167dfeba8
4 changed files with 246 additions and 223 deletions

View file

@ -1174,9 +1174,6 @@ public class MwmActivity extends BaseMwmFragmentActivity
Framework.nativeDeactivatePopup();
mPlacePage.setMapObject(null, false);
}
if (mNavAnimationController != null)
mNavAnimationController.onPlacePageVisibilityChanged(isVisible);
}
@Override
@ -1388,20 +1385,46 @@ public class MwmActivity extends BaseMwmFragmentActivity
private void adjustMenuLineFrameVisibility()
{
final RoutingController controller = RoutingController.get();
final int menuHeight = getCurrentMenu().getFrame().getHeight();
if (controller.isBuilt() || controller.isUberRequestHandled())
{
mMainMenu.showLineFrame(true);
mMainMenu.showLineFrame(true, new Runnable()
{
@Override
public void run()
{
adjustCompass(0);
adjustRuler(0, 0);
}
});
return;
}
if (controller.isPlanning() || controller.isBuilding() || controller.isErrorEncountered())
{
mMainMenu.showLineFrame(false);
mMainMenu.showLineFrame(false, new Runnable()
{
@Override
public void run()
{
adjustCompass(menuHeight);
adjustRuler(0, menuHeight);
}
});
return;
}
mMainMenu.showLineFrame(true);
mMainMenu.showLineFrame(true, new Runnable()
{
@Override
public void run()
{
adjustCompass(0);
adjustRuler(0, 0);
}
});
}
private void setNavButtonsTopLimit(int limit)
@ -1415,6 +1438,8 @@ public class MwmActivity extends BaseMwmFragmentActivity
@Override
public void showRoutePlan(boolean show, @Nullable Runnable completionListener)
{
if (mNavAnimationController != null && !mIsFragmentContainer)
mNavAnimationController.slide(show);
if (show)
{
mSearchController.hide();
@ -1538,7 +1563,7 @@ public class MwmActivity extends BaseMwmFragmentActivity
public void animateSearchPoiTransition(@NonNull final Rect startRect,
@Nullable final Runnable runnable)
{
Animations.rizeTransition(mRootView, startRect, runnable);
Animations.riseTransition(mRootView, startRect, runnable);
}
@Override

View file

@ -1,5 +1,8 @@
package com.mapswithme.maps;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.res.Resources;
import android.support.annotation.NonNull;
import android.view.View;
@ -7,12 +10,11 @@ import com.mapswithme.maps.routing.RoutingController;
import com.mapswithme.util.Animations;
import com.mapswithme.util.Config;
import com.mapswithme.util.UiUtils;
import com.mapswithme.util.log.DebugLogger;
class NavigationButtonsAnimationController
{
private static final DebugLogger LOGGER =
new DebugLogger(NavigationButtonsAnimationController.class.getSimpleName());
private static final int ANIM_TOGGLE = MwmApplication.get().getResources().getInteger(R.integer.anim_slots_toggle);
@NonNull
private final View mZoomIn;
@NonNull
@ -20,24 +22,207 @@ class NavigationButtonsAnimationController
@NonNull
private final View mMyPosition;
private float mBottomLimit;
private float mTopLimit;
private final float mMargin;
private float mBottom;
private float mTop;
private boolean mMyPosAnimate;
private boolean mIsZoomAnimate;
private boolean mIsMyPosAnimate;
private boolean mIsSlideDown;
private final float mMenuHeight;
NavigationButtonsAnimationController(@NonNull View zoomIn, @NonNull View zoomOut,
@NonNull View myPosition)
{
mZoomIn = zoomIn;
mZoomOut = zoomOut;
checkZoomButtonsVisibility();
UiUtils.showIf(showZoomButtons(), mZoomIn, mZoomOut);
mMyPosition = myPosition;
calculateBottomLimit();
Resources res = mZoomIn.getResources();
mMargin = res.getDimension(R.dimen.margin_base_plus);
mMenuHeight = res.getDimension(R.dimen.menu_line_height);
calculateLimitTranslations();
}
private void checkZoomButtonsVisibility()
private void calculateLimitTranslations()
{
UiUtils.showIf(showZoomButtons(), mZoomIn, mZoomOut);
mTop = mMargin;
mMyPosition.addOnLayoutChangeListener(new View.OnLayoutChangeListener()
{
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom)
{
mBottom = bottom + mMargin;
mMyPosition.removeOnLayoutChangeListener(this);
}
});
}
void setTopLimit(float limit)
{
mTop = limit + mMargin;
update();
}
private void fadeOutZoom()
{
if (mIsSlideDown)
return;
mIsZoomAnimate = true;
Animations.fadeOutView(mZoomIn, new Runnable()
{
@Override
public void run()
{
mZoomIn.setVisibility(View.INVISIBLE);
mIsZoomAnimate = false;
}
});
Animations.fadeOutView(mZoomOut, new Runnable()
{
@Override
public void run()
{
mZoomOut.setVisibility(View.INVISIBLE);
}
});
}
private void fadeInZoom()
{
mIsZoomAnimate = true;
mZoomIn.setVisibility(View.VISIBLE);
mZoomOut.setVisibility(View.VISIBLE);
Animations.fadeInView(mZoomIn, new Runnable()
{
@Override
public void run()
{
mIsZoomAnimate = false;
}
});
Animations.fadeInView(mZoomOut, null);
}
private void fadeOutMyPosition()
{
if (mIsSlideDown)
return;
mIsMyPosAnimate = true;
Animations.fadeOutView(mMyPosition, new Runnable()
{
@Override
public void run()
{
UiUtils.invisible(mMyPosition);
mIsMyPosAnimate = false;
}
});
}
private void fadeInMyPosition()
{
mIsMyPosAnimate = true;
mMyPosition.setVisibility(View.VISIBLE);
Animations.fadeInView(mMyPosition, new Runnable()
{
@Override
public void run()
{
mIsMyPosAnimate = false;
}
});
}
void onPlacePageMoved(float translationY)
{
if (UiUtils.isLandscape(mMyPosition.getContext()) || mBottom == 0)
return;
final float amount = mZoomIn.getTranslationY() > 0 ? mMenuHeight : 0;
final float translation = translationY - mBottom;
update(translation <= amount ? translation : amount);
}
private void update()
{
update(mZoomIn.getTranslationY());
}
private void update(final float translation)
{
mMyPosition.setTranslationY(translation);
mZoomOut.setTranslationY(translation);
mZoomIn.setTranslationY(translation);
if (!mIsZoomAnimate && isOverTopLimit(mZoomIn))
{
fadeOutZoom();
}
else if (!mIsZoomAnimate && satisfyTopLimit(mZoomIn))
{
fadeInZoom();
}
if (!shouldBeHidden() && !mIsMyPosAnimate
&& isOverTopLimit(mMyPosition))
{
fadeOutMyPosition();
}
else if (!shouldBeHidden() && !mIsMyPosAnimate
&& satisfyTopLimit(mMyPosition))
{
fadeInMyPosition();
}
}
private boolean isOverTopLimit(@NonNull View view)
{
return view.getVisibility() == View.VISIBLE && view.getY() <= mTop;
}
private boolean satisfyTopLimit(@NonNull View view)
{
return view.getVisibility() == View.INVISIBLE && view.getY() >= mTop;
}
private boolean shouldBeHidden()
{
return LocationState.getMode() == LocationState.FOLLOW_AND_ROTATE
&& (RoutingController.get().isPlanning() || RoutingController.get().isNavigating());
}
void slide(boolean isDown)
{
if (UiUtils.isLandscape(mMyPosition.getContext())
|| (!isDown && mZoomIn.getTranslationY() <= 0))
return;
mIsSlideDown = isDown;
ValueAnimator animator = ValueAnimator.ofFloat(isDown ? 0 : mMenuHeight, isDown ? mMenuHeight : 0);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
{
@Override
public void onAnimationUpdate(ValueAnimator animation)
{
float value = (float) animation.getAnimatedValue();
update(value);
}
});
animator.addListener(new UiUtils.SimpleAnimatorListener()
{
@Override
public void onAnimationEnd(Animator animation)
{
mIsSlideDown = false;
update();
}
});
animator.setDuration(ANIM_TOGGLE);
animator.start();
}
void disappearZoomButtons()
@ -54,204 +239,8 @@ class NavigationButtonsAnimationController
if (!showZoomButtons())
return;
if (!canZoomButtonsFitInScreen())
return;
Animations.appearSliding(mZoomIn, Animations.RIGHT, null);
Animations.appearSliding(mZoomOut, Animations.RIGHT, null);
updateZoomButtonsPosition();
}
void setTopLimit(float limit)
{
mTopLimit = limit;
updateZoomButtonsPosition();
}
void onPlacePageVisibilityChanged(boolean isVisible)
{
if (isVisible)
fadeOutZooms();
else
fadeInZooms();
}
void onPlacePageMoved(float translationY)
{
if (mBottomLimit == 0)
return;
float translation = translationY - mBottomLimit;
animateMyPosition(translation <= 0 ? translation : 0);
}
private void calculateBottomLimit()
{
mMyPosition.addOnLayoutChangeListener(new View.OnLayoutChangeListener()
{
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom)
{
mBottomLimit = bottom;
mMyPosition.removeOnLayoutChangeListener(this);
}
});
}
private void updateZoomButtonsPosition()
{
// It means that the zoom buttons fit in screen perfectly,
// any updates of position are no needed.
if (mZoomIn.getTop() >= mTopLimit && UiUtils.isVisible(mZoomIn))
return;
// If the top limit is decreased we try to return zoom buttons at initial position.
if (mTopLimit < mZoomIn.getTop() && tryPlaceZoomButtonsAtInitialPosition())
{
LOGGER.d("Zoom buttons were come back to initial position");
return;
}
// If top view overlaps the zoomIn button.
if (mTopLimit > mZoomIn.getTop())
{
// We should try to pull out the zoomIn button from under the top view
// if available space allows us doing that.
if (tryPlaceZoomButtonsUnderTopLimit())
return;
// Otherwise, we just fade out the zoom buttons
// since there is not enough space on the screen for them.
fadeOutZooms();
}
}
private boolean tryPlaceZoomButtonsAtInitialPosition()
{
float availableSpace = mBottomLimit - mTopLimit;
float requiredSpace = mBottomLimit - mZoomIn.getTop() - mZoomIn.getTranslationY();
if (requiredSpace > availableSpace)
return false;
mZoomIn.setTranslationY(0);
mZoomOut.setTranslationY(0);
return true;
}
private boolean tryPlaceZoomButtonsUnderTopLimit()
{
if (!canZoomButtonsFitInScreen())
return false;
float requiredTranslate = mTopLimit - mZoomIn.getTop();
mZoomIn.setTranslationY(requiredTranslate);
mZoomOut.setTranslationY(requiredTranslate);
return true;
}
private boolean canZoomButtonsFitInScreen()
{
float availableSpace = mBottomLimit - mTopLimit;
return getMinimumRequiredHeightForButtons() <= availableSpace;
}
private float getMinimumRequiredHeightForButtons()
{
return mZoomIn.getHeight() + mZoomOut.getHeight() + mMyPosition.getHeight();
}
private void fadeOutZooms()
{
Animations.fadeOutView(mZoomIn, new Runnable()
{
@Override
public void run()
{
mZoomIn.setVisibility(View.INVISIBLE);
}
});
Animations.fadeOutView(mZoomOut, new Runnable()
{
@Override
public void run()
{
mZoomOut.setVisibility(View.INVISIBLE);
}
});
}
private void fadeInZooms()
{
if (!showZoomButtons())
return;
if (!canZoomButtonsFitInScreen())
return;
mZoomIn.setVisibility(View.VISIBLE);
mZoomOut.setVisibility(View.VISIBLE);
Animations.fadeInView(mZoomIn, null);
Animations.fadeInView(mZoomOut, null);
}
private void fadeOutMyPosition()
{
mMyPosAnimate = true;
Animations.fadeOutView(mMyPosition, new Runnable()
{
@Override
public void run()
{
UiUtils.invisible(mMyPosition);
mMyPosAnimate = false;
}
});
}
private void fadeInMyPosition()
{
mMyPosAnimate = true;
mMyPosition.setVisibility(View.VISIBLE);
Animations.fadeInView(mMyPosition, new Runnable()
{
@Override
public void run()
{
mMyPosAnimate = false;
}
});
}
private void animateMyPosition(float translation)
{
mMyPosition.setTranslationY(translation);
if (!shouldMyPositionBeHidden() && !mMyPosAnimate
&& isOverTopLimit(mMyPosition))
{
fadeOutMyPosition();
}
else if (!shouldMyPositionBeHidden() && !mMyPosAnimate
&& satisfyTopLimit(mMyPosition))
{
fadeInMyPosition();
}
}
private boolean isOverTopLimit(@NonNull View view)
{
return view.getVisibility() == View.VISIBLE && view.getY() <= mTopLimit;
}
private boolean satisfyTopLimit(@NonNull View view)
{
return view.getVisibility() == View.INVISIBLE && view.getY() >= mTopLimit;
}
private boolean shouldMyPositionBeHidden()
{
return LocationState.getMode() == LocationState.FOLLOW_AND_ROTATE
&& (RoutingController.get().isPlanning());
Animations.appearSliding(mZoomIn, Animations.LEFT, null);
Animations.appearSliding(mZoomOut, Animations.LEFT, null);
}
private static boolean showZoomButtons()
@ -261,6 +250,6 @@ class NavigationButtonsAnimationController
public void onResume()
{
checkZoomButtonsVisibility();
update();
}
}

View file

@ -1,8 +1,8 @@
package com.mapswithme.maps.widget.menu;
import android.support.annotation.Nullable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import com.mapswithme.maps.MwmActivity;
@ -11,9 +11,9 @@ import com.mapswithme.maps.R;
import com.mapswithme.maps.downloader.MapManager;
import com.mapswithme.maps.downloader.UpdateInfo;
import com.mapswithme.maps.routing.RoutingController;
import com.mapswithme.util.Animations;
import com.mapswithme.util.Graphics;
import com.mapswithme.util.UiUtils;
import ru.mail.android.mytarget.core.models.Stat;
import java.util.ArrayList;
import java.util.HashMap;
@ -263,9 +263,9 @@ public class MainMenu extends BaseMenu
{
UiUtils.show(mButtonsFrame);
expandContent = false;
} else
}
else
{
UiUtils.showIf(state == State.MENU, mButtonsFrame);
UiUtils.showIf(isRouting, mRoutePlanFrame);
if (isRouting)
@ -313,8 +313,17 @@ public class MainMenu extends BaseMenu
return mAnimationTrackListener;
}
public void showLineFrame(boolean show)
public void showLineFrame(boolean show, @Nullable Runnable completion)
{
UiUtils.showIf(show, mLineFrame);
if (show)
{
UiUtils.hide(mFrame);
Animations.appearSliding(mFrame, Animations.BOTTOM, completion);
}
else
{
UiUtils.show(mFrame);
Animations.disappearSliding(mFrame, Animations.BOTTOM, completion);
}
}
}

View file

@ -143,7 +143,7 @@ public final class Animations
});
}
public static void rizeTransition(@NonNull ViewGroup rootView, @NonNull final Rect startRect,
public static void riseTransition(@NonNull ViewGroup rootView, @NonNull final Rect startRect,
@Nullable final Runnable runnable)
{
Context context = rootView.getContext();