I am trying to achieve this loading with Maps. I have tried loading my animation after the Google Map is created and location is found. But the location loading starts after the animation is completed. How can I achieve the reveal animation after the map camera has navigated to the user's location.
I am not using Splash Screen. The animation code is within the MapsActivity.
private void setupRevealBackground(){
final int revealX = (int) (mBackgroundView.getX() + mBackgroundView.getWidth() / 2);
final int revealY = (int) (mBackgroundView.getY() + mBackgroundView.getHeight() / 2);
ViewTreeObserver viewTreeObserver = mBackgroundView.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
revealActivity(revealX, revealY);
mBackgroundView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}
}
protected void revealActivity(int x, int y) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
float finalRadius = (float) (Math.max(mBackgroundView.getWidth(), mBackgroundView.getHeight()) * 1.1);
// create the animator for this view (the start radius is zero)
Animator circularReveal = ViewAnimationUtils.createCircularReveal(mBackgroundView, x, y, 0, finalRadius);
circularReveal.setDuration(400);
circularReveal.setInterpolator(new AccelerateInterpolator());
// make the view visible and start the animation
circularReveal.start();
circularReveal.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
mBackgroundView.setVisibility(View.GONE);
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
} else {
finish();
}
}
I am updating the location after the location is found Preference Change Listener.
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(Constants.LatitudePref)) {
Latitude = LocationResultHelper.getSavedLatitude(sharedPreferences);
Longitude = LocationResultHelper.getSavedLongitude(sharedPreferences);
if (mPickUpLatLng == null){
updateMarker(new LatLng(Latitude, Longitude));
}
} else if (key.equals(Constants.KEY_LOCATION_UPDATES_REQUESTED)) {
LocationRequestHelper.getRequesting(this);
}
}
This is my updateMarker Method after location is found
private void updateMarker(LatLng userLocation) {
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(userLocation, 16));
setupRevealBackground();
}
I have tried adding setupRevealBackground() method in the CameraIdleListener but the method is never called.
As indicated in this answer you can use OnMapLoadedCallback
When you have a reference to the map set the call back.
mMap.setOnMapLoadedCallback(this);
When the onMapLoaded event fires take the snapshot.
#Override
public void onMapLoaded() {
// Here you can display your map with animation
}
Related
am creating login and signup in same page with circle reveal animation ,switch the view using a floating action button, it very lag when switching one view to another view,
Login page Contain two relative layout and a floating action button
one view for login and other for signup
when floating action button click view will switch to another ie login to signup and vise versa.. i achieved but it is too lag how can i make it smooth
in tab(big screen) it works very smoothly(am creating two layout one for mobile and other for tab)
can anyone help me.. this is my code..
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void viewMenu() {
if (!isOpen) {
// int x = layoutContent.getRight();
// int y = layoutContent.getBottom();
int x = Math.round(fab.getX() + fab.getWidth() / 2);
int y = Math.round(fab.getY() - fab.getHeight());
int startRadius = 0;
int endRadius = (int) Math.hypot(layoutMain.getWidth(), layoutMain.getHeight());
fab.setBackgroundTintList(ColorStateList.valueOf(ResourcesCompat.getColor(getResources(),android.R.color.white,null)));
fab.setImageResource(R.drawable.ic_cross);
Animator anim = ViewAnimationUtils.createCircularReveal(layoutButtons, x, y, startRadius, endRadius);
anim.setInterpolator(new AccelerateDecelerateInterpolator());
layoutButtons.setVisibility(View.VISIBLE);
anim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animator) {
// fst_view.setVisibility(View.GONE);
}
#Override
public void onAnimationEnd(Animator animator) {
}
#Override
public void onAnimationCancel(Animator animator) {
}
#Override
public void onAnimationRepeat(Animator animator) {
}
});
anim.start();
isOpen = true;
} else {
// int x = layoutButtons.getRight();
// int y = layoutButtons.getBottom();
int x = Math.round(fab.getX() + fab.getWidth() / 2);
int y = Math.round(fab.getY() + fab.getHeight()/2) - toolbar.getHeight();
int startRadius = Math.max(layoutContent.getWidth(), layoutContent.getHeight());
int endRadius = 0;
fab.setBackgroundTintList(ColorStateList.valueOf(ResourcesCompat.getColor(getResources(),R.color.colorAccent,null)));
fab.setImageResource(R.drawable.ic_menu_camera);
// fst_view.setVisibility(View.VISIBLE);
Animator anim = ViewAnimationUtils.createCircularReveal(layoutButtons, x, y, startRadius, endRadius);
anim.setInterpolator(new AccelerateDecelerateInterpolator());
anim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animator) {
}
#Override
public void onAnimationEnd(Animator animator) {
layoutButtons.setVisibility(View.GONE);
}
#Override
public void onAnimationCancel(Animator animator) {
}
#Override
public void onAnimationRepeat(Animator animator) {
}
});
anim.start();
isOpen = false;
}
}
As far as I can see, you could use FastOutLinearInInterpolator() instead of AccelerateDecelerateInterpolator(), and add anim.setDuration() with animation duration you want.
And set layoutButtons.setVisibility() to View.INVISIBLE like described here in the bottom of the article.
Furthermore, you can rid of from Math.round() when you calculate view x, y coordinates.
It worked great for my case.
Here is my example code
in iOS, Google Map allow to provide an animation when Marker pop on map.
I'am looking to do the same on Android. So there is nothing to do it or someone know a workaround?
You can animate marker with general Android approach: Animations and Transitions. Use this or that as examples:
private static void animateMarker(final Marker marker, final int current, final LatLng[] line) {
if (line == null || line.length == 0 || current >= line.length) {
return;
}
ObjectAnimator animator = ObjectAnimator.ofObject(marker, property, typeEvaluator, line[current]);
animator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
animateMarker(marker, current + 1, line);
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
animator.setDuration(DURATION);
animator.start();
}
or use libraries, like that.
As per your response from the server, you receive/extract the latitude and longitude.
//call this method for adding marker with lat and long
public void addmarker(){
LatLng marker = new LatLng(double lat, double long);
googleMap.addMarker(new MarkerOptions().position(marker)
.title("My title"));
googleMap.moveCamera(CameraUpdateFactory.newLatLng(marker));
// now the marker is set
// add a delay of 100-200 ms and start you animation (delay for failsafe)
}
I have a custom view kind of like a progress bar. inside this view i have an ObjectAnimator.AnimatorUpdateListener which i'm trying to use to call invalidate on the view. However, my view is not updating! I tried to add a button which simply changed the value to something else and called invalidate once and it worked, my view updated to reflect the value change.
Am I missing something here? Am I calling invalidate too many times or something?
My "progressbar" starts with a float at 0, and the animation is supposed to animate it to 100. Calling a method to update it to 50 and call invalidate works, but the ObjectAnimator doesnt seem to be calling invalidate.
Everything is being called on the UI Thread
ObjectAnimator does not call invalidate() - your method should do it when needed
ObjectAnimator oAnimator = ObjectAnimator.ofInt(view,"someproperty",0,100)
void setSomeProperty(value) {
mValue = value
invalidate()
}
I have used this and it worked.
public interface ProgressAnimationListener {
public void onAnimationStart();
public void onAnimationFinish();
public void onAnimationProgress(int progress);
}
private ObjectAnimator progressBarAnimator;
public synchronized void animateProgressTo(final int start, final int end, final int duration, final ProgressAnimationListener listener) {
stopAnimation();
setProgress(start);
progressBarAnimator = ObjectAnimator.ofFloat(this, "animateProgress", start, end);
progressBarAnimator.setDuration(duration);
progressBarAnimator.setInterpolator(new LinearInterpolator());
progressBarAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationCancel(final Animator animation) {
}
#Override
public void onAnimationEnd(final Animator animation) {
setProgress(end);
if (listener != null) {
listener.onAnimationFinish();
}
}
#Override
public void onAnimationRepeat(final Animator animation) {
}
#Override
public void onAnimationStart(final Animator animation) {
if (listener != null) {
listener.onAnimationStart();
}
}
});
progressBarAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(final ValueAnimator animation) {
int progress = ((Float) animation.getAnimatedValue()).intValue();
if (progress != getProgress()) {
//Log.d(TAG, progress + "");
setProgress(progress);
if (listener != null) {
listener.onAnimationProgress(progress);
}
}
}
});
progressBarAnimator.start();
}
public synchronized boolean isAnimationRunning() {
return progressBarAnimator != null && progressBarAnimator.isRunning();
}
public synchronized void stopAnimation() {
if (isAnimationRunning()) {
progressBarAnimator.cancel();
progressBarAnimator = null;
}
}
I can't get the circular reveal animation to work.
I think I checked the most obvious things:
It starts, width and height are > 0 and it is visible, no Exception..
I load some data from the internet and display it in the view(fab)
The animation should only play after the download finishes.
TmdbHelper helper = new TmdbHelper();
helper.getMovieById(id, "en", new TmdbHelper.ResultListener() {
#Override
public void onResultReceived(JSONObject result) {
// called when finished downloading
try {
String rating = result.getString("vote_average");
AnimationHelper.circularReveal(fab, 500, 0);
fab.setText(rating);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
AnimationHelper:
public static void circularReveal(final View view, final long duration, long startDelay) {
// get the center for the clipping circle
int cx = (view.getLeft() + view.getRight()) / 2;
int cy = (view.getTop() + view.getBottom()) / 2;
// get the final radius for the clipping circle
int finalRadius = Math.max(view.getWidth(), view.getHeight());
// create the animator for this view (the start radius is zero)
Animator anim =
ViewAnimationUtils.createCircularReveal(view, cx, cy, 0, finalRadius);
anim.setDuration(duration);
anim.setStartDelay(startDelay);
anim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
view.setVisibility(View.VISIBLE);
}
#Override
public void onAnimationEnd(Animator animation) {
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {}
});
// make the view visible and start the animation
anim.start();
}
I use the circular reveal animation in other parts like this to make sure the view is attached, and it works:
headerContainer.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
headerContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);
AnimationHelper.circularReveal(headerContainer, 500, 200);
}
});
Perhaps you should erase this line inside your onResultRecieved():
fab.setVisibility(View.VISIBLE);
My assumption is that your circular reveal method is working just fine. It is because of you have made the FAB visible before the animation even begin, you can't see it in action.
As an addition, those lines you've shown which is working doesn't have fab.setVisibility(View.VISIBLE) called anywhere in it.
1st Approach:
Try Transition Listener.
getWindow().getSharedElementExitTransition().addListener(new Transition.TransitionListener() {
#Override
public void onTransitionStart(Transition transition) {
}
#Override
public void onTransitionEnd(Transition transition) {
}
#Override
public void onTransitionCancel(Transition transition) {
}
#Override
public void onTransitionPause(Transition transition) {
}
#Override
public void onTransitionResume(Transition transition) {
}
});
2nd Approach: Try setting start delay and listener to the reveal animation and when animation starts then set the view visible
if (Build.VERSION.SDK_INT >= 21) {
Animator anim = ViewAnimationUtils.createCircularReveal(viewRoot, cx, cy, 0, finalRadius);
anim.setStartDelay(300);
anim.setDuration(1000);
anim.setInterpolator(new DecelerateInterpolator());
anim.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
viewRoot.setVisibility(View.VISIBLE);
}
#Override
public void onAnimationEnd(Animator animation) {
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
anim.start();
}
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
anim.start();
I have a button in my layout. And I am animating the position of that button using ObjectAnimator with translationX animation.
ObjectAnimator btnAnimator = ObjectAnimator.ofFloat(myBtn, "translationX",
ViewHelper.getTranslationX(myBtn), 0);
btnAnimator.addListener(new AnimatorListener() {
#Override
public void onAnimationCancel(Animator arg0) {}
#Override
public void onAnimationEnd(Animator arg0) {
Log.i("TAG","Animation Finished");
}
#Override
public void onAnimationRepeat(Animator arg0) {}
#Override
public void onAnimationStart(Animator arg0) {}
});
btnAnimator.setDuration(animationSpeed).start();
Now I would like to have a listener for the TranslationX of that button to notify whenever the TranslationX position of the button changes.
Here's an easy way I found to do what you're after:
btnAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
Log.e("TAG", "translateX: "+animation.getAnimatedValue("translationX"));
}
});
btnAnimator.setDuration(animationSpeed).start();
Two possible approaches:
1) Override onLayout() in your view to manually compare and detect position changes.
2) Use onLayoutChangeListener on your View:
button.addOnLayoutChangeListener(new OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
int oldTop, int oldRight, int oldBottom) {
// Check your new position vs the old one
}
});
I used this and it worked as a decent way of listening for changes of the view translation.
var previousTranslationX = view.translationX
var previousTranslationY = view.translationY
view.viewTreeObserver.addOnDrawListener {
if (previousTranslationX != view.translationX ||
previousTranslationY != view.translationY) {
previousTranslationX = view.translationX
previousTranslationY = view.translationY
dispatchViewTranslationUpdated(view)
}
}
Simply register a callback to be invoked when the view tree is about to be drawn.
Note: This listener almost called every time the view is drawn!
public class MyView extends View {
private float oldScaleX;
public MyView(Context context) {
super(context);
getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
#Override
public void onDraw() {
// Many things can invoke this method! We don't know why view going
// to be redrawn, So we must determine the cause ourselves.
float newScaleX=getScaleX();
if (oldScaleX!=newScaleX) {
scaleXUpdated();
oldScaleX=newScaleX;
}
}
});
}
private void scaleXUpdated() {
Log.e(TAG,"scaleX updated "+getScaleX);
}
}