Подтвердить что ты не робот

Android "Лучшая практика" возвращает значения из диалога

Что такое "правильный" способ вернуть значения вызывающей активности из сложного пользовательского диалогового окна - например, текстовые поля, дату или время, кучу переключателей и т.д., плюс "Сохранить" и "Отменить" "?

Некоторые методы, которые я видел в Интернете, включают:

  • публичные элементы данных в классе, создаваемом диалогом, который может быть прочитан Activity

  • публичные "get" аксессоры., "..."., "

  • Запуск диалога с помощью Intent (в отличие от show()) плюс обработчики в классе Dialog, которые берут входные данные из различных элементов управления и связывают их для передачи обратно в Activity, поэтому, когда прослушиватель обращается к "Save" пакет передается с помощью ReturnIntent()

  • Слушатели в Управлении, которые обрабатывают ввод элементов управления, которые находятся в диалоговом окне, например, поэтому слушатели TimePicker или DatePicker действительно находятся в Activity. В этой схеме практически вся работа выполняется в Activity

  • Один прослушиватель в действии для кнопки "Сохранить", а затем "Активность" непосредственно допрашивает элементы управления в диалоговом окне; Действие отклоняет диалог.

... плюс еще, что я уже забыл.

Существует ли конкретный метод, который учитывал бы канонически правильный или "лучший метод"?

4b9b3361

Ответ 1

Я использую следующий путь:

  • Все мои действия имеют одну и ту же родительскую активность (скажем, ControlActivity). ControlActivity имеет private volatile Bundle controlBundle; с соответствующим устройством getter/setter
  • Когда я начинаю диалог, я использовал диалог через мой собственный метод:

    public void showMyDialog(int id, Bundle bundle)
    {
        this.controlBundle=bundle;
        this.showDialog(id, bundle);
    }
    

Поэтому каждый раз, когда я знаю параметры, отправленные в диалог

  1. Когда диалог вот-вот завершится, я создаю в диалоге еще один Bundle с необходимыми значениями, а затем поставлю их через набор Activity:

((ControlActivity )this.getOwnerActivity).setControlBundle(bundle);

Итак, в конце, когда заканчивается диалог, я знаю значение "возвращено" из диалогового окна. Я знаю, что он не похож на int retCode=this.showMyDialog(); немного сложнее, но он работоспособен.

Ответ 2

Возможно, я неправильно понимаю ваш вопрос, но почему бы просто не использовать встроенную систему слушателей:

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 not
    // the right terminology)
}

Ответ 3

Для моего 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();
    }
}

Далее, в моей основной деятельности у меня уже был обработчик сообщений для обновлений пользовательского интерфейса из других потоков, поэтому я просто добавил код для обработки сообщений из диалогов. Используя другой параметр dialogId, когда я создаю экземпляр StandardDialog для различных программных функций, я могу выполнить правильный код для обработки ответов yes/no/cancel на разные вопросы. Эта идея может быть расширена для сложных пользовательских диалогов, отправив Bundle данных, хотя это намного медленнее, чем простое целое сообщение.

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}() в действии. Чтобы вызвать диалог, в качестве примера у меня есть метод, реагирующий на кнопку "Clear записанные 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(), если нажата кнопка "Да". (Мне не понадобилось сообщение для кнопки "Нет", так как в этом случае не было никаких действий).

В любом случае, этот метод работает для меня и облегчает передачу результатов из диалогового окна.

Ответ 4

Я размышлял над этим сам на некоторое время, и в конечном итоге самый удобный способ, который я нашел, это разбить мою деятельность на различные методы, которые представляют каждую единицу управляющего потока. Например, если действия в моей деятельности говорят: загружать переменные из намерения, проверять некоторые данные, обрабатывать и продолжать, если они доступны, если не выполнять фоновый вызов, ждать взаимодействия с пользователем, начать другое действие.

Я обычно набираю части, которые являются общими, первые два и последние в этом случае. Я завершу первые в onCreate() и сделаю отдельный для последнего... say startAnotherActivity(Data). Вы можете расположить средние части, чтобы они состояли из checkData(Data) (возможно, слияния с onCreate()), который вызывает либо processAvailableData(Data), либо performBackgroundTask(Data). Фоновая задача выполнит операцию в фоновом режиме и возвращает управление на onBackgroundTaskCompleted(OtherData).

Теперь оба processAvailableData(Data) и onBackgroundTaskCompleted(OtherData) вызывают метод getUserResponse(), который, в свою очередь, может либо вызвать startAnotherActivity(Data), либо объединить свои функции с самим собой.

Я считаю, что этот подход дает ряд преимуществ.

  • Это помогает с проблемой возврата данных, на которую указывает ваш вопрос, путем "перемещения вперед" вместо возврата данных.
  • Это позволяет легко добавлять новые функции. Например, если мы хотим предоставить пользователю больше опций, мы можем просто вызвать соответствующий метод из getUserResponse(), который может повлиять на данные, которые в конечном итоге будут переданы следующему действию.
  • Это помогает избежать ненужных проблем с потоком (ознакомьтесь с вопросами, относящимися к finish() и return на SO), когда наше интуитивное предположение является определенным потоком и оказывается другим.
  • Помогает лучше управлять переменными, поэтому у вас не будет много полей уровня класса, чтобы избежать проблем с доступом к переменным в анонимных внутренних классах (onClick(), doInBackground() и т.д.).

Я уверен, что больше методов добавляет некоторые накладные расходы, но, вероятно, компенсируется преимуществами потока, повторного использования и простоты, которые вы получаете (хотелось бы услышать мнение эксперта по компиляции на этом).

Ответ 5

Объясню одно решение на примере двух фрагментов. Представьте, что существует 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, которые ранее дали отличный ответ.

Ответ 6

После небольшого исследования я остановился на интерфейсе обратного вызова. Мой код выглядит следующим образом:

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();
}

}