一个以动画形式显示返回结果的对话框 (支持loading、打勾、打叉、感叹号)

本文介绍了如何创建一个动画对话框,模仿支付宝支付成功的打勾动画。通过自定义LoadingView、SucceedView、FailedView和WarningView,利用属性动画ValueAnimator实现效果。文章详细讲解了各个组件的实现,并封装成ResultDialog对话框,提供了在 BaseActivity 中一键调用的便捷方式,简化在 Android 应用中展示动画对话框的操作。
摘要由CSDN通过智能技术生成

大家一定看过支付宝支付成功时显示的那个打勾的动画,这次就带来这样一个控件,把动画封装成一个控件,再二次封装在对话

框里,调用时只需要写一句话,很方便。先看一下效果吧:  


                                                                


首先还是惯例的简单说一下原理吧。其实很简单,本质就是画一个图案,勾的图案就是画个圆弧,再画一段折线,其它图案同

理。然后所谓动画就是把这个图案绘制的过程一帧一帧迭代,这里直接用了系统提供的属性动画类ValueAnimator来实现迭代过

程。关于属性动画这里就不多说了,不了解的童鞋可以自行度娘。


废话了一堆,开始正题。这次的文件数量有点多,我们一个一个来。


我把loading、打勾、打叉、感叹号四个动画每一个单独封装成一个View的子类,分别叫LoadingView、SucceedView、

FailedView、WarningView。上本体:

LoadingView.java:

public class LoadingView extends View {
    private Context context;

    // 动画播放周期
    private int duration = 1000;

    // 动画控制器
    private Interpolator interpolator = new LinearInterpolator();

    // 是否正在播放loading动画
    private boolean isLoading;

    public LoadingView(Context context) {
        this(context, null);
    }

    public LoadingView(Context context, AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public LoadingView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context);
    }

    private void init(Context context) {
        this.context = context;

        setBackgroundDrawable(context.getResources().getDrawable(R.drawable.circle_loading));
    }

    /**
     * 开始播放动画
     */
    public void start() {
        if (isLoading) {
            return;
        }
        isLoading = true;
        RotateAnimation animation = new RotateAnimation(0, 360,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        animation.setDuration(duration);
        animation.setInterpolator(interpolator);
        animation.setRepeatCount(-1);
        startAnimation(animation);
    }

    /**
     * 停止播放动画
     */
    public void stop() {
        clearAnimation();
        isLoading = false;
    }

    /**
     * 设置动画播放周期
     * @param duration 播放周期,单位毫秒
     */
    public void setDuration(int duration) {
        this.duration = duration;
    }

    /**
     * 设置动画控制器
     * @param interpolator
     */
    public void setInterpolator(Interpolator interpolator) {
        this.interpolator = interpolator;
    }

    /**
     * 获取是否正在播放loading动画
     * @return
     */
    public boolean isLoading() {
        return isLoading;
    }
}

SucceedView.java:

public class SucceedView extends View {
    private Context context;

    private int width;
    private int height;
    private int radius;

    private float startX;
    private float startY;
    private float middleX;
    private float middleY;
    private float endX;
    private float endY;

    // 图案线宽
    private int lineWidth;

    // 图案线颜色
    private int lineColor = Color.parseColor("#ADE584");

    // 动画播放时间
    private int duration = 1500;

    // 动画控制器
    private Interpolator interpolator = new AccelerateDecelerateInterpolator();

    private Paint circlePaint;
    private Paint linePaint;
    private float progress;
    private boolean isDraw;

    // 动画播放回调
    private OnShowAnimationListener listener;

    public SucceedView(Context context) {
        this(context, null);
    }

    public SucceedView(Context context, AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public SucceedView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.context = context;
    }

    private void init() {
        if (lineWidth == 0) lineWidth = dp2px(3);

        if (circlePaint == null) circlePaint = new Paint();
        circlePaint.setStrokeWidth(lineWidth);
        circlePaint.setColor(lineColor);
        circlePaint.setStyle(Paint.Style.STROKE);
        circlePaint.setAntiAlias(true);
        circlePaint.setStrokeCap(Paint.Cap.ROUND);

        if (linePaint == null) linePaint = new Paint();
        linePaint.setStrokeWidth(1.5f * lineWidth);
        linePaint.setColor(lineColor);
        linePaint.setStyle(Paint.Style.STROKE);
        linePaint.setAntiAlias(true);
        linePaint.setStrokeCap(Paint.Cap.ROUND);

        startX = lineWidth / 2 + radius * 2 / 5;
        startY = lineWidth / 2 + radius;
        middleX = lineWidth / 2 + radius - 2 * lineWidth;
        middleY = lineWidth / 2 + radius * 7 / 5;
        endX = lineWidth / 2 + radius + radius * (float) Math.cos(2 * Math.PI * 37.5 / 360);
        endY = lineWidth + radius - radius * (float) Math.sin(2 * Math.PI * 37.5 / 360);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        width = getMeasuredWidth();
        height = width;
        radius = (width / 2 - lineWidth / 2);
        getLayoutParams().height = height;
        init();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        doDraw(canvas);
    }

    /**
     * 绘制图案
     *
     * @param canvas
     */
    private void doDraw(Canvas canvas) {
        if (progress > 0) {
            // 画外面的圈
            if (progress <= 0.5) {
                canvas.drawArc(new RectF(lineWidth / 2, lineWidth / 2, width - lineWidth / 2, height - lineWidth / 2), -60, -315 * (progress / (float) 0.6), false, circlePaint);
                // 画勾的左半部分
            } else if (progress <= 0.7) {
                canvas.drawArc(new RectF(lineWidth / 2, lineWidth / 2, width - lineWidth / 2, height - lineWidth / 2), -60, -315, false, circlePaint);
                canvas.drawLine(startX, startY, startX + (middleX - startX) * (progress - 0.5f) / 0.2f, startY + (middleY - startY) * (progress - 0.5f) / 0.2f, linePaint);
                // 画勾的右半部分
            } else {
                canvas.drawArc(new RectF(lineWidth / 2, lineWidth / 2, width - lineWidth / 2, height - lineWidth / 2), -60, -315, false, circlePaint);
                canvas.drawLine(startX, startY, middleX, middleY, linePaint);
                canvas.drawLine(middleX, middleY, middleX + (endX - middleX) * (progress - 0.7f) / 0.3f, middleY + (endY - middleY) * (progress - 0.7f) / 0.3f, linePaint);
            }
        }
    }

    /**
     * 开始播放动画
     */
    public void start() {
        if (isDraw) {
            return;
        }
        isDraw = true;
        ValueAnimator animator = new ValueAnimator().ofFloat(0, 1, 1);
        animator.setDuration(duration);
        animator.setInterpolator(interpolator);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                if (isDraw) {
                    progress = (float) animation.getAnimatedValue();
                    invalidate();
                }
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                if (listener != null) listener.onStart();
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                super.onAnimationCancel(animation);
                isDraw = false;
                if (listener != null) listener.onCancel();
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                isDraw = false;
                if (listener != null) listener.onFinish();
            }
        });
        animator.start();
    }

    /**
     * 停止播放动画方法
     */
    public void stop() {
        isDraw &
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,您可以使用 WPF 动画和布局来创建在点击按钮之后的 loading 动画,并将其显示在窗口中央。以下是一个简单的实现步骤: 1. 首先,在您的 WPF 窗口中添加一个 `Grid` 元素,用于布局您的内容。 2. 在该 `Grid` 中添加一个 `Border` 元素,用于包含您的 loading 动画,并将其设置为不可见。 3. 创建一个 `Storyboard`,用于控制您的 loading 动画。在 `Storyboard` 中,使用 `DoubleAnimation` 控制 `Opacity` 属性,从而实现淡入淡出的效果。 4. 在按钮的 `Click` 事件处理程序中,将 `Border` 元素设置为可见,并启动 `Storyboard`,以显示 loading 动画。 5. 当操作完成后,停止 `Storyboard` 并将 `Border` 元素设置为不可见。 以下是一个简单的示例代码,演示如何实现这个功能: XAML 代码: ```xml <Window x:Class="WpfApp1.MainWindow" ...> <Grid> <Button Content="Click me" Click="Button_Click"/> <Border x:Name="LoadingBorder" Visibility="Collapsed" Background="White" Opacity="0"> <TextBlock Text="Loading..." FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center"/> </Border> </Grid> </Window> ``` C# 代码: ```csharp using System.Threading.Tasks; using System.Windows; using System.Windows.Media.Animation; namespace WpfApp1 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private async void Button_Click(object sender, RoutedEventArgs e) { // 显示 loading 动画 LoadingBorder.Visibility = Visibility.Visible; // 创建淡入动画 var fadeInAnimation = new DoubleAnimation { From = 0, To = 1, Duration = new Duration(TimeSpan.FromSeconds(0.5)) }; // 创建淡出动画 var fadeOutAnimation = new DoubleAnimation { From = 1, To = 0, Duration = new Duration(TimeSpan.FromSeconds(0.5)) }; // 创建 Storyboard var storyboard = new Storyboard(); storyboard.Children.Add(fadeInAnimation); storyboard.Children.Add(fadeOutAnimation); Storyboard.SetTarget(fadeInAnimation, LoadingBorder); Storyboard.SetTarget(fadeOutAnimation, LoadingBorder); Storyboard.SetTargetProperty(fadeInAnimation, new PropertyPath(OpacityProperty)); Storyboard.SetTargetProperty(fadeOutAnimation, new PropertyPath(OpacityProperty)); // 执行操作 await Task.Delay(3000); // 停止动画并隐藏 loading 动画 storyboard.Stop(); LoadingBorder.Visibility = Visibility.Collapsed; } } } ``` 该示例代码中,当用户点击按钮时,首先会显示一个带有文本 “Loading…” 的 `Border` 元素。然后,创建了两个 `DoubleAnimation`,一个用于淡入,另一个用于淡出。这些动画被添加到一个 `Storyboard` 中,并将其目标设置为 `LoadingBorder` 的 `Opacity` 属性。接着,`Storyboard` 启动,控制 `Border` 的 `Opacity` 属性,从而实现 loading 动画的淡入淡出效果。操作完成后,停止动画并将 `Border` 元素设置为不可见。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值