I have an app where I override the onDrawerClosed of an ActionBarDrawerToggle. I have noticed that there is always an unwanted delay before the onDrawerClosed method is called. The problem is very easy to spot: when I override onDrawerSlide and put in some logging I see clearly that the slideOffset value is 0.0 somewhere between 50-100ms before onDrawerClosed is called. Why is this delay there?
mDrawerToggle = new ActionBarDrawerToggle([...]) {
public void onDrawerClosed(View view) {
Log.d(TAG, String.format("% 12d onDrawerClosed", Calendar.getInstance().getTimeInMillis()));
super.onDrawerClosed(view);
}
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
}
#Override
public void onDrawerSlide(View drawerView, float slideOffset) {
Log.d(TAG, String.format("% 12d onDrawerSlide %f", Calendar.getInstance().getTimeInMillis(), slideOffset)));
// THIS IS CALLED WITH slideOffset = 0.0f TOO LONG BEFORE onDrawerClosed
super.onDrawerSlide(drawerView, slideOffset);
}
};
i'm not sure about this answer can you put that Log.d after super invoked
Related
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
}
As Writing Custom View for Android said:
Remove any posted Runnable in onDetachedFromWindow
I have a custom view, CircleCheckBox. When it's clicked animation will be executed.
The animation is implemented by View#postInvalidate().
So is there a way to remove any posted runnable in onDetachedFromWindow()?
EDIT
Let me tell you how CircleCheckBox works.
Step 1
In CircleCheckBox constructor I set View#OnClickListener for it:
this.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
toggle();
if (mListener != null) {
mListener.onCheckedChanged(isChecked());
}
}
});
Step 2
In method toggle() it will invoke:
#Override
public void setChecked(boolean checked) {
....
if (checked) {
startCheckedAnimation();
} else {
startUnCheckedAnimation();
}
}
Step 3
Let's say startCheckedAnimation().
private void startCheckedAnimation() {
// circle animation
ValueAnimator circleAnimator = ValueAnimator.ofFloat(0f, 1f);
circleAnimator.setInterpolator(new DecelerateInterpolator());
circleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
mArcPath.reset();
mArcPath.addArc(mRectF, -159, 360 * (1 - value));
postInvalidate();
}
});
circleAnimator.setDuration(mDuration / 4);
circleAnimator.start();
}
I use postInvalidate() method to let view invoke onDraw().
View#clearAnimation() will cancel all animations that have been applied to the View.
In your case you are using a ValueAnimator, which means that you have to keep a reference to it and when you need to cancel the animation you should perform ValueAnimator#cancel():
private ValueAnimator circleAnimator;
...
circleAnimator = ValueAnimator.ofFloat(0f, 1f);
// setup and start `ValueAnimator`
Then, when needed to cancel the animation:
circleAnimator.cancel();
I think you can just use removeCallbacks(Runnable r)method.
void removeCallbacks (Runnable r)
Remove any pending posts of Runnable r that are in the message queue.
I am trying to fix the expanded height of the sliding panel. What I want is that the sliding panel layout can be expanded only to 40 or 50% of screen height. I tried adding umanoAnchorPoint attribute with value 0.4 but the view can still be dragged to full screen.
You may use the sliding listener like this:
slidingUpPanelLayout.addPanelSlideListener(new SlidingUpPanelLayout.PanelSlideListener() {
#Override
public void onPanelSlide(View panel, float slideOffset) {
}
#Override
public void onPanelStateChanged(View panel, SlidingUpPanelLayout.PanelState previousState, SlidingUpPanelLayout.PanelState newState) {
if(newState == SlidingUpPanelLayout.PanelState.EXPANDED) slidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.ANCHORED);
}
});
or maybe:
slidingUpPanelLayout.addPanelSlideListener(new SlidingUpPanelLayout.PanelSlideListener() {
#Override
public void onPanelSlide(View panel, float slideOffset) {
if(slideOffset > desiredOffset) slidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.ANCHORED);
}
#Override
public void onPanelStateChanged(View panel, SlidingUpPanelLayout.PanelState previousState, SlidingUpPanelLayout.PanelState newState) {
}
});
I'm trying to make Navigation Drawer which moves whole screen (whole height, not width). I'm not using ActionBar and other libraries, just default Android drawer. Anyone have any examples?
What you need to do is to set the translationX property of your content view by the pixel amount the drawer has moved to.
In your implementation of http://developer.android.com/reference/android/support/v4/widget/DrawerLayout.DrawerListener.html you should implement onDrawerSlide(View drawerView, float slideOffset).
In this method you should add this line.
mDrawerLayout.findViewById(R.id.your_content_id).setTranslationX(drawerView.getWidth() * slideOffset);
That should do the trick.
Implements DrawerListener (passing as parameter the R.id of your layout):
import android.animation.ObjectAnimator;
import android.support.v4.widget.DrawerLayout.DrawerListener;
import android.view.View;
public class DrawerLayoutListener implements DrawerListener {
private View _contentDrawer;
private int _idView;
public DrawerLayoutListener(int idView) {
_idView = idView;
}
#Override
public void onDrawerClosed(View arg0) {}
#Override
public void onDrawerOpened(View arg0) {}
#Override
public void onDrawerSlide(View drawerView, float slideOffset) {
if (_contentDrawer == null) _contentDrawer = ((View) drawerView.getParent()).findViewById(_idView);
float moveFactor = (drawerView.getWidth() * slideOffset);
ObjectAnimator.ofFloat(_contentDrawer, "translationX", moveFactor).setDuration(0).start();
}
#Override
public void onDrawerStateChanged(int arg0) {}
}
And set it as listener to your DrawerLayout:
drawer_layout.setDrawerListener(new DrawerLayoutListener(R.id.content_frame));
I have a SurfaceView that should be set with setZOrderOnTop(true) , is it possible to have a DrawerLayout that opens over it. currently the surface draws over the drawer.
In a similar situation the solution from DrawerLayout not working with Android 4.4 & SurfaceView worked for me:
#Override
public void onDrawerSlide(View drawerView, float slideOffset)
{
mDrawerLayout.bringChildToFront(drawerView);
mDrawerLayout.requestLayout();
}
Inside of your DrawerListener
The below code worked for me.
actionBarDrawerToggle = new ActionBarDrawerToggle(this,drawerLayout,R.string.open, R.string.close)
{
#Override
public void onDrawerSlide(View drawerView, float slideOffset) {
super.onDrawerSlide(drawerView, slideOffset);
drawerLayout.bringChildToFront(drawerView);
drawerLayout.requestLayout();
}
};