Skip to content

Instantly share code, notes, and snippets.

@entdark
Created March 21, 2022 17:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save entdark/99c5fe39a8524a7da986b8f218965230 to your computer and use it in GitHub Desktop.
Save entdark/99c5fe39a8524a7da986b8f218965230 to your computer and use it in GitHub Desktop.
Telegram back-arrow<->cross rotating animation in Xamarin.Android
//source: https://github.com/DrKLO/Telegram/blob/master/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BackDrawable.java
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Views.Animations;
using DPTOPX_EXTENSION;
namespace YOUR_NAMESPACE {
public class BackDrawable : Drawable {
private const float DefaultAnimationTime = 300.0f;
private readonly Paint paint = new Paint(PaintFlags.AntiAlias);
private readonly bool alwaysClose;
private readonly DecelerateInterpolator interpolator = new DecelerateInterpolator();
private bool reverseAngle;
private long lastFrameTime;
private float finalRotation;
private float currentRotation;
private int currentAnimationTime;
private Color color = Color.White;
public Color Color {
get => color;
set { color = value; InvalidateSelf(); }
}
private Color rotatedColor = Color.Gray;
public Color RotatedColor {
get => rotatedColor;
set { rotatedColor = value; InvalidateSelf(); }
}
private int arrowRotation;
public int ArrowRotation {
get => arrowRotation;
set { arrowRotation = value; InvalidateSelf(); }
}
public float AnimationTime { get; set; } = DefaultAnimationTime;
public bool Rotated { get; set; } = true;
public BackDrawable(bool close) {
paint.StrokeWidth = 2.0f.DpToPx();
alwaysClose = close;
}
public void SetRotation(float rotation, bool animated) {
lastFrameTime = 0;
if (currentRotation == 1) {
reverseAngle = true;
} else if (currentRotation == 0) {
reverseAngle = false;
}
lastFrameTime = 0;
if (animated) {
if (currentRotation < rotation) {
currentAnimationTime = (int)(currentRotation * AnimationTime);
} else {
currentAnimationTime = (int)((1.0f - currentRotation) * AnimationTime);
}
lastFrameTime = Java.Lang.JavaSystem.CurrentTimeMillis();
finalRotation = rotation;
} else {
finalRotation = currentRotation = rotation;
}
InvalidateSelf();
}
public override void Draw(Canvas canvas) {
if (currentRotation != finalRotation) {
if (lastFrameTime != 0) {
long dt = Java.Lang.JavaSystem.CurrentTimeMillis() - lastFrameTime;
currentAnimationTime += (int)dt;
if (currentAnimationTime >= AnimationTime) {
currentRotation = finalRotation;
} else {
if (currentRotation < finalRotation) {
currentRotation = interpolator.GetInterpolation(currentAnimationTime / AnimationTime) * finalRotation;
} else {
currentRotation = 1.0f - interpolator.GetInterpolation(currentAnimationTime / AnimationTime);
}
}
}
lastFrameTime = Java.Lang.JavaSystem.CurrentTimeMillis();
InvalidateSelf();
}
int rD = Rotated ? (int)((rotatedColor.R - color.R) * currentRotation) : 0;
int rG = Rotated ? (int)((rotatedColor.G - color.G) * currentRotation) : 0;
int rB = Rotated ? (int)((rotatedColor.B - color.B) * currentRotation) : 0;
paint.Color = Color.Rgb(color.R + rD, color.G + rG, color.B + rB);
canvas.Save();
canvas.Translate(IntrinsicWidth / 2, IntrinsicHeight / 2);
if (arrowRotation != 0) {
canvas.Rotate(arrowRotation);
}
float rotation = currentRotation;
if (!alwaysClose) {
canvas.Rotate(currentRotation * (reverseAngle ? -225 : 135));
} else {
canvas.Rotate(135 + currentRotation * (reverseAngle ? -180 : 180));
rotation = 1.0f;
}
canvas.DrawLine(-7.0f.DpToPx() - 1.0f.DpToPx() * rotation, 0, 8.0f.DpToPx(), 0, paint);
float startYDiff = -0.5f.DpToPx();
float endYDiff = 7.0f.DpToPx() + 1.0f.DpToPx() * rotation;
float startXDiff = -7.0f.DpToPx() + 7.0f.DpToPx() * rotation;
float endXDiff = 0.5f.DpToPx() - 0.5f.DpToPx() * rotation;
canvas.DrawLine(startXDiff, -startYDiff, endXDiff, -endYDiff, paint);
canvas.DrawLine(startXDiff, startYDiff, endXDiff, endYDiff, paint);
canvas.Restore();
}
public override void SetAlpha(int alpha) {}
public override void SetColorFilter(ColorFilter cf) {}
public override int Opacity => (int)Format.Transparent;
public override int IntrinsicWidth => 24.0f.DpToPx();
public override int IntrinsicHeight => 24.0f.DpToPx();
}
}
var back = new BackDrawable(false) {
Color = Color.White,
RotatedColor = Color.Red
};
Toolbar.NavigationIcon = back;
//arrow -> cross
back.SetRotation(1.0f, true);
//cross -> arrow
back.SetRotation(0.0f, true);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment