대화 상자 / AlertDialogs : 대화 상자가 켜져있는 동안 "실행 차단"방법 (.NET 스타일)
.NET 환경에서 왔기 때문에 이제 Android에서 대화 상자가 작동하는 방식을 이해하려고합니다.
.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
하고 버튼을 누를 때마다 값을 반환하는 방법을 알고 있는지 (그리고 대화 상자가 사라질 때)입니다.
편집 1
좀 더 명확하게하려면 :
실행을 중지하고 사용자가 팝업을 클릭 할 버튼을 선택할 때까지 기다려야합니다. Dialog를 표시하기 위해 호출을 따르는 코드는 Dialog에서 클릭 한 버튼에 따라 다릅니다.
이것이 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
실행이 팝업의 선택에 따라 달라지기를 원한다면 어떻게 든 "정상 실행"(이 경우 nextStep
)의 모든 변수를 onClick-methods에서 사용할 수 있도록 만들어야하는데 , 그것은 나에게 지옥처럼 들립니다.
편집 2
또 다른 명백한 예는 "예" 및 "아니오" 옵션과 함께 "계속 하시겠습니까"를 묻는 팝업 입니다.
사용자가 "예"를 누르면 전체 메서드가 중단되어야하며 그렇지 않으면 실행이 계속됩니다. 어떻게 잘 해결합니까?
Ted, 당신은 이것을하고 싶지 않습니다. 정말 :) 가장 큰 이유는 Dialog를 표시하는 동안 UI 스레드를 차단하면 Dialog의 이벤트를 그리고 처리하는 스레드를 차단하기 때문입니다. 즉, 대화가 응답하지 않습니다. 사용자가 대화 상자를 클릭하는 데 몇 초 이상 걸리는 경우에도 ANR이 발생합니다.
Erich의 대답은 정확히 필요한 것입니다. 나는 그것이 당신이 원하는 것이 아니라는 것을 알고 있지만 그것은 중요하지 않습니다. 개발자가 동기식 대화 상자를 작성하지 못하도록 Android를 설계 했으므로 선택의 여지가 많지 않습니다.
위의 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;
}
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과 더 비슷하게 만드는 코드를 작성할 수 있지만 대화 수명주기는 .NET과 상당히 얽혀 Activity
있으므로 결국에는 가치가있는 것보다 더 문제가 될 수 있습니다. 추가 대화 참조는 여기에 있습니다 .
Android에서 대화 상자는 비동기식이므로 코드를 약간 다르게 구성해야합니다.
따라서 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입니다. 깔끔하게 보이지 않을 수도 있지만 그것이 작동하는 방식입니다.
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;
}
}
Android의 메모리 및 성능 대화 상자를 최적화하려는 시도는 비동기식입니다 (이러한 이유로 관리 됨). Windows 세계에서 왔기 때문에 모달 대화 상자에 익숙합니다. Android 대화 상자는 모달이지만 실행과 관련하여 모달이 아닙니다. 대화 상자를 표시 한 후에도 실행이 중지되지 않습니다.
내가 본 Android의 Dialogs에 대한 최고의 설명은 "Pro Android" http://www.apress.com/book/view/1430215968입니다.
이것은 완벽한 설명은 아니지만 Windows와 Android의 대화 상자 간의 차이점에 대해 두뇌를 감싸는 데 도움이 될 것입니다. Windows에서는 A를 수행하고 대화 상자로 질문 한 다음 B 또는 C를 수행합니다. Android 디자인 A에서는 대화 상자에 대한 OnClickListener (s)의 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
안타깝게도 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;
}
Ted, 아마 알게 되셨 겠지만, 안타깝게도 Android에서는 그렇게 할 수 없습니다. 대화 상자는 모달이지만 비동기 적이며 .NET (또는 Windows)에서했던 것처럼 설정하려는 순서를 확실히 방해합니다. 코드를 뒤틀어 서 예제를 기반으로 매우 쉽게 따라 할 수있는 논리를 깨야합니다.
또 다른 매우 간단한 예는 파일에 데이터를 저장하는 것입니다. 파일이 이미 있는지 확인하고 덮어 쓸지 여부를 묻습니다. 대화 상자를 표시하고 결과에 따라 작동하는 if 문 (예 / 아니오)을 갖는 대신 콜백 (Java에서 리스너라고 함)을 사용하고 논리를 여러 함수로 분할해야합니다.
Windows에서 대화 상자가 표시되면 메시지 펌프가 백그라운드에서 계속되고 (현재 처리중인 메시지 만 보류 중임) 제대로 작동합니다. 예를 들어 대화 상자를 표시하는 동안 사용자가 앱을 이동하고 다시 칠할 수 있습니다. WinMo는 동기식 모달 대화 상자를 지원하므로 BlackBerry도 지원하지만 Android는 지원하지 않습니다.
가장 깔끔하고 간단한 해결책은 사용자가 확인 버튼을 클릭 할 때 리스너가 반환 값과 함께 호출되도록 자체 리스너 인터페이스를 사용하는 것입니다. 이 방법은 화려하거나 복잡하지 않으며 Android 원칙을 존중합니다.
다음과 같이 리스너 인터페이스를 정의하십시오.
public interface EditListener
/* Used to get an integer return value from a dialog
*
*/
{
void returnValue(int value);
}
내 응용 프로그램의 경우 AlertDialog를 사용하고 정수 값을 편집하고 싶을 때마다 호출하는 EditValue 클래스를 만들었습니다. 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;}
}
);
android.app.Dialog.setOnDismissListener (OnDismissListener listener)를 사용하십시오 .
재 작성 :
모바일과 데스크톱 환경 사이에는 물론 몇 년 전과 현재까지 애플리케이션이 개발 된 방식 사이에는 근본적인 차이가 있습니다.
a) 모바일 장치는 에너지를 절약해야합니다. 그들이 제공하는 가치의 일부. 따라서 자원을 절약해야합니다. 스레드는 값 비싼 리소스입니다. 스레드의 진행을 중지하는 것은이 리소스의 허용 할 수없는 낭비입니다.
b) 요즘 사용자는 훨씬 더 많은 것을 요구합니다. 이를 지원하기 위해 우리는 CPU가 완전히 작동하고 가능한 최소한의 에너지 소비를 가질 가치가 있다고 믿습니다. 그 응용 프로그램은 장치에서 혼자가 아니며 동시에 실행되는 다른 앱이 알 수 없으며 앱이 반드시 가장 긴급한 것은 아닙니다.
c) 시스템 수준 잠금은 옵션이 아닙니다. 모바일 장치는 백그라운드에서 여러 이벤트 및 서비스와 함께 작동하며 응용 프로그램에 의해 잠길 수있는 것은 맞지 않습니다.
"시스템 잠금"이 작동하는 동안 전화를받는 사용자를 생각해보십시오.
위의 사실을 바탕으로 제안 된 질문에 대한 답변은 다음과 같습니다.
- 이 가능한 대화 상자를 구축하는 방법은 그 사용자의 응답 할 때까지 블록 메인 쓰레드?
아니요. 해결 방법은 사용자 경험이 악화되고 시스템 자체를 비난하는 실수를 할 수 있습니다. 이것은 불공평하며 플랫폼과 모든 개발자에게 불이익을줍니다.
- 대화로 전체 시스템을 차단하는 방법이 있습니까?
아니요. 이것은 플랫폼에서 엄격히 금지되어 있습니다. 어떤 응용 프로그램도 시스템이나 다른 응용 프로그램의 작동을 방해 할 수 없습니다.
- 내 애플리케이션을 리팩토링하거나 Android 모바일 시스템 아키텍처에 맞게 프로그래밍 방식을 재고해야합니다.
예. 이 측면을 포함합니다.
버튼에 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
}
})
}
호출하려면-예-showDialog (DIALOG_ERROR_PREF);
더 많은 http://developer.android.com/guide/topics/ui/dialogs.html
당신의 질문에 대답하기 위해 ... btw 내가 9 개월 늦었다 고 외쳤습니다 : D ... 이런 종류의 문제에 "해결책"이 있습니다. 즉
new AlertDialog.Builder(some_class.this).setTitle("bla").setMessage("bla bla").show();
wait();
간단히 wait ();
OnClickListener에서 다음과 같이 notify ()로 클래스를 다시 시작합니다.
@Override
public void onClick(DialogInterface dialog, int item) {
Toast.makeText(getApplicationContext(), "test", Toast.LENGTH_LONG).show();
**notify**();
dialog.cancel();
}
동일한 해결 방법은 Android에서 4 개의 토스트 및 기타 비동기 호출로 이동합니다.
저는 Android / Java 세계에 처음 왔으며 여기에서 모달 대화 상자가 작동하지 않는다는 사실을 알고 놀랐습니다 (내가 읽은 내용을 이해하지 못하는 경우). 지금은 매우 모호한 이유 때문에 태블릿에서 매우 모달 방식으로 작동하는 확인 버튼이있는이 "ShowMessage"를 받았습니다.
내 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이 제안한 인터페이스 접근 방식에 따라 모달 Yes / No 대화 상자를 구현했으며 꽤 잘 작동합니다.
보정:
내 대답은 내가 오해 한 질문과 관련이 없습니다. 어떤 이유로, 나는 M. Romain Guy를 "당신은 그렇게하고 싶지 않다"고 해석했습니다. 나는 읽었어야했다 : "당신은 그렇게하고 싶지 않다 ...이 방법".
죄송합니다.
스레드 (UI 스레드가 아님)에서 시도해보십시오.
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();
}
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);
I am using Xamarin.Android (MonoDroid), and I have requirments for developing UI Blocking confirm box. I am not going to argue with the client because I can see good reasons for why they want that (details here), so I need to implement this. I tried @Daniel and @MindSpiker above, but these did not work on MonoForAndroid, the moment the message is sent between the threads, the app is crashed. I assume it is something to do with Xamarin mapping.
I ended up creating a separate thread from the UI thread and then blocking that and waiting for the user response as follows:
// (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;
Full details on how to do this can be found in my blog post here
This is the simplest way:
new AlertDialog.Builder(this).setTitle("title").setMessage("message").create().show();
'program tip' 카테고리의 다른 글
'모건'모듈은 익스프레스 앱과 어떤 관련이 있습니까? (0) | 2020.11.08 |
---|---|
Vim에서 Ex 명령을 (재) 매핑 할 수 있습니까? (0) | 2020.11.08 |
루비의 정적 변수 (0) | 2020.11.08 |
필터를 통해 STDERR 만 파이프 (0) | 2020.11.08 |
<<의 용도는 무엇입니까? (0) | 2020.11.08 |