I am working on the spectrogram (heatmap) that can be interacted with fingers. It can only pan horizontally, and the Z values from heatmap will be updated whenever panning stopped.
I override onUp function from ZoomPanModifier to fetch the latest x range, which is temporarily logged by VisiableRangeChangeListener. But the issue is, when I’m panning with the finger horizontally, the chart will always be moving a little bit after onUp() occurred. So there’s a difference between the desired x range and actual x range.
Is there a way to get the final x range after a panning action?
Thanks.
- Gang Xu asked 5 years ago
- last edited 5 years ago
- You must login to post comments
Hi Gang Xu,
Unfortunately we don’t support such behavior out of the box and it will be quite tricky to implement, because of how inertial scroll is implemented, but it’s possible to do by tracking changes in VisibleRange of target axis. Here a custom modifier which allow to do this:
class CustomZoomPanModifier extends ZoomPanModifier {
private boolean isInertialAnimation = false;
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
isInertialAnimation = true; // we use inertial scroll for handling fling gesture
return super.onFling(e1, e2, velocityX, velocityY);
}
@Override
public void onRenderSurfaceRendered(RenderedMessage message) {
final IRange visibleRange = getXAxis().getVisibleRange();
final double oldMin = visibleRange.getMinAsDouble();
final double oldMax = visibleRange.getMaxAsDouble();
// if we still animating using inertial scroll visible range will change in this call
super.onRenderSurfaceRendered(message);
final double newMin = visibleRange.getMinAsDouble();
final double newMax = visibleRange.getMaxAsDouble();
// detect when inertial scroll ended
if(isInertialAnimation && Double.compare(oldMin, newMin) == 0 && Double.compare(oldMax, newMax) == 0) {
Log.d(TAG, "animation ended");
// here goes your code
isInertialAnimation = false; // animation ended
}
}
}
Is this suitable for your needs?
Hope this will help you!
Best regards,
Yura
- Yura Khariton answered 5 years ago
- You must login to post comments
Thanks for your prompt reply Yura, it works like a charm.
Even though it only works on the event of the inertial scroll, not the finger down-scroll-up action. But I managed to combine two events together for both scenarios.
- Gang Xu answered 5 years ago
- You must login to post comments
Hi Yura,
With the solution you provided, I randomly had glitches that the event has not been triggered. I use onUp() and onRenderSurfaceRendered() to capture the panning events with or without isInertialAnimation enabled. But I found there’s a scenario both methods cannot trigger the events, which is when the methods occurred in the order listed below.
onRenderSurfaceRendered();
onRenderSurfaceRendered();
onRenderSurfaceRendered(); # not able to trigger event when isInertialAnimation is false
onFling(); # isInertialAnimation switch from false to true
onUp(); # not able to trigger event when isInertialAnimation is true
I hope I made my point clear, and please let me know if you have a solution for this. Thanks.
public class CustomZoomPanModifier extends ZoomPanModifier {
private boolean isInertialAnimation = false;
private static final String TAG = "CustomZoomPanModifier";
@Override
protected void onUp(MotionEvent e) {
if (!isInertialAnimation) {
myCode();
}
super.onUp(e);
}
// use onFling and onRenderSurfaceRendered to trigger event of panning with inertial scroll
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
isInertialAnimation = true; // we use inertial scroll for handling fling gesture
return super.onFling(e1, e2, velocityX, velocityY);
}
@Override
public void onRenderSurfaceRendered(RenderedMessage message) {
final IRange visibleRange = getXAxis().getVisibleRange();
final double oldMin = visibleRange.getMinAsDouble();
final double oldMax = visibleRange.getMaxAsDouble();
// if we still animating using inertial scroll visible range will change in this call
super.onRenderSurfaceRendered(message);
final double newMin = visibleRange.getMinAsDouble();
final double newMax = visibleRange.getMaxAsDouble();
// determine if fling stopped
boolean isMinEqual = Double.compare(oldMin, newMin) == 0;
boolean isMaxEqual = Double.compare(oldMax, newMax) == 0;
if (isInertialAnimation && isMinEqual && isMaxEqual) {
myCode();
isInertialAnimation = false;
}
}
}
- Gang Xu answered 3 years ago
-
I’m sorry but I don’t understand what is expected behavior in this case. What do you do in myCode() method? Have you tried to override onScroll() method which should be called when there should be no inertial scroll and set isInertialAnimation = false there?
-
Sorry for the confusion. myCode() is my own method to re-render the heatmap surface. My task is to re-render the heatmap whenever the x-axis visible range changed by swiping the surface to left or right with a single finger. I used onUp() to capture the gesture of the first scenario and onRenderSurfaceRendered() to capture the gesture of the second scenario. first scenario: finger down, swipe to left/right, finger up when chart surface stopped moving second scenario: finger down, swipe to left/right, finger up while the chart surface still moving But there’s a chance both onUp() and onRenderSurfaceRendered() failed to capture the event when isInertialAnimation just turned to true and onRenderSurfaceRendered() already stopped. If it’s still not clear to you, I will make a video clip to explain. Thanks for your patience. And would you elaborate on how onRenderSurfacedRendered() works? I couldn’t find any description on the documentation. And how onScroll() help in my case?
- You must login to post comments
Please login first to submit.