Je voudrais implémenter une méthode qui affiche une boîte de dialogue, attend jusqu'à ce que la boîte de dialogue soit fermée, puis renvoie un résultat en fonction du contenu de la boîte de dialogue. Est-ce possible?
public String getUserInput()
{
//do something to show dialog
String input = //get input from dialog
return input;
}
Je suis en train d'essayer d'implémenter une interface qui a la méthode "public String getUserInput ()", où la chaîne retournée doit être récupérée via une boîte de dialogue. Cela se fait facilement en Java, semble impossible sous Android?
EDIT: Publier un exemple de code comme demandé dans le commentaire
getInput()
doit être appelé depuis un thread en arrière-plan (je l'appelle depuis un AsynchTask). getInput () affiche une boîte de dialogue et appelle en attente. Lorsque vous cliquez sur le bouton ok dans la boîte de dialogue, celle-ci définit l'entrée utilisateur dans une variable membre et appelle notify. Lorsque notify est appelé, getInput () continue et renvoie la variable membre.
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;
}
Merci pour tous les commentaires, j'ai été en mesure de résoudre ce problème en utilisant un fil de fond avec un wait () et notify (). Je reconnais que ce n’est pas la meilleure idée pour un paradigme donné, mais il était nécessaire de se conformer à une bibliothèque avec laquelle je travaille.
Est-ce possible?
Non, il n'y a pas de modèle d'interface utilisateur bloquant dans Android. Tout est asynchrone.
METTRE À JOUR
En réponse à certains de vos commentaires sur la question elle-même, vous ne pouvez pas afficher une interface utilisateur à partir d'un fil d'arrière-plan. Comme je l'ai écrit dans cette réponse, il n'y a pas de modèle d'interface utilisateur bloquant dans Android. Il suffit de placer votre code dans le gestionnaire de boutons de la boîte de dialogue à exécuter lorsque celle-ci est acceptée, comme dans ce projet exemple .
La bonne façon de procéder consiste à utiliser un modèle de programme basé sur les événements: "ne nous appelez pas, nous vous appellerons".
Dans la programmation en mode console simple, votre code a tendance à appeler des fonctions d’entrée bloquantes, qui ne reviennent que lorsque vous avez obtenu une valeur.
De nombreux environnements de programmation graphiques fonctionnent différemment - votre code n'est pas en cours d'exécution, mais il est appelé par le système d'exploitation/le gestionnaire de fenêtres lorsque quelque chose présentant un intérêt potentiel se produit. Vous faites quelque chose en réponse à cela et revenez rapidement. Si vous ne le faites pas, vous ne pouvez être informé de rien d’autre, car le système d’exploitation n’a aucun moyen de vous contacter avant votre retour. (En comparaison avec win32, c'est comme si la boucle de message était implémentée par Android et que vous écrivez uniquement le reste du code que la boucle de message appelle avec des événements - si vous ne retournez pas rapidement, la boucle de message se bloque)
En conséquence, vous devez repenser votre concept de flux de programme. Au lieu d'écrire une liste de tâches comme une simple série d'énoncés, considérez-la comme une séquence d'actions qui dépendent les unes des autres et des entrées. Rappelez-vous quelle action vous êtes actuellement dans une variable d'état. Lorsque vous êtes appelé avec un événement tel qu'une entrée utilisateur, vérifiez si cet événement signifie qu'il est désormais possible de passer à l'étape suivante et, le cas échéant, mettez à jour votre variable d'état avant de revenir rapidement au système d'exploitation afin de pouvoir recevoir la prochaine étape. un événement. Si l'événement n'était pas ce dont vous aviez besoin, revenez simplement sans mettre à jour votre état.
Si ce modèle ne fonctionne pas pour vous, vous pouvez écrire un thread d'arrière-plan de la logique du programme qui s'exécute comme une application en mode console utilisant une entrée bloquante. Mais vos fonctions d’entrée attendront un indicateur ou quelque chose pour être averti que l’entrée est disponible. Ensuite, sur votre thread d'interface utilisateur où Android transmet des événements, vous mettez à jour l'indicateur et vous le retournez rapidement. Le thread d'arrière-plan voit que l'indicateur a été modifié pour indiquer que des données ont été fournies et qu'il continue à être exécuté. (Quelque chose comme un émulateur de terminal Android prend cela à l'extrême, où le composant d'arrière-plan est en réalité un autre processus - un processus sous Linux en mode console, et il obtient son entrée en utilisant potentiellement des E/S bloquantes. insère des caractères dans le tuyau stdin et les extrait du tuyau stdout pour les afficher à l'écran.)
J'ai eu du mal à comprendre toutes les solutions proposées ci-dessus et j'ai donc trouvé la mienne.
J'emballe le code qui est supposé être exécuté une fois que la saisie de l'utilisateur est validée, comme suit:
Runnable rOpenFile = new Runnable() {
@Override
public void run() {
.... code to perform
}
}
Ensuite, juste en dessous de cela, je passe le nom de la fonction exécutable à la méthode de dialogue de l'utilisateur.
userInput("Open File", rOpenFile);
La méthode userInput est basée sur le générateur alertDialog décrit ci-dessus. Lorsque la saisie de l'utilisateur est validée, il commence l'exécution qui doit avoir lieu.
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();
}
Quelque chose comme ça ferait
/**
*
*/
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();
}
}
Le lancement est:
public void launchPreferedNameEdit() {
Intent foo = new Intent(this, TextEntryActivity.class);
foo.putExtra("value", objItem.getPreferedNickname());
this.startActivityForResult(foo, EDIT_PREFERED_NAME);
}
Vous obtenez le résultat en utilisant
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;
}
}
Vous pouvez penser en termes de machine à états où, si vous avez initialement besoin d'une première entrée utilisateur, vous pouvez définir un indicateur pour indiquer "entrée utilisateur nécessaire" ou autre. Ensuite, lors du traitement d'un événement, vous cochez cette option et, si cette option est définie, vous ouvrez une boîte de dialogue comme seule action pour l'événement et désactivez l'indicateur. Ensuite, à partir du gestionnaire d’événements de boîte de dialogue, après avoir traité les entrées de l’utilisateur, vous pouvez appeler le code normalement prévu pour le cas où une boîte de dialogue n’est pas nécessaire.
CASE: Mes données étaient prêtes à être traitées après un événement d'écoute de changement de préférence et j'avais besoin d'ajouter une chaîne interrogée par l'utilisateur. Il ne semble pas possible d'ouvrir une boîte de dialogue d'alerte lorsque le menu des options est ouvert ... j'ai donc dû attendre. J'ai jeté l'objet à moitié complet dans la prochaine activité du flux de travail et ai défini onResume () pour vérifier si son espace réservé était! Null, auquel cas j'ai ouvert la boîte de dialogue et terminé l'objet * "dans le gestionnaire de boutons de le dialogue "* .
Comme il s’agit de mon premier message, je ne peux pas voter pour la réponse correcte donnée ci-dessus, mais je veux éviter à quiconque d’exercer ce problème le temps et l’élégance des solutions moins correctes. Le dialogue est la place.