来自.NET环境,我现在正在研究如何在Android中使用Dialogs。
在.NET中,调用创建并显示弹出对话框的MessageBox.Show(...)
时。在对Show的调用中,我可以指定弹出窗口中应该有哪些按钮,例如:
DialogResult myDialogResult = MessageBox.Show("My text here", "My caption here", MessageBoxButtons.YesNoCancel);
正如您所看到的,当在弹出窗口中按下按钮时,对Show的调用将返回一个DialogResult,通知我单击了哪个按钮。请注意,在.NET中,执行会在调用Show(...)
的行中停止,因此它可以在按下按钮时返回值。
如果我在上面的例子中按“否”,myDialogResult将等于
myDialogResult == DialogResult.No
由于我发现使用/创建弹出窗口的.NET方式非常简单直观,我想在Android中创建弹出窗口的方式也是如此。
所以,问题是,是否有人知道如何“停止执行”,就像MessageBox.Show
一样,然后每按一次按钮就返回一个值(并且对话框消失了)?
更清楚一点:
我需要执行停止并等到用户选择了一个按钮才能在弹出窗口中单击。显示对话框的调用后面的代码取决于在对话框中单击的按钮。
这就是为什么我不能使用Erich和Alex建议的原因,因为在下面建议的onClick方法中编写代码是行不通的。原因是我无法继续“正常执行”。让我举一个例子:
让我举一个例子:
int nextStep = 0; // this variable will not be reached from within the onClick-methods
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Hello!")
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
nextStep = 1; // *** COMPILER ERROR!! ***
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
nextStep = 2; // *** COMPILER ERROR!! ***
}
})
.create().show();
if (nextStep == 1)
{
// then do some damage
}
else if (nextStep == 2
// dont do damage
如果我希望执行依赖于弹出窗口中的选择,我会以某种方式使onClick方法中的“正常执行”(在本例中为nextStep
)中的所有变量都可用,这对我来说听起来很糟糕。
另一个明显的例子是弹出窗口询问“你想继续”选项“是”和“否”。
如果用户按“是”,则应中止整个方法,否则应继续执行。你怎么解决这个问题呢?
答案 0 :(得分:45)
Ted,你不想这样做,真的:)最大的原因是如果你在显示一个Dialog时阻塞UI线程,你将阻止负责绘制和处理事件的线程你的对话。这意味着您的对话框将无响应。如果用户点击对话框需要几秒钟,您还将导致ANR。
Erich的答案正是你所需要的。我知道这不是你想要的,但这并不重要。我们设计了Android以防止开发人员编写同步对话框,因此您没有太多选择。
答案 1 :(得分:19)
在Android中,结构与.NET不同:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Hello!")
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Handle Ok
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// Handle Cancel
}
})
.create();
将为您提供一个包含两个按钮的对话框,您可以使用回调来处理按钮点击。您可能能够编写一些代码来使语法更接近.NET,但对话框生命周期与Activity
非常交织在一起,所以最终它可能比它的价值更麻烦。其他对话框引用为here。
答案 2 :(得分:19)
上面Daniel的答案的简化版本。该功能在警告对话框中从用户获得是或否,但可以轻松修改以获得其他输入。
private boolean mResult;
public boolean getYesNoWithExecutionStop(String title, String message, Context context) {
// make a handler that throws a runtime exception when a message is received
final Handler handler = new Handler() {
@Override
public void handleMessage(Message mesg) {
throw new RuntimeException();
}
};
// make a text input dialog and show it
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setTitle(title);
alert.setMessage(message);
alert.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
mResult = true;
handler.sendMessage(handler.obtainMessage());
}
});
alert.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
mResult = false;
handler.sendMessage(handler.obtainMessage());
}
});
alert.show();
// loop till a runtime exception is triggered.
try { Looper.loop(); }
catch(RuntimeException e2) {}
return mResult;
}
答案 3 :(得分:11)
在Android中,Dialogs是异步的,所以你必须以不同的方式构建你的代码。
所以在C#中你的逻辑在伪代码中运行了类似的东西:
void doSomeStuff() {
int result = showDialog("Pick Yes or No");
if (result == YES) {
//do stuff for yes
}
else if (result == NO) {
//do stuff for no
}
//finish off here
}
对于Android来说,它必须不那么整洁。想想就好。你会有OnClickListener
这样的:
public void onClick(DialogInterface dialog, int whichButton) {
if (whichButton == BUTTON_POSITIVE) {
doOptionYes();
}
else if (whichButton == BUTTON_NEGATIVE) {
doOptionNo();
}
}
以下方法支持以下方法:
void doOptionYes() {
//do stuff for yes
endThings();
}
void doOptionNo() {
//do stuff for no
endThings();
}
void endThings() {
//clean up here
}
那么一种方法现在是四种。它可能看起来不是很整洁,但我担心它的工作原理。
答案 4 :(得分:8)
PasswordDialog dlg = new PasswordDialog(this);
if(dlg.showDialog() == DialogResult.OK)
{
//blabla, anything your self
}
public class PasswordDialog extends Dialog
{
int dialogResult;
Handler mHandler ;
public PasswordDialog(Activity context, String mailName, boolean retry)
{
super(context);
setOwnerActivity(context);
onCreate();
TextView promptLbl = (TextView) findViewById(R.id.promptLbl);
promptLbl.setText("Input password/n" + mailName);
}
public int getDialogResult()
{
return dialogResult;
}
public void setDialogResult(int dialogResult)
{
this.dialogResult = dialogResult;
}
/** Called when the activity is first created. */
public void onCreate() {
setContentView(R.layout.password_dialog);
findViewById(R.id.cancelBtn).setOnClickListener(new android.view.View.OnClickListener() {
@Override
public void onClick(View paramView)
{
endDialog(DialogResult.CANCEL);
}
});
findViewById(R.id.okBtn).setOnClickListener(new android.view.View.OnClickListener() {
@Override
public void onClick(View paramView)
{
endDialog(DialogResult.OK);
}
});
}
public void endDialog(int result)
{
dismiss();
setDialogResult(result);
Message m = mHandler.obtainMessage();
mHandler.sendMessage(m);
}
public int showDialog()
{
mHandler = new Handler() {
@Override
public void handleMessage(Message mesg) {
// process incoming messages here
//super.handleMessage(msg);
throw new RuntimeException();
}
};
super.show();
try {
Looper.getMainLooper().loop();
}
catch(RuntimeException e2)
{
}
return dialogResult;
}
}
答案 5 :(得分:5)
Android和iOS的开发人员认为他们强大而聪明,足以拒绝Modal Dialog概念(这已经在市场上存在很多年了,之前并没有打扰过任何人),不幸的是我们。我相信Android的解决方案 - 因为你可以使用Runnable类从非ui线程显示对话框,所以应该有一种方法在该线程(非ui)中等待,直到对话框完成。
编辑: 这是我的解决方案,效果很好:
int pressedButtonID;
private final Semaphore dialogSemaphore = new Semaphore(0, true);
final Runnable mMyDialog = new Runnable()
{
public void run()
{
AlertDialog errorDialog = new AlertDialog.Builder( [your activity object here] ).create();
errorDialog.setMessage("My dialog!");
errorDialog.setButton("My Button1", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
pressedButtonID = MY_BUTTON_ID1;
dialogSemaphore.release();
}
});
errorDialog.setButton2("My Button2", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
pressedButtonID = MY_BUTTON_ID2;
dialogSemaphore.release();
}
});
errorDialog.setCancelable(false);
errorDialog.show();
}
};
public int ShowMyModalDialog() //should be called from non-UI thread
{
pressedButtonID = MY_BUTTON_INVALID_ID;
runOnUiThread(mMyDialog);
try
{
dialogSemaphore.acquire();
}
catch (InterruptedException e)
{
}
return pressedButtonID;
}
答案 6 :(得分:5)
尝试在Android中优化内存和性能对话框是异步的(因此也会管理它们)。来自Windows世界,您习惯于模态对话框。 Android对话框是模态的,但在执行时更像非模态。显示对话框后,执行不会停止。
我见过的Android中对话的最佳描述是“Pro Android”http://www.apress.com/book/view/1430215968
这不是一个完美的解释,但它应该可以帮助您将大脑与Windows和Android中的对话框区别开来。在Windows中你想做A,用对话框问一个问题,然后做B或C.在android设计中,你需要在OnClickListener的onClick()中为B和C所需的所有代码用于对话框。然后执行A并启动对话框。你完成了A!当用户点击按钮时,B或C将被执行。
Windows
-------
A code
launch dialog
user picks B or C
B or C code
done!
Android
-------
OnClick for B code (does not get executed yet)
OnClick for C code (does not get executed yet)
A code
launch dialog
done!
user picks B or C
答案 7 :(得分:4)
Ted,你可能已经发现,不幸的是你在Android上无法做到这一点。对话是模态的,但是是异步的,它肯定会破坏你试图建立的序列,就像在.NET(或Windows)上所做的那样。您将不得不扭曲代码并打破一些根据您的示例非常容易理解的逻辑。
另一个非常简单的例子是将数据保存在文件中,只是为了发现该文件已存在并要求覆盖它。您不必显示对话框并使用if语句对结果进行操作(是/否),而是必须使用回调(在Java中称为侦听器)并将逻辑拆分为多个函数。
在Windows上,当显示一个对话框时,消息泵在后台继续(只有正在处理的当前消息处于保持状态),并且工作正常。这允许用户移动您的应用程序,并在您显示对话框时重新绘制。 WinMo支持同步模态对话框,BlackBerry也是如此,但不支持Android。
答案 8 :(得分:2)
最简洁和最简单的解决方案是使用您自己的侦听器接口,以便当用户单击“确定”按钮时,将使用返回值调用侦听器。这种方法没有什么花哨或复杂的东西,并且尊重android原则。
定义您的侦听器界面,如下所示:
public interface EditListener
/* Used to get an integer return value from a dialog
*
*/
{
void returnValue(int value);
}
对于我的应用程序,我创建了一个EditValue类,它使用AlertDialog,每当我想编辑一个整数值时我都会调用它。请注意EditListener接口如何作为参数传递给此代码。当用户单击“确定”按钮时,该值将通过EditListener方法返回到您的调用代码:
public final class EditValue
/* Used to edit a value using an alert dialog
* The edited value is returned via the returnValue method of the supplied EditListener interface
* Could be modified with different constructors to edit double/float etc
*/
{
public EditValue(final Activity parent, int value, String title, String message,
final EditListener editListener)
{AlertDialog.Builder alert= new AlertDialog.Builder(parent);
if(title==null) title= message;
else if(message==null) message= title;
if(title!=null) alert.setTitle(title);
if(message!=null) alert.setMessage(message);
// Set an EditText view to get user input
final EditText input = new EditText(parent);
input.setText(String.valueOf(value));
input.setInputType(InputType.TYPE_CLASS_NUMBER);
alert.setView(input);
alert.setPositiveButton("OK",new DialogInterface.OnClickListener()
{public void onClick(DialogInterface dialog, int which)
{try
{int newValue= Integer.valueOf(input.getText().toString());
editListener.returnValue(newValue);
dialog.dismiss();
}catch(NumberFormatException err) { }
}
});
alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener()
{public void onClick(DialogInterface dialog, int which)
{dialog.dismiss();
}
});
alert.show();
}
}
最后,当您使用EditValue时,您需要声明EditListener,现在您可以访问返回值并执行您想要执行的操作:
new EditValue(main,AnchorManager.anchorageLimit,
main.res.getString(R.string.config_anchorage_limit),null,
new EditListener()
{public void returnValue(int value) {AnchorManager.anchorageLimit= value;}
}
);
答案 9 :(得分:1)
答案 10 :(得分:1)
重写:
移动和桌面环境之间以及应用程序开发方式与几年前和今天之间存在根本区别:
a)移动设备需要节约能源。他们提供的部分价值。所以你需要节省资源。线程是一种昂贵的资源。停止线程的进度是不可接受的浪费这个资源。b)如今用户要求更高。为了帮助他们,我们相信它应该具有完全工作的CPU和尽可能小的能量消耗。它的应用并不是单独在设备上,有一个未知数量的其他应用程序同时运行,你的应用程序不一定是最紧急的。
c)系统级锁不是一种选择:移动设备在后台处理许多事件和服务,并且它们中的任何一个都不能被应用程序锁定。考虑用户在“系统锁定”工作时接听电话......
基于上述事实,提出的问题的答案是:
号。变通办法会使用户体验变得更糟,并且可能会错误地指责系统本身。这是不公平的,会对平台及其所有开发人员造成不利影响。
号。这在平台上是严格禁止的。任何应用程序都不会干扰系统或其他应用程序的运行。
是。包括这方面。
答案 11 :(得分:0)
在线程中尝试此操作(不是UI-Thread):
final CountDownLatch latch = new CountDownLatch(1);
handler.post(new Runnable() {
@Override
public void run() {
OnClickListener okListener = new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
latch.countDown();
}
};
AlertDialog dialog = new AlertDialog.Builder(context).setTitle(title)
.setMessage(msg).setPositiveButton("OK", okListener).create();
dialog.show();
}
});
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
答案 12 :(得分:0)
您将onclick侦听器设置为按钮。 dissmis对话并做你的行动。不需要停止任何事情
protected Dialog onCreateDialog(int id) {
return new AlertDialog.Builder(this).setTitle(R.string.no_connection).setIcon(android.R.drawable.ic_dialog_alert).setView(textEntryView).setPositiveButton(R.string.exit, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int whichButton) {
// Here goes what you want to do
}
})
}
调用 - ex - showDialog(DIALOG_ERROR_PREF);
更多 http://developer.android.com/guide/topics/ui/dialogs.html
答案 13 :(得分:0)
回答你的问题......顺便说一下,我已经晚了9个月了:D ......有一种“解决方法”4这种问题。即
new AlertDialog.Builder(some_class.this).setTitle("bla").setMessage("bla bla").show();
wait();
只需添加等待();
他们在OnClickListener中再次使用通知()启动该类
@Override
public void onClick(DialogInterface dialog, int item) {
Toast.makeText(getApplicationContext(), "test", Toast.LENGTH_LONG).show();
**notify**();
dialog.cancel();
}
同样的解决方法是4个toast和android中的其他异步调用
答案 14 :(得分:0)
我是Android / Java世界的新手,很惊讶地发现这里(除非我不明白我读到的内容)模态对话框不起作用。对于我目前的一些非常模糊的原因,我得到了这个“ShowMessage”相当于一个ok按钮,可以在我的平板电脑上以非常模式的方式工作。
来自我的TDialogs.java模块:
class DialogMes
{
AlertDialog alertDialog ;
private final Message NO_HANDLER = null;
public DialogMes(Activity parent,String aTitle, String mes)
{
alertDialog = new AlertDialog.Builder(parent).create();
alertDialog.setTitle(aTitle);
alertDialog.setMessage(mes) ;
alertDialog.setButton("OK",NO_HANDLER) ;
alertDialog.show() ;
}
}
以下是测试代码的一部分:
public class TestDialogsActivity extends Activity implements DlgConfirmEvent
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btShowMessage = (Button) findViewById(R.id.btShowMessage);
btShowMessage.setOnClickListener(new View.OnClickListener() {
public void onClick(View view)
{
DialogMes dlgMes = new DialogMes( TestDialogsActivity.this,"Message","life is good") ;
}
});
我还按照JohnnyBeGood上面建议的界面方法实现了一个模态是/否对话框,它的效果也很好。
<强>校正:强>
我的回答与我误解的问题无关。出于某种原因,我解释了M. Romain Guy“你不想那样做”作为对模态对话的禁忌。我应该读到:“你不想这样做......这样”。
我道歉。
答案 15 :(得分:0)
UserSelect =null
AlertDialog.Builder builder = new Builder(ImonaAndroidApp.LoginScreen);
builder.setMessage("you message");
builder.setPositiveButton("OK", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
UserSelect = true ;
}
});
builder.setNegativeButton("Cancel", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
UserSelect = false ;
}
});
// in UI thread
builder.show();
// wait until the user select
while(UserSelect ==null);
答案 16 :(得分:0)
我正在使用Xamarin.Android(MonoDroid),我有开发UI Blocking确认框的要求。我不打算与客户争论,因为我可以看到他们为什么要这样做的充分理由(details here),所以我需要实现这一点。我上面尝试过@Daniel和@MindSpiker,但是这些在MonoForAndroid上没有用,在线程之间发送消息的那一刻,应用程序崩溃了。我认为它与Xamarin映射有关。
我最终从UI线程创建一个单独的线程,然后阻止它并等待用户响应,如下所示:
// (since the controllers code is shared cross-platforms)
protected void RunConfirmAction(Action runnableAction)
{
if (runnableAction != null)
{
if (Core.Platform.IsAndroid)
{
var confirmThread = new Thread(() => runnableAction());
confirmThread.Start();
}
else
{
runnableAction();
}
}
}
// The call to the logout method has now changed like this:
RunConfirmAction(Logout);
// the implemtation of the MessageBox waiting is like this:
public DialogResult MessageBoxShow(string message, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
{
if (_CurrentContext != null && _CurrentContext.Screen != null && MainForm.MainActivity != null)
{
Action<bool> callback = OnConfirmCallBack;
_IsCurrentlyInConfirmProcess = true;
Action messageBoxDelegate = () => MessageBox.Show(((Activity)MainForm.MainActivity), callback, message, caption, buttons);
RunOnMainUiThread(messageBoxDelegate);
while (_IsCurrentlyInConfirmProcess)
{
Thread.Sleep(1000);
}
}
else
{
LogHandler.LogError("Trying to display a Message box with no activity in the CurrentContext. Message was: " + message);
}
return _ConfirmBoxResult ? DialogResult.OK : DialogResult.No;
}
private void OnConfirmCallBack(bool confirmResult)
{
_ConfirmBoxResult = confirmResult;
_IsCurrentlyInConfirmProcess = false;
}
private bool _ConfirmBoxResult = false;
private bool _IsCurrentlyInConfirmProcess = false;
有关如何执行此操作的详细信息,请参阅我的博文here
答案 17 :(得分:0)
让我对StackOverflow社区表示感谢,并与您分享一些美好的东西,我在Android代码中将上述示例用于HTTP调用,现在变成了MODAL,而不是通常的额外线程以及合并代码的复杂性通过使用一些特殊的线程。 (目前正在我们的应用程序中运行-2020年10月15日)
public JSONArray genericHttpModal(Context context, final String url, final JSONObject request) {
this.url = url;
genericHttpRequestQueue = Volley.newRequestQueue(context);
class MyJsonReturn {
JSONArray returnJsonArray;
public void set(JSONArray i) {
returnJsonArray = i;
}
public void set(String i) {
try {
returnJsonArray.put(0, i);
} catch (JSONException e) {
e.printStackTrace();
}
}
public JSONArray get() {
return returnJsonArray;
}
}
final MyJsonReturn mymy = new MyJsonReturn();
// Positive Response / HTTP OK.
final Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
throw new RuntimeException();
}
};
final Response.Listener responseListener = new Response.Listener<String>() {
@Override
public void onResponse(String response) {
try {
mymy.set(new JSONArray(response));
} catch (JSONException e) {
mymy.set("[{\"JSONException:\"" + e.getMessage() + "\"}]");
}
handler.sendMessage(handler.obtainMessage());
}
};
// Negative Response / HTTP NOT OK
final Response.ErrorListener errorListener = new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
result = "fail";
try {
mymy.set(new JSONArray("[{\"JSONException:\"" + result + "\"}]"));
} catch (JSONException e) {
mymy.set("[{\"JSONException:\"" + e.getMessage() + "\"}]");
}
handler.sendMessage(handler.obtainMessage());
}
};
final StringRequest httpRequest = new StringRequest(Request.Method.POST, URL_POINTER + url,
responseListener,
errorListener) {
// Here the mRequestQueue handler will get the parameters for this request here.
// Ref: https://stackoverflow.com/questions/33573803/how-to-send-a-post-request-using-volley-with-string-body#33578202
// Ref: Multi Threaded solution 14 Oct 2020 (David Svarrer) : https://stackoverflow.com/questions/2028697/dialogs-alertdialogs-how-to-block-execution-while-dialog-is-up-net-style (This stackoverflow here)
@Override
protected java.util.Map<String, String> getParams() throws AuthFailureError {
return jsonObjectToMap(request);
}
};
httpRequest.setShouldCache(false); // Elijah: Could work on dropping the cache !!!
genericHttpRequestQueue.add(httpRequest);
try {
Looper.loop();
} catch (RuntimeException re) {
}
return mymy.get();
}
答案 18 :(得分:0)
胡!该死的,我为此苦苦挣扎了一整天。不知何故,我得到了解决方法,尽管我不推荐它。你将不得不使用 Handler 来实现这一点。
调用getDialogBack
函数获取
boolean result = getDialogBack(this);
Log.d(TAG, "onCreate: "+result);
在 getDialogBack
中编写对话框和处理程序代码以使其同步
public int getDialogBack(Context context,int i) {
final Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message mesg) {
throw new RuntimeException();
}
};
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setTitle("Title");
alert.setMessage("Message");
alert.setPositiveButton("Want to print next", new
DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
handler.sendMessage(handler.obtainMessage());
}
});
alert.setNegativeButton("Return False", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
handler.sendMessage(handler.obtainMessage());
}
});
alert.show();
try {
Looper.loop();
} catch (RuntimeException e) {
}
return true;
}
答案 19 :(得分:-1)
这是最简单的方法:
new AlertDialog.Builder(this).setTitle("title").setMessage("message").create().show();