Intereting Posts
Ошибка: невозможно получить заблокированный буффер, очень вероятный клиент пытается заблокировать больше, чем maxImages buffers Определение частоты / высоты для чайников Как удалить скрытый символ нежелательной почты после кодировки php json Как создать руководство пользователя в приложении Android? Невозможно создать экземпляр активности ComponentInfo. Не нашел класс Параметры glGenTexture? Как использовать android ndk для доступа к камере Добавить несколько контактов в пакетном режиме Как получить доступ к новому API Gmail из моего приложения для Android? Android AlarmManager иногда опаздывает Список слоев xml для Android не отображается корректно на некоторых устройствах Невозможно создать экземпляр задачи async с помощью `cls.newInstance ()`? Как преобразовать String в String и наоборот в Android Файлы проекта Android Studio не отображаются Частота выборки сенсоров телефонов samsung android

Альтернативы startActivityForResult

Изменить: изменил фрагмент на частичный, я не знал об объекте Fragment, когда писал это.

У меня есть часть, которая содержит кнопку, чтобы открыть список контактов. Для этого необходимо позвонить

startActivityForResult( new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI), MY_REQUEST_CODE ); 

И обрабатывая результат в моей Деятельности, что-то вроде:

 public void onActivityResult( int requestCode, int resultCode, Intent data ) { if (resultCode == RESULT_OK) { switch (requestCode) { case MY_REQUEST_CODE: { Address address = contact_address( data ); if (address != null) { // do something with address } } break; } } } 

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

Я хотел бы избежать распространения идентификатора MY_REQUEST_CODE до конца частичного действия, вызывающего действие, или любого его изменения, например присвоение кнопки onClickListener кнопке. Я не хочу, чтобы пользовательский интерфейс верхнего уровня заботился о том, как частичный Построена вообще.

Существует ли стандартный способ достижения этого? Мне кажется, что если onActivityResult может быть принято, чтобы принять Uri вместо int-кодов, можно было бы избежать распространения. Надеюсь, что я пропустил что-то очевидное здесь …

У меня есть фрагмент, содержащий кнопку, чтобы открыть список контактов. Для этого требуется вызвать [ startActivityForResult() ]

Самый простой ответ – вызывать startActivityForResult() на Fragment вместо Activity . Результат должен быть возвращен к собственному onActivityResult() фрагмента.

При этом ваш подход, по-видимому, является почти полной инверсией по сравнению с тем, что я рекомендую. Фрагменты не должны начинаться. Фрагмент не должен знать и не заботиться о том, как обрабатывается конкретное событие пользовательского интерфейса (например, нажатие кнопки) для вещей, которые находятся вне самого фрагмента. В этом случае деятельность должна отвечать за получение контакта, при этом фрагмент просто говорит о деятельности «эй, эта кнопка нажата, да, и дайте мне обратную связь». Мало того, что это важно для разделения проблем, но это важно для тестирования, поэтому вы можете протестировать это поведение с издевательством контактов и т. Д.

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

Вы не можете вставлять фрагменты в фрагменты в Android. Если вы попытаетесь, вы получите недостоверные результаты .

Я не хочу, чтобы пользовательский интерфейс верхнего уровня заботился о том, как вообще создается фрагмент.

Действия абсолютно должны знать, как создается фрагмент, поскольку деятельность является той, которая выполняет построение.

С манжетой, вот как я подхожу к ней:

  1. Установите интерфейс прослушивателя для событий пользовательского интерфейса, созданных фрагментом, который выходит за границы самого фрагмента. Для целей этого ответа я буду называть это OnFooEventListener .

  2. Когда действие создает / настраивает фрагмент, эта операция предоставляет экземпляр OnFooEventListener для фрагмента. Это может быть сама деятельность, если активность реализует интерфейс.

  3. Установите интерфейс прослушивателя для асинхронного события pick-a-contact, которое фрагмент хотел бы, чтобы кто-то выполнял от его имени. Для целей этого ответа я буду называть это OnContactPickedListener . У него будет такой метод, как onContactPicked() содержащий Uri выбранного контакта.

  4. В OnFooEventListener есть что-то вроде requestContact() который будет вызываться, когда пользователь нажимает кнопку. requestContact() будет использовать экземпляр OnContactPickedListener в качестве параметра.

  5. Эта операция будет выполнять startActivityForResult() а в onActivityResult() вызовет связанный onContactPicked() в OnContactPickedListener . Активность будет кэшировать их в HashMap или что-то в то время, когда запрос был в процессе.

Теперь у нас есть четкое разделение между активностью и фрагментом. Фрагмент по-прежнему может быть размещен любым количеством действий (например, один для большого экрана, другой для обычного экрана). Контакт может поставляться либо с помощью средств производства ( ACTION_PICK ), либо что-то еще для тестирования (например, значение, установленное как часть тестового примера). Активность может обрабатывать любое количество таких фрагментов без проблем.

Ни один из двух методов здесь не работает. Оба относятся к проблеме, когда вызывающая активность заменяется системой (например, посредством вращения экрана).

В ответе Commonsware OnContactPickedListener, назначенный requestContact (), будет ссылаться на элемент управления, который больше не существует.

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

Однако есть решение, которое полагается на то, что контекст слушателя будет переназначаемым.

Опираясь на мое предыдущее решение, переработайте интерфейс ActivityResultListener как статический класс:

 abstract public static class ActivityResultListener { private Context context_; public ActivityResultListener( Context context ) { context_ = context; } public Context getContext() { return context_; } public void setContext( Context context ) { context_ = context; } abstract public void onResultCode( int resultCode, Intent data ); } 

Настройте внутренний класс для записи состояния для BaseActivity:

 protected static class BaseState { private final ActivityResultListener activity_result_listener_; protected BaseState( BaseActivity activity ) { activity_result_listener_ = activity.activity_result_listener_; } protected void setState( BaseActivity activity ) { activity.activity_result_listener_ = activity_result_listener_; if (activity.activity_result_listener_ != null) { activity.activity_result_listener_.setContext( activity ); } } } 

Обратите внимание, особенно на вызов setContext () в setState (). Это позволяет избежать проблем, связанных с реализацией нестатического интерфейса, т. Е. Что их ссылки исчезают при восстановлении Activity.

Сохранять состояние из BaseActivity:

 @Override public Object onRetainNonConfigurationInstance() { return new BaseState( this ); } 

Восстановить состояние из BaseActivity.onCreate ()

 Object state = getLastNonConfigurationInstance(); if (state instanceof BaseState) { ((BaseState)state).setState( this ); } 

В реализации ActivityResultListener обязательно используйте getContext () и findViewById () для разыменования всего по требованию, а не для хранения ссылок:

 private static class ContactChoiceListener extends BaseActivity.ActivityResultListener { private final int id_; public ContactChoiceListener( Context context, int id ) { super( context ); id_ = id; } @Override public void onResultCode( int resultCode, Intent data ) { if (resultCode == BaseActivity.RESULT_OK) { AddressEditor editor = (AddressEditor)((BaseActivity)getContext()).findViewById( id_ ); if (editor != null) editor.add_contact_address( data ); } } } 

Уф. И самое главное, это все устарело, потому что у фрагментов есть совершенно другой способ справиться с состоянием, используя setRetainInstance (boolean).

Я скоро буду реализовывать эту версию, опубликую здесь, если есть интерес.

Я не чувствовал, что решение Commonsware ответило на мой вопрос, потому что для каждого контейнера частичного добавления для обработчиков событий, содержащихся полностью в частичном, требуется каждый обработчик.

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

Поэтому я придумал какое-то решение, хотя признаю, что это тоже не так.

Сначала я подклассифицирую Activity и создаю небольшую структуру для связывания слушателя с помощью startActivityForResult () и onActivityResult ().

 public class BaseActivity extends Activity { // assume that we'll never start more than one activity at a time from our activity (a safe assumption?) private static final int LISTENED_REQUEST_CODE = 1000000000; public static interface ActivityResultListener { public void onResultCode( int resultCode, Intent data ); } private ActivityResultListener activity_result_listener_; public void startActivityForResult( Intent intent, ActivityResultListener listener ) { // paranoia if (activity_result_listener_ != null) { Log.e( TAG, "Activity trying to start more than one activity at a time..." ); return; } activity_result_listener_ = listener; startActivityForResult( intent, LISTENED_REQUEST_CODE ); } public void onActivityResult( int requestCode, int resultCode, Intent data ) { if (requestCode == LISTENED_REQUEST_CODE) { if (activity_result_listener_ != null) { ActivityResultListener listener = activity_result_listener_; activity_result_listener_ = null; listener.onResultCode( resultCode, data ); return; } } super.onActivityResult(requestCode, resultCode, data); } } 

Затем в частичном я вызываю свой перегруженный startActivityForResult () и реализую прослушиватель:

 public void onFinishInflate() { ImageButton contact_button = (ImageButton)findViewById(R.id.contact_button); contact_button.setOnClickListener( new OnClickListener() { @Override public void onClick(View view) { ((BaseActivity)getContext()).startActivityForResult( new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI), new BaseActivity.ActivityResultListener() { @Override public void onResultCode( int resultCode, Intent data ) { if (resultCode == BaseActivity.RESULT_OK) { add_contact_address( data ); } } }); } } ); } 

Поэтому теперь я могу использовать это частичное место без необходимости определять слушателей для каждого экземпляра.

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