Xamarin Forms的Toast等效功能

116

是否有使用Xamarin Forms(而不是Android或iOS特定)创建弹出窗口的方法,就像Android使用Toast一样,不需要用户交互,并且在(短暂的)一段时间后消失?

从搜索中发现,所有我看到的都需要用户点击才能消失的警报。

21个回答

230

这有一个简单的解决方案。通过使用DependencyService,您可以在Android和iOS中轻松地获得类似Toast的方法。

在通用包中创建一个接口。

public interface IMessage
{
    void LongAlert(string message);
    void ShortAlert(string message);
}

安卓板块

[assembly: Xamarin.Forms.Dependency(typeof(MessageAndroid))]
namespace Your.Namespace
{
    public class MessageAndroid : IMessage
    {
        public void LongAlert(string message)
        {
            Toast.MakeText(Application.Context, message, ToastLength.Long).Show();
        }
    
        public void ShortAlert(string message)
        {
            Toast.MakeText(Application.Context, message, ToastLength.Short).Show();
        }
    }
}

iOS 部分

在 iOS 中没有像 Toast 那样的本地解决方案,因此我们需要实现自己的方法。

[assembly: Xamarin.Forms.Dependency(typeof(MessageIOS))]
namespace Your.Namespace
{
    public class MessageIOS : IMessage
    {
        const double LONG_DELAY = 3.5;
        const double SHORT_DELAY = 2.0;

        NSTimer alertDelay;
        UIAlertController alert;

        public void LongAlert(string message)
        {
            ShowAlert(message, LONG_DELAY);
        }
        public void ShortAlert(string message)
        {
            ShowAlert(message, SHORT_DELAY);
        }

        void ShowAlert(string message, double seconds)
        {
            alertDelay = NSTimer.CreateScheduledTimer(seconds, (obj) =>
            {
                dismissMessage();
            });
            alert = UIAlertController.Create(null, message, UIAlertControllerStyle.Alert);
            UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(alert, true, null);
        }

        void dismissMessage()
        {
            if (alert != null)
            {
                alert.DismissViewController(true, null);
            }
            if (alertDelay != null)
            {
                alertDelay.Dispose();
            }
        }
    }
}
请注意,在每个平台上,我们都需要使用DependencyService注册我们的类。
现在,您可以在项目中的任何地方访问我们的Toast服务。
DependencyService.Get<IMessage>().ShortAlert(string message); 
DependencyService.Get<IMessage>().LongAlert(string message);

27
这绝对是这个问题最好的答案。不需要任何第三方插件或库。 - Bret Faller
5
在DependencyService这一行,我遇到了“Object reference not set to an instance of an object.”的错误信息。 - Joyce de Lanna
5
是的,这是目前为止最好的答案,我喜欢依赖服务。 - Lutaaya Huzaifah Idris
2
充满胜利。你是否也有一个UWP版本? - Nieminen
4
@JoycedeLanna 不要忘记在 MainActivity.cs注册 接口。在 LoadApplication(new App()) 之前添加这行代码 DependencyService.Register<IMessage, MessageAndroid>(); - Sadra M.
显示剩余12条评论

23
您可以使用NuGet中的Acr.UserDialogs包,并像下面这样编写代码:

Acr.UserDialogs

Acr.UserDialogs.UserDialogs.Instance.Toast(Message, new TimeSpan(3));

18

这是 Alex Chengalan 的 iOS 代码 的一个版本,避免了当显示多个消息时 UI 卡顿的问题...


public class MessageIOS : IMessage
    {
        const double LONG_DELAY = 3.5;
        const double SHORT_DELAY = 0.75;

        public void LongAlert(string message)
        {
            ShowAlert(message, LONG_DELAY);
        }

        public void ShortAlert(string message)
        {
            ShowAlert(message, SHORT_DELAY);
        }

        void ShowAlert(string message, double seconds)
        {
            var alert = UIAlertController.Create(null, message, UIAlertControllerStyle.Alert);

            var alertDelay = NSTimer.CreateScheduledTimer(seconds, obj =>
            {
                DismissMessage(alert, obj);
            });

            UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(alert, true, null);
        }

        void DismissMessage(UIAlertController alert, NSTimer alertDelay)
        {
            if (alert != null)
            {
                alert.DismissViewController(true, null);
            }

            if (alertDelay != null)
            {
                alertDelay.Dispose();
            }
        }
    }

这个不起作用。不确定是否与最新的iOS版本有关,但已经不再工作。 - Emil

17
您可以使用Xamarin社区工具包中的SnackBar,在本地支持的平台上使用原生实现,因为Toast已在API级别30中弃用。没有操作的SnackBar等效于一个Toast。

该方法已在API级别30中弃用。自定义Toast视图已被弃用。应用程序可以使用makeText(android.content.Context, java.lang.CharSequence, int)方法创建标准文本Toast,或在前台时使用Snackbar。从Android Build.VERSION_CODES#R开始,定位API级别Build.VERSION_CODES#R或更高版本且在后台的应用程序将不会显示自定义Toast视图。(来源)。

使用Xamarin Community Toolkit开始

  1. 在所有项目中安装包
  2. 导入命名空间:using Xamarin.CommunityToolkit.Extensions;
  3. 在页面代码后台上,在事件发生时显示SnackBar
await this.DisplayToastAsync("This is a Toast Message");
await this.DisplayToastAsync("This is a Toast Message for 5 seconds", 5000);

您可以指定SnackBar消失的持续时间(以毫秒为单位),或者保留默认时间,即3秒。

enter image description here


资源

SnackBar样例

官方代码库 https://github.com/xamarin/XamarinCommunityToolkit

官方文档 https://learn.microsoft.com/en-us/xamarin/community-toolkit/


编辑

  1. 锚定式Toast: 您可以通过调用该视图(锚点)对象上的扩展方法DisplayToastAsync(),而不是从页面实例(this)上调用,将Toast 锚定在某个视图上(如上面的屏幕截图):
<Button x:name="floatingButton" .../>

await floatingButton.DisplayToastAsync("This is a Toast Message for 5 seconds", 5000);
  1. 填充和圆角:(从xct版本1.3.0预览-1开始)

您可以像下面的示例一样设置Toast的圆角和填充:

var messageOptions = new MessageOptions
{
    Message = "Toast with Padding and round corner",
    Foreground = Color.White,
    Font = Font.SystemFontOfSize(16),
    Padding = new Thickness(20)
};

var options = new ToastOptions
    {
        MessageOptions = messageOptions,
        CornerRadius = new Thickness(40, 40, 0, 0),
        BackgroundColor = Color.FromHex("#CC0000")
    };

await this.DisplayToastAsync(options);

输入图像描述

输入图像描述

PS:相同的属性可以应用于SnackBar视图。


编辑2

如果xct SnackBar提供的内容无法满足您的要求,或者您想要显示不仅是文本而是一些复杂的视图,则可能需要使用弹出窗口


如何将文本显示在中心,并为 Snackbar 提供边距。 - Priyanka
我真希望在尝试最受欢迎的答案之前看到了这个。天哪。 - Krausladen
@Priyanka,这个我不太确定。如果你认为这可能是一个新特性,你可以在 GitHub 存储库中请求它,或者查看弹出控件(虽然在这种情况下它可能有点过度设计)。Krausladen 表示很高兴知道它对你有帮助。 - Cfun

11

继Alex的回答之后,这里是UWP版本:

public class Message : IMessage {
  private const double LONG_DELAY = 3.5;
  private const double SHORT_DELAY = 2.0;

  public void LongAlert(string message) =>
    ShowMessage(message, LONG_DELAY);

  public void ShortAlert(string message) =>
    ShowMessage(message, SHORT_DELAY);

  private void ShowMessage(string message, double duration) {
    var label = new TextBlock {
      Text = message,
      Foreground = new SolidColorBrush(Windows.UI.Colors.White),
      HorizontalAlignment = HorizontalAlignment.Center,
      VerticalAlignment = VerticalAlignment.Center,
    };
    var style = new Style { TargetType = typeof(FlyoutPresenter) };
    style.Setters.Add(new Setter(Control.BackgroundProperty, new SolidColorBrush(Windows.UI.Colors.Black)));
    style.Setters.Add(new Setter(FrameworkElement.MaxHeightProperty, 1));
    var flyout = new Flyout {
      Content = label,
      Placement = FlyoutPlacementMode.Full,
      FlyoutPresenterStyle = style,
    };

    flyout.ShowAt(Window.Current.Content as FrameworkElement);

    var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(duration) };
    timer.Tick += (sender, e) => {
      timer.Stop();
      flyout.Hide();
    };
    timer.Start();
  }
}

彩色和样式由您决定,MaxHeight 实际上是必需的,以保持高度最小。


那么在 UWP 中注册它作为依赖服务是不需要的吗? - Olorunfemi Davis
它的工作方式与其他两个变体完全相同。是的,一个依赖服务。 - Gábor

9

我们通常会使用Egors Toasts插件,但由于当前项目在iOS上需要权限,因此我们采用了不同的方法,使用Rg.Plugins.Popup nuget (https://github.com/rotorgames/Rg.Plugins.Popup)。

我编写了一个基本的xaml/cs页面类型为PopupPage,

<?xml version="1.0" encoding="utf-8" ?>
<popup:PopupPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:popup="clr-namespace:Rg.Plugins.Popup.Pages;assembly=Rg.Plugins.Popup"
         x:Class="YourApp.Controls.ToastPage">
...

如果您注册了应用程序启动时使用的服务接口,或使用Xamarin.Forms.DependencyService获取服务,则可以将其创建为服务。

该服务会新建基于PopupPage的页面,并执行相关操作。

await PopupNavigation.PushAsync(newToastPage);
await Task.Delay(2000);
await PopupNavigation.PopAllAsync();

弹出页面可以通过点击页面外部(假设它没有填满屏幕)来关闭。

在iOS/Droid上似乎运作良好,但如果有人知道这种方法存在风险,请指正。


rg弹出窗口非常好用。我使用类似的解决方法来在整个页面上加载显示或活动指示器。其他插件的问题在于它们依赖于异步函数和主线程,但是rg弹出窗口可以在第二个线程上运行,这使其非常有用。确实是个好主意,但我希望它能像Android Toasts一样具有本地外观。 - Emil
到目前为止,这是实现跨平台提示的最佳方法。Rg.Popup非常灵活,我几乎在每个项目中都使用它。无需使用其他插件或平台代码来显示提示。 - GiampaoloGabba

6

您可以使用 IUserDialog NuGet 并直接使用其 toastAlert 功能。

var toastConfig = new ToastConfig("Toasting...");
toastConfig.SetDuration(3000);
toastConfig.SetBackgroundColor(System.Drawing.Color.FromArgb(12, 131, 193));

UserDialogs.Instance.Toast(toastConfig);

4

@MengTim,为了解决@alex-chengalan的解决方案中的多个toast问题,我只需在ShowAlert()中包含一个检查,以查看alertalertDelay是否为空,然后在DismissMessage中将alertalertDelay设为null即可。

void ShowAlert(string message, double seconds)
    {
        if(alert == null && alertDelay == null) {
            alertDelay = NSTimer.CreateScheduledTimer(seconds, (obj) =>
            {
                DismissMessage();
            });
            alert = UIAlertController.Create(null, message, UIAlertControllerStyle.Alert);
            UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(alert, true, null);
        }
    }

    void DismissMessage()
    {
        if (alert != null)
        {
            alert.DismissViewController(true, null);
            alert = null;
        }
        if (alertDelay != null)
        {
            alertDelay.Dispose();
            alertDelay = null;
        }
    }

如果您正在寻找一个快速解决方案,那看起来至少可以清除UI挂起问题。我试图在导航到新页面时显示toast,并认为设置的PresentViewController本质上取消了我的导航。很抱歉我没有在帖子中发表评论,我的声望太低了:(


4

我建议从nuget下载Plugin.Toast库。它功能良好。

CrossToastPopUp.Current.ShowToastMessage("my toast message");

或者从ACR.UserDialogs Nuget库中获取

UserDialogs.Instance.ShowLoading("Loading");

有没有办法将它移动到顶部?自定义并显示多个选项? - G_Money
不,这个库仅支持基本的Toast消息。您只能更改背景和文本颜色以及消息的持续时间。 - Fk Bey

4

这里是我在Xamarin.iOS中使用的代码片段,用于显示toast:

  public void ShowToast(String message, UIView view)
    {
        UIView residualView = view.ViewWithTag(1989);
        if (residualView != null)
            residualView.RemoveFromSuperview();

        var viewBack = new UIView(new CoreGraphics.CGRect(83, 0, 300, 100));
        viewBack.BackgroundColor = UIColor.Black;
        viewBack.Tag = 1989;
        UILabel lblMsg = new UILabel(new CoreGraphics.CGRect(0, 20, 300, 60));
        lblMsg.Lines = 2;
        lblMsg.Text = message;
        lblMsg.TextColor = UIColor.White;
        lblMsg.TextAlignment = UITextAlignment.Center;
        viewBack.Center = view.Center;
        viewBack.AddSubview(lblMsg);
        view.AddSubview(viewBack);
        roundtheCorner(viewBack);
        UIView.BeginAnimations("Toast");
        UIView.SetAnimationDuration(3.0f);
        viewBack.Alpha = 0.0f;
        UIView.CommitAnimations();
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接