Android:等待来自对话框的用户输入?

Posted

技术标签:

【中文标题】Android:等待来自对话框的用户输入?【英文标题】:Android: wait on user input from dialog? 【发布时间】:2010-12-07 20:33:55 【问题描述】:

我想实现一个显示对话框的方法,等待对话框关闭,然后根据对话框内容返回结果。这可能吗?

public String getUserInput()

    //do something to show dialog
    String input = //get input from dialog
    return input;

我实际上是在尝试实现一个具有方法“public String getUserInput()”的接口,其中返回的字符串必须通过对话框检索。这在java中很容易完成,在android中似乎不可能?

编辑:根据评论中的要求发布一些示例代码

getInput() 必须从后台线程调用(我从 AsynchTask 调用它)。 getInput() 显示一个对话框并调用等待。当在对话框上按下 ok 按钮时,对话框将用户输入设置在成员变量中并调用 notify。当调用 notify 时,getInput() 继续并返回成员变量。

String m_Input;

public synchronized String getInput()

    runOnUiThread(new Runnable() 
    
        @Override
        public void run() 
        
            AlertDialog.Builder alert = new AlertDialog.Builder(context);
            //customize alert dialog to allow desired input
            alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() 
            public void onClick(DialogInterface dialog, int whichButton)
            
                          m_Input = alert.getCustomInput();
                          notify();
            
        );
        alert.show();   
        
    );

    try 
    
         wait();
     
    catch (InterruptedException e) 
    
    

    return m_Input;

【问题讨论】:

您不能在 UI 线程上调用的方法中等待用户。因此,您必须切换到程序流的事件驱动模型,或者从后台线程调用它,该线程可以等到在 UI 线程上调用 onWhatever() 方法以响应用户操作更新,无论您的后台方法正在等待什么。 我不太明白,你能解释一下我如何从后台线程调用它,然后在 onWhatever() 上返回我的字符串吗? 因为如果您将 UI 线程阻塞超过 3(?),我认为您会在三秒钟后收到一个 ANR 对话框弹出窗口,询问您是否要关闭应用程序。永远不要在 Android 的 UI 线程上运行阻塞的东西。 好的,那么,如果我能确保 getUserInput() 被 UI 线程调用,那么我就可以显示对话框并等待响应? 您可以使用它来显示对话框并阻止后台线程:github.com/jrummyapps/blocking-dialog 【参考方案1】:

这可能吗?

没有。 Android 中没有阻塞的 UI 模型。一切都是异步的。

更新

针对问题本身的一些 cmets,您无法从后台线程显示 UI。正如我在这个答案中所写,Android 中没有阻塞 UI 模型。只需将您的代码放入对话框的按钮处理程序中,以便在对话框被接受时执行,例如在this sample project 中。

【讨论】:

也许这是某人不想听到的答案?我已经发生过几次了。 您不能直接从后台线程显示 UI,但您可以从后台线程请求 UI 线程为您显示它。 @Chris Stratton:非常正确。不过,为了假装拥有一个阻塞的 UI 模型而分叉一个线程似乎有点受折磨。 我不会推荐它,除非事件驱动模型实在是太令人头疼了,或者除非处理大量遗留代码,尤其是最小化移植到构建的遗留代码作为一个 jni 库。 @Chris Stratton:啊……我可能会购买的 JNI 角度,尽管我似乎记得 Swing 也没有阻塞对话框。但是,我的记忆很模糊,我不知道 SWT 是如何处理它的。【参考方案2】:

正确的做法是事件驱动的程序模型,即“不要打电话给我们,我们会打电话给你”。

在简单的控制台模式编程中,您的代码往往会调用阻塞输入函数,这些函数在您获得值之前不会返回。

许多 gui 编程环境的工作方式不同 - 您的代码不会正常运行,而是在发生潜在感兴趣的事情时由操作系统/窗口管理器调用。您对此采取了一些措施并立即返回 - 如果您不这样做,您将无法收到任何其他通知,因为操作系统在您返回之前无法联系您。 (与win32相比,消息循环好像是Android实现的,你只需要编写消息循环调用事件的其余代码——如果你不及时返回,消息循环就会挂起)

因此,您需要重新思考程序流程的概念。与其将待办事项列表写成一系列简单的语句,不如将其视为一系列相互依赖和依赖输入的动作。记住您当前在状态变量中执行的操作。当您被诸如用户输入之类的事件调用时,查看该事件是否意味着现在可以继续下一步,如果是,请在迅速返回操作系统之前更新您的状态变量以便能够接收下一个事件。如果事件不是您需要的,则直接返回而不更新您的状态。

如果此模型不适合您,您可以编写一个程序逻辑后台线程,该线程使用阻塞输入像控制台模式应用程序一样运行。但是您的输入函数实际上只会等待一个标志或某些东西来通知输入可用。然后在 Android 传递事件的 UI 线程上,更新标志并立即返回。后台线程看到标志已更改以指示已提供数据,并继续执行。 (类似于 android 终端模拟器之类的东西将这一点发挥到了极致,其中后台组件实际上是另一个进程 - 控制台模式 linux 进程,它使用来自管道的潜在阻塞 I/O 获取其输入。java 组件接受 android UI 事件和将字符填充到标准输入管道中并将它们从标准输出管道中拉出以显示在屏幕上。)

【讨论】:

【参考方案3】:

感谢所有反馈,我能够使用后台线程以及 wait() 和 notify() 来解决这个问题。我承认这不是给定范例的最佳想法,但有必要符合我正在使用的库。

【讨论】:

体验了nodejs和python之后,不得不说android不复杂……复杂【参考方案4】:

到目前为止,我很难理解上面提供的所有解决方案,所以我找到了自己的解决方案。

我将应该在用户输入确定后执行的代码包装在一个可运行文件中,如下所示:

    Runnable rOpenFile = new Runnable() 
        @Override
        public void run() 
            .... code to perform
        
    

然后在下面,我将可运行函数的名称传递给用户对话框方法。

    userInput("Open File", rOpenFile);

userInput 方法基于上述的 alertDialog 构建器。当用户输入确定时,它会启动预期的可运行文件。

private void userInput(String sTitle, final Runnable func) 
    AlertDialog.Builder aBuilder = new AlertDialog.Builder(this);

    aBuilder.setTitle(sTitle);
    final EditText input = new EditText(this);
    input.setInputType(InputType.TYPE_CLASS_TEXT);
    aBuilder.setView(input);

    bDialogDone = false;

    aBuilder.setPositiveButton("Ok", new DialogInterface.OnClickListener() 
        @Override
        public void onClick(DialogInterface dialog, int which) 
            final String sText = input.getText().toString();
            sEingabe = sText;
            func.run();
        
    );
    aBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() 
        @Override
        public void onClick(DialogInterface dialog, int which) 
            dialog.cancel();
            sEingabe = "";
        
    );

    aBuilder.show();

【讨论】:

【参考方案5】:

这样就可以了

/**
 *
 */

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;

/**
 * @author 
 */
public class TextEntryActivity extends Activity 
    private EditText et;

    /*
     * (non-Javadoc)
     * @see android.app.Activity#onCreate(android.os.Bundle)
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_text_entry);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
                WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
        // title
        try 
            String s = getIntent().getExtras().getString("title");
            if (s.length() > 0) 
                this.setTitle(s);
            
         catch (Exception e) 
        
        // value

        try 
            et = ((EditText) findViewById(R.id.txtValue));
            et.setText(getIntent().getExtras().getString("value"));
         catch (Exception e) 
        
        // button
        ((Button) findViewById(R.id.btnDone)).setOnClickListener(new OnClickListener() 

            @Override
            public void onClick(View v) 
                executeDone();
            
        );
    

    /* (non-Javadoc)
     * @see android.app.Activity#onBackPressed()
     */
    @Override
    public void onBackPressed() 
        executeDone();
        super.onBackPressed();
    

    /**
     *
     */
    private void executeDone() 
        Intent resultIntent = new Intent();
        resultIntent.putExtra("value", TextEntryActivity.this.et.getText().toString());
        setResult(Activity.RESULT_OK, resultIntent);
        finish();
    



发射是:

public void launchPreferedNameEdit() 
    Intent foo = new Intent(this, TextEntryActivity.class);
    foo.putExtra("value", objItem.getPreferedNickname());
    this.startActivityForResult(foo, EDIT_PREFERED_NAME);

你通过使用得到结果

protected void onActivityResult(int requestCode, int resultCode, Intent data) 
    switch (requestCode) 
        case EDIT_PREFERED_NAME:
            try 
                String value = data.getStringExtra("value");
                if (value != null && value.length() > 0) 

                
             catch (Exception e) 
            
            break;
        default:
            break;
    

【讨论】:

我可能错了,但我认为这对于我所描述的问题根本不起作用?我已经编辑了我的问题以澄清问题。 如您所述,这是不可能的。但是我的帖子提供了如何使用具有输入文本的活动来执行此操作的方法。【参考方案6】:

案例:在偏好更改侦听器事件之后,我的数据已准备好处理,我需要添加一个从用户查询的字符串。选项菜单打开时似乎无法弹出警报对话框......所以我不得不等待。我将半完整对象放入工作流中的下一个活动并设置其 onResume() 以检查其占位符是否为 !null 在这种情况下,我弹出对话框并在按钮中完成对象 *"对话框的处理程序"*

由于这是我的第一篇文章,因此我无法投票支持上面给出的正确答案,但希望避免其他任何人陷入这种时间和不太正确的解决方案的不优雅。对话框就是位置。

【讨论】:

【参考方案7】:

您可以从状态机的角度来考虑,如果您最初需要用户首次输入,您可以设置一个标志来标记“需要用户输入”或其他任何内容。然后在处理事件时检查该标志,如果设置,则启动一个对话框作为事件的唯一操作并取消设置标志。然后在处理用户输入后从对话框事件处理程序中调用通常用于不需要对话框的情况的代码。

【讨论】:

【参考方案8】:

我只是做了一个对话框供你参考。

它会显示并等待然后关闭

并且我部署了Java等待和通知来实现,这个函数可以直接复制运行。

private final Object lock = new Lock();
private static final class Lock 
private void showWaitDialog(final String message, final int time_to_wait)  //ms
    if(this.isFinishing()) return;
    final String TTAG = "[showWaitDialog]";
    Log.d(TTAG, "dialog going to show");
    final ProgressDialog waitProgress = ProgressDialog.show(this, "WARNING", message, true);
    waitProgress.setCancelable(false);
    waitProgress.setOnShowListener(new DialogInterface.OnShowListener()  //callback got the asynchronous
        @Override
        public void onShow(DialogInterface dialog) 
            Log.d(TTAG, "dialog showed");
            synchronized (lock) 
                try 
                    Log.d(TTAG, "main thread going to wait");
                    lock.wait();
                 catch (InterruptedException e) 
                    Log.e(TTAG, e.toString());
                    Log.e(TTAG, "main thread going ahead");
                
            
        
    );
    new Thread(new Runnable() 
        @Override
        public void run() 
            synchronized (lock) 
                try 
                    Thread.sleep(time_to_wait);
                 catch (Exception e) 
                    Log.d(TTAG, e.toString());
                
                lock.notifyAll();
                Log.d(TTAG, "dialog notified");
                waitProgress.dismiss();
            
        
    ).start();

【讨论】:

以上是关于Android:等待来自对话框的用户输入?的主要内容,如果未能解决你的问题,请参考以下文章

等待来自另一个类的 JApplet 中的用户输入

OpenCV - imshow()在等待来自命令窗口的输入时没有响应

PHP等待来自命令行的输入[重复]

android之EditText输入错误时该怎样提示用户

如何将 QInputDialog 设置为模态

Selenium 等待用户点击 python 中的警告对话框