使用Android AlertDialog从Func〈object,bool>实现返回true/false值,而不会阻塞UI

pn9klfpd  于 2023-01-19  发布在  Android
关注(0)|答案(2)|浏览(104)

我必须在我的Xamarin项目中使用一个函数,它有一个DoOperation(Func<object, bool> ConfirmOperation)重载,对象有一个属性,叫做string TextToCheck,函数要检查这个属性,如果符合一定的条件,就需要询问用户是否要继续操作,DoOperation(Func<object, bool> ConfirmOperation)自身调用的Func<object, bool>函数的实现在Windows中类似于以下内容。窗体:

private bool ConfirmOperation(object Object) {
    if(Object.TextToCheck == Criteria) {
        if(MessageBox.Show("MESSAGE", "TITLE", MessageBoxButtons.YesNo) == DialogResult.No) {
                return false;
        } else {
            return true;
        }
    } else
        return true;
}

当然,这会阻塞UI,但至少它是有效的,我看不出在这种情况下阻塞有什么缺点。
我的问题是:如何在我的Xamarin.Android项目(Android 4.4.2)中实现这个private bool ConfirmOperation(object Object)函数,该函数会提示用户并根据Yes/No按钮按下返回bool结果,并且不会阻塞UI?
这显然行不通,因为. Show()不会阻塞UI:

private bool ConfirmOperation(object Object) {
    if(Object.TextToCheck!= "AABBCCDD") {
        AlertDialog.Builder AlertDialog = new AlertDialog.Builder(this);
        AlertDialog.SetTitle("Warning!");
        AlertDialog.SetMessage("Do you want to proceed with the operation?");
        AlertDialog.SetNegativeButton("No", (senderAlert, args) => {

                });

        AlertDialog.SetPositiveButton("Yes", (senderAlert, args) => {

                });

        AlertDialog.Show();

        return TheResultFromTheAlertDialog;
    }

    else {
        return true;
    }
}
m4pnthwp

m4pnthwp1#

提示用户,并根据按下是/否按钮返回布尔结果,并且不会阻塞UI?

    • 更新日期:**

这里有一个更优雅的解决方案,您可以使用AutoResetEvent显示Alert,然后将其 Package 在Task中并将其命名为Async

示例:

public async Task<bool> DisplayMessage(string titile, string content)
{
    objDialog = new AlertDialog.Builder(this)
       .SetTitle(titile)
       .SetMessage(content)
       .SetCancelable(false)
       .Create();
    bool result = false;

    await Task.Run(() =>
    {
        var waitHandle = new AutoResetEvent(false);
        objDialog.SetButton((int)(DialogButtonType.Positive), "yes", (sender, e) =>
        {
            result = true;
            waitHandle.Set();
        });

        objDialog.SetButton((int)DialogButtonType.Negative, "no", (sender, e) =>
        {
            result = false;
            waitHandle.Set();
        });

        RunOnUiThread(() =>
        {
            objDialog.Show();
        });
        waitHandle.WaitOne();
    });
    objDialog.Dispose();
    return result;
}

用法:

button.Click += async (s, e) =>
{
    var result = await DisplayMessage("Title", "Content");
    System.Diagnostics.Debug.WriteLine(result + "=====================");
};

它完美地工作。
您可以使用Looper.Loop()来阻止UI线程,直到您从ConfirmOperation方法获得结果。最重要的是,此方法不会触发ANR
下面是我的代码:

private bool ConfirmOperation(string Object)
    {
        if (Object != "AABBCCDD")
        {
            if (Object == "user_Click_no")
            {
                return false;
            }
            bool TheResultFromTheAlertDialog = false;

            AlertDialog.Builder AlertDialog = new AlertDialog.Builder(this);

            AlertDialog.SetTitle("Warning!");
            AlertDialog.SetMessage("Do you want to proceed with the operation?");
            AlertDialog.SetNegativeButton("No", (senderAlert, args) => {

                Message message = mHandler.ObtainMessage();
                message.What = 1;
                mHandler.SendMessage(message);
            });
            AlertDialog.SetPositiveButton("Yes", (senderAlert, args) => {

                Message message = mHandler.ObtainMessage();
                message.What = 0;
                mHandler.SendMessage(message);
            });

            AlertDialog.Show();

            try { Looper.Loop(); } catch (Java.Lang.Exception e) { }

            return TheResultFromTheAlertDialog;//Actually, it didn't work
        }
        else
        {
            //It's true or user click yes.
            return true;
        }
    }

当它不符合你的条件时,它会显示一个Dialog并阻塞主线程。一旦用户点击yesno按钮,它会发送一条消息并抛出新的RuntimeException(),因此,这个块将被解除,你可以在你的Handler类中接收消息。然后你可以调用ConfirmOperation方法得到结果。(只需做出判断并返回一个值。)

mHandler = new MyHandler(this);
 ...

public class MyHandler : Handler
    {
        public MainActivity mainActivity;
        public MyHandler(MainActivity mainActivity)
        {
            this.mainActivity = mainActivity;
        }

        public override void HandleMessage(Message msg)
        {
            try
            {
                throw new RuntimeException();
            }
            catch
            {
            }
            finally
            {
                if (msg.What == 0)//click yes
                {
                    var a = mainActivity.ConfirmOperation("AABBCCDD");//return true;

                    Toast.MakeText(mainActivity, "user_Click_yes", ToastLength.Short).Show();
                }
                else if (msg.What == 1)//click no
                {
                    var a = mainActivity.ConfirmOperation("user_Click_no");//return false;
                    Toast.MakeText(mainActivity, "user_Click_no", ToastLength.Short).Show();
                }
            }
        }
    }
ghhkc1vu

ghhkc1vu2#

对于这种情况,TaskCompletionSource类非常有用。
何时应使用TaskCompletionSource?
请考虑实现以下扩展方法

public static async Task<bool?> ShowAlertDialogAsync(this Context context,
        string title, string message,
        string positive = default,
        string negative = default,
        string neutral = default)
    {
        var source = new TaskCompletionSource<bool?>();

        new AlertDialog.Builder(context).SetTitle(title).SetMessage(message)
            .SetPositiveButton(positive, (sender, args) => source.SetResult(true))
            .SetNegativeButton(negative, (sender, args) => source.SetResult(false))
            .SetNeutralButton(neutral, (sender, args) => source.SetResult(default))
            .Show();

        return await source.Task;
    }

如有疑问

var result = await activity.ShowAlertDialogAsync
    (
        "Question", "Confirm the action?", "Yes", "No"
    );
    
    if (result is true) PerformAction();

通知

await activity.ShowAlertDialogAsync
    (
        "Notice", "Hey, you are so fine!", neutral: "Ok"
    );

相关问题