Android "Лучшая практика" возвращает значения из диалогового окна
каков " правильный "способ возврата значений к вызывающей активности из сложного пользовательского диалога - скажем, текстовые поля, выбор даты или времени, куча переключателей и т. д., а также кнопка" Сохранить "и" отменить"?
некоторые из методов, которые я видел в интернете, включают:
открытые члены данных в диалоговом производном классе, которые могут быть прочитаны Activity
общедоступные методы доступа "get". . . ". . ". . "
запуск диалога с намерением (в отличие от показать() ) плюс обработчики в классе Dialog, которые принимают входные данные от различных элементов управления и связывают их для передачи обратно в действие, поэтому, когда слушатель нажимает "сохранить", пакет передается обратно с помощью ReturnIntent ()
слушатели в действии, которые обрабатывают ввод от элементов управления, которые находятся в диалоговом окне, например, так что TimePicker или DatePicker слушатели действительно находятся в деятельности. В этой схеме практически вся работа выполняется в деятельности
один слушатель в действии для кнопки "Сохранить", а затем действие непосредственно опрашивает элементы управления в диалоговом окне; действие отклоняет диалоговое окно.
...плюс еще кое-что, что я уже забыл.
есть ли определенная техника, которая считается канонически правильной или " лучшей практикой" метод?
6 ответов:
Я использую следующий способ:
- все мои действия имеют одну и ту же родительскую активность (скажем, ControlActivity). ControlActivity имеет
private volatile Bundle controlBundle;С соответствующим геттером / сеттеромкогда я начинаю диалог, я использовал, чтобы вызвать диалог через мой собственный метод:
public void showMyDialog(int id, Bundle bundle) { this.controlBundle=bundle; this.showDialog(id, bundle); }поэтому каждый раз, когда я знаю параметры, отправленные в dialog
- когда диалог собирается завершить, я формирую в диалоге другой
BundleС необходимые значения, а затем положить их через мойActivityпакета сеттер:((ControlActivity )this.getOwnerActivity).setControlBundle(bundle);так что в конце концов, когда диалог заканчивается, я знаю значение "возвращено" из диалога. Я знаю, что это не так
int retCode=this.showMyDialog();это немного сложнее, но это выполнимо.
возможно, я неправильно понимаю ваш вопрос, но почему бы просто не использовать встроенную систему прослушивания:
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // run whatever code you want to run here // if you need to pass data back, just call a function in your // activity and pass it some parameters } })вот как я всегда обрабатывал данные из диалоговых окон.
EDIT: позвольте мне дать вам более конкретный пример, который лучше ответит на ваш вопрос. Я собираюсь украсть некоторые примеры кода с этой страницы, которые вы должны читайте:
http://developer.android.com/guide/topics/ui/dialogs.html
// Alert Dialog code (mostly copied from the Android docs AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Pick a color"); builder.setItems(items, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { myFunction(item); } }); AlertDialog alert = builder.create();...
// Now elsewhere in your Activity class, you would have this function private void myFunction(int result){ // Now the data has been "returned" (as pointed out, that's not // the right terminology) }
для моего MIDI-приложения мне нужны были диалоги подтверждения да / нет / отмена, поэтому я сначала сделал общий класс StandardDialog:
public class StandardDialog { import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.os.Handler; public class StandardDialog { public static final int dlgResultOk = 0; public static final int dlgResultYes = 1; public static final int dlgResultNo = 2; public static final int dlgResultCancel = 3; public static final int dlgTypeOk = 10; public static final int dlgTypeYesNo = 11; public static final int dlgTypeYesNoCancel = 12; private Handler mResponseHandler; private AlertDialog.Builder mDialogBuilder; private int mDialogId; public StandardDialog(Activity parent, Handler reponseHandler, String title, String message, int dialogType, int dialogId) { mResponseHandler = reponseHandler; mDialogId = dialogId; mDialogBuilder = new AlertDialog.Builder(parent); mDialogBuilder.setCancelable(false); mDialogBuilder.setTitle(title); mDialogBuilder.setIcon(android.R.drawable.ic_dialog_alert); mDialogBuilder.setMessage(message); switch (dialogType) { case dlgTypeOk: mDialogBuilder.setNeutralButton("Ok", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mResponseHandler.sendEmptyMessage(mDialogId + dlgResultOk); } }); break; case dlgTypeYesNo: case dlgTypeYesNoCancel: mDialogBuilder.setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mResponseHandler.sendEmptyMessage(mDialogId + dlgResultYes); } }); mDialogBuilder.setNegativeButton("No", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mResponseHandler.sendEmptyMessage(mDialogId + dlgResultNo); } }); if (dialogType == dlgTypeYesNoCancel) { mDialogBuilder.setNeutralButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mResponseHandler.sendEmptyMessage(mDialogId + dlgResultCancel); } }); } break; } mDialogBuilder.show(); } }далее в моей основной деятельности у меня уже был обработчик сообщений для обновления UI из других потоков, так что я просто добавил код для обработки сообщений от окна. Используя другой параметр dialogId при создании экземпляра StandardDialog для различных функций программы, я могу выполнить правильный код для обработки ответов yes / no / cancel для разных функций вопросы. Эта идея может быть расширена для сложных пользовательских диалогов, отправив пакет данных, хотя это намного медленнее, чем простое целочисленное сообщение.
private Handler uiMsgHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg != null) { // {Code to check for other UI messages here} // Check for dialog box responses if (msg.what == (clearDlgId + StandardDialog.dlgResultYes)) { doClearDlgYesClicked(); } else if (msg.what == (recordDlgId + StandardDialog.dlgResultYes)) { doRecordDlgYesClicked(); } else if (msg.what == (recordDlgId + StandardDialog.dlgResultNo)) { doRecordDlgNoClicked(); } } } };тогда все, что мне нужно сделать, это определить методы do{Whatever}() в действии. Чтобы вызвать диалог, в качестве примера у меня есть метод, отвечающий на кнопку" Очистить записанные MIDI-события " и подтвердите его следующим образом:
public void onClearBtnClicked(View view) { new StandardDialog(this, uiMsgHandler, getResources().getString(R.string.dlgTitleClear), getResources().getString(R.string.dlgMsgClear), StandardDialog.dlgTypeYesNo, clearDlgId); }
clearDlgIdопределяется как уникальное целое число в другом месте. Этот метод создает диалоговое окно да / нет всплывает перед действием, которое теряет фокус, пока диалоговое окно не закроется, и в это время действие получает сообщение с результатом диалога. Затем обработчик сообщений вызываетdoClearDlgYesClicked()метод, если была нажата кнопка" да". (Мне не нужно было сообщение для кнопки "нет", так как в этом случае никаких действий не требовалось).в любом случае, этот метод работает для меня, и позволяет легко передавать результаты обратно из диалога.
я размышлял над этим сам некоторое время, и в конечном итоге самый удобный способ, который я нашел для этого, - это разбить мою деятельность на различные методы, которые представляют каждую единицу потока управления. Например, если действия в моей деятельности говорят : загрузите переменные из intent, проверьте некоторые данные, обработайте и продолжите, если доступно, если нет фонового вызова, дождитесь взаимодействия с пользователем, начните другое действие.
я вообще пикап части, которые являются общими, первые два и последний в данном случае. Я заверну первые в
onCreate()и сделать отдельный для последнего... скажиstartAnotherActivity(Data). Вы можете расположить средние части так, чтобы они состояли изcheckData(Data)(возможно, слилась вonCreate()), которая вызывает либоprocessAvailableData(Data)илиperformBackgroundTask(Data). Фоновая задача выполнит операцию в фоновом режиме и вернет управление вonBackgroundTaskCompleted(OtherData).теперь
processAvailableData(Data)иonBackgroundTaskCompleted(OtherData)вызовgetUserResponse()метод, который, в свою очередь, может либо вызовstartAnotherActivity(Data)или слить его функционирует сам с собой.я чувствую, что этот подход дает ряд преимуществ.
- это помогает с вопросом возврата данных, на который указывает ваш вопрос, "двигаясь вперед" вместо возврата данных.
- это позволяет легче добавлять новые функции. Например, если бы мы хотели дать пользователю больше возможностей, мы могли бы просто вызвать соответствующий метод из
getUserResponse()что может повлиять на данные, которые в конечном итоге передаются следующему деятельность.- это помогает избежать ненужных проблем потока (проверьте вопросы, связанные с
finish()иreturnon SO), когда наше интуитивное предположение-это определенный поток, а он оказывается другим.- помогает лучше управлять переменными, поэтому у вас не будет много полей уровня класса, чтобы избежать проблем с доступом к переменным в анонимных внутренних классах (onClick (), doInBackground () и т. д.).
я уверен, что больше методов добавляет некоторые накладные расходы, но его, вероятно, компенсируются преимуществами потока, повторного использования и простоты, которые вы получаете (хотелось бы услышать мнение эксперта по компиляции об этом).
я объясню одно решение на примере двух фрагментов. Представьте, что есть
SimpleFragment, который имеет только одно текстовое поле для отображения даты. То естьDatePickerFragmentчто позволяет выбрать конкретную дату. То, что я хочу, это то, чтоDatePickerFragmentпередает значение даты обратно в вызовSimpleFragmentвсякий раз, когда пользователь подтверждает ее выбор.SimpleFragment
Итак, прежде всего мы начинаем
DatePickerFragmentвнутриSimpleFragment:private DateTime mFavoriteDate; // Joda-Time date private void launchDatePicker() { DatePickerFragment datePickerFragment = new DatePickerFragment(); Bundle extras = new Bundle(); // Pass an initial or the last value for the date picker long dateInMilliSeconds = mFavoriteDate.getMillis(); extras.putLong(BundleKeys.LAST_KNOWN_DATE, dateInMilliSeconds); datePickerFragment.setArguments(extras); datePickerFragment.setTargetFragment(this, SIMPLE_FRAGMENT_REQUEST_CODE); datePickerFragment.show(getActivity().getSupportFragmentManager(), DatePickerFragment.FRAGMENT_TAG); }DatePickerFragment
во фрагменте диалогового окна мы готовимся передать назад выбранную дату, когда пользователь нажимает положительную кнопку:
public static final String DATE_PICKED_INTENT_KEY = "DATE_PICKED_INTENT_KEY"; public static final int DATE_PICKED_RESULT_CODE = 123; @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // ... Long dateInMilliSeconds = getArguments().getLong(BundleKeys.LAST_KNOWN_DATE); DateTime date = new DateTime(dateInMilliSeconds); initializePickerUiControl(date); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity); dialogBuilder .setPositiveButton(R.string.date_picker_positive, (dialog, which) -> { // Pass date to caller passBackDate(); }) .setNegativeButton(R.string.date_picker_negative, (dialog, which) -> { // Nothing to do here }); return dialogBuilder.create(); } private void passBackDate() { DateTime dateTime = getDateTimeFromPickerControl(); Intent intent = new Intent(); intent.putExtra(DATE_PICKED_INTENT_KEY, dateTime.getMillis()); getTargetFragment().onActivityResult( getTargetRequestCode(), DATE_PICKED_RESULT_CODE, intent); }SimpleFragment
назад в запрашивающем фрагменте мы потребляем то, что было передано обратно диалогом:
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == SIMPLE_FRAGMENT_REQUEST_CODE && resultCode == DatePickerFragment.DATE_PICKED_RESULT_CODE) { long datePickedInMilliseconds = data.getLongExtra( DatePickerFragment.DATE_PICKED_INTENT_KEY, 0); mFavoriteDate = new DateTime(datePickedInMilliseconds); updateFavoriteDateTextView(); } else { super.onActivityResult(requestCode, resultCode, data); } }ссылки mattpic кто дал отличный ответ!--25--> раньше.
после совсем немного исследований я остановился на интерфейсе обратного вызова. Мой код выглядит следующим образом:
MyFragment.java
public class MyFragment extends Fragment {...
private void displayFilter() { FragmentManager fragmentManager = getFragmentManager(); FilterDialogFragment filterDialogFragment = new FilterDialogFragment(); Bundle bundle = new Bundle(); bundle.putSerializable("listener", new FilterDialogFragment.OnFilterClickListener() { @Override public void onFilterClickListener() { System.out.println("LISTENER CLICKED"); } }); filterDialogFragment.setArguments(bundle); filterDialogFragment.show(fragmentManager, DIALOG_FILTER); }MyDialog.java
public class MyDialog extends DialogFragment { private ImageButton mBtnTest; private OnFilterClickListener mOnFilterClickListener; @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); // Get the layout inflater LayoutInflater inflater = getActivity().getLayoutInflater(); View filterLayout = inflater.inflate(R.layout.filter_dialog, null); // Inflate and set the layout for the dialog // Pass null as the parent view because its going in the dialog layout builder.setView(filterLayout) .setTitle("Filter"); Dialog dialog = builder.create(); mOnFilterClickListener = (OnFilterClickListener) getArguments().getSerializable("listener"); mBtnTest = (ImageButton)filterLayout.findViewById(R.id.fandb); mBtnTest.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { mOnFilterClickListener.onFilterClickListener(); dismiss(); } }); return dialog; } public interface OnFilterClickListener extends Serializable { void onFilterClickListener(); } }
Comments