Основы Android Fragments: почему? Является ли это концептуально неправильным?
У меня есть вопрос о "правильном программировании" в Android.
В настоящее время я разрабатываю приложение, используя фрагменты. Он включает в себя динамически добавленные фрагменты для Activity, фрагменты, завышенные от XML, вложенные фрагменты из XML или динамически добавленные. Скажем, немного всего.
Концепция, на которой этот вопрос фокусируется, - это процесс коммуникации, связанный с фрагментами. Итак, я прочитал документы, и я не первый раз пытаюсь использовать фрагменты.
Здравый смысл (и документы) говорят, что если Фрагмент хочет говорить или общаться с ним, мы должны использовать интерфейс.
Пример:
TestFragment
public class TestFragment extends Fragment {
private TestFragmentInterface listener;
public interface TestFragmentInterface {
void actionMethod();
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
if (getActivity() instanceof TestFragmentInterface) {
listener = (TestFragmentInterface) getActivity();
}
// sending the event
if (listener != null) listener.actionMethod();
}
}
TestActivity
public class Test implements TestFragmentInterface {
@Override
public void actionMethod() {
..
}
}
Здесь все прекрасно.
Это улучшает повторное использование, так как мой TestFragment таким образом может взаимодействовать с любым видом Activity, поскольку Activity реализует интерфейс, который я объявляю.
Другой способ: активность может взаимодействовать с фрагментом, удерживая ссылку и вызывая ее общедоступные методы. Это также предложенный способ обмена фрагментарно-фрагментацией с использованием Activity как моста.
Это классно, но иногда кажется, что использование интерфейса для этого немного "слишком много".
Вопрос A
В сценарии фрагменты, которые я прикрепляю, имеют довольно целенаправленную роль, то есть они выполняются для этого конкретного действия и не будут использоваться иначе, концептуально неправильно игнорировать реализацию интерфейса и просто делать что-то вроде
((TestActivity) getActivity().myCustomMethod();
?
Это также относится к сценарию, где (не в моем случае, но просто принимая его как "в худшем случае" ), моя деятельность должна иметь дело с широким спектром этих РАЗЛИЧНЫХ фрагментов, то есть он должен реализовывать один метод для каждого фрагмента он должен справиться. Это приводит код к большому беспорядку "потенциально не необходимых строк".
Двигаемся дальше: все еще с использованием "сфокусированных" фрагментов, направленных на то, чтобы работать только определенным образом, что такое использование вложенных фрагментов?
Добавлено их как
public class TestFragment extends Fragment {
private void myTestMethod() {
NestedFragment nested = new NestedFragment();
getChildFragmentManager()
.beginTransaction()
.add(R.id.container, nested)
.commit();
}
}
это связывает NestedFragment с TestFragment. Я говорю это снова, NestedFragment, как и TestFragment, должен использоваться только таким образом, у него нет смысла работать иначе.
Вернемся к вопросу, как я должен себя вести в этой ситуации?
Вопрос B
1) должен ли я предоставить интерфейс в NestedFragment и сделать так, чтобы TestFragments реализовывал NestedFragmentInterface? В этом случае я буду действовать следующим образом
NestedFragment
public class NestedFragment extends Fragment {
private NestedFragmentInterface listener;
public interface NestedFragmentInterface {
void actionMethodNested();
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
if (getParentFragment() instanceof NestedFragmentInterface) {
listener = (NestedFragmentInterface) getParentFragment();
}
// sending the event
if (listener != null) listener.actionMethodNested();
}
}
2) должен (или мог) игнорировать интерфейс и просто вызывать
getParentFragment().publicParentMethod();
?
3) должен ли я создать интерфейс в NestedFragment, но пусть активность реализует его, чтобы активность вызывала TestFragment?
Вопрос C
Что касается идеи использования Activity как моста между фрагментами, я считаю, что это сделано для правильного управления жизненным циклом всех этих объектов. Сохраняется ли возможность делать прямой фрагмент-фрагмент (используя интерфейс или напрямую вызывать общедоступные методы), пытаясь вручную обработать исключение, которое система может выбросить меня?
Ответы
Ответ 1
Я попытаюсь все это немного очистить.
Прежде всего, рассмотрите подход к настройке прослушивателя для фрагмента. Нехорошо устанавливать слушателя в методе onViewCreated, потому что он лидирует с избыточным слушателем реселлера, создавая любой фрагмент. Достаточно установить его в метод onAttach.
Я рассказал о кодовых строках. Заставьте меня заметить, хорошо, что BaseFragment реализовал обычное поведение в вашем приложении, так как FragmentListener создавал представление из ресурса.
И более того, чтобы уменьшить коды кода и получить часть повторного использования кода, вы можете использовать generic в BaseFragment. Итак, посмотрите следующий фрагмент кода:
public abstract BaseFragment<T extends BaseFragmentListener> extends Fragment {
T mListener;
public void onAttach(Activity activity) {
super.onAttach(activity);
if (Activity instanceof T)
mListener = (T) activity;
}
abstract int getLayoutResourceId();
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View layout = inflater.inflate(getLayoutResourceId(), null);
// you can use some view injected tools here, mb ButterKnife
return layout;
}
}
Ответ A (для вопроса A):
Если у вас есть фрагмент всего лишь одного действия, вам нужно решить: "Вам действительно нужно использовать фрагмент здесь?". Но mb хорошо иметь фрагмент точно для одного действия, чтобы извлечь некоторую логику представления из активности и очистить базовую логику. Но для очистки базовой логики архитектуры для вашего приложения используется Listeners. Это облегчит жизнь другим разработчикам.
Ответ B:
Для вложенных фрагментов вам нужно решить, что им нужно, чтобы использовать точные действия или просто фрагменты и использовать его в качестве моста для другой системы. Если вы знаете, что вложенный фрагмент будет вложен все время, вам нужно объявить родительский фрагмент в качестве слушателя, иначе вы должны использовать другой подход.
Примечание:
В качестве базового подхода к общению между diff частью приложения вы можете использовать события, попробуйте также взглянуть на шину событий, например. Это дает вам общий подход к общению, и вы можете извлекать логику вызова пользовательских методов слушателей и, более того, вся логика будет находиться в обработке событий, и у вас будет одна система посредников для сотрудничества.
Ответ C:
Я частично объясняю один из подходов к сотрудничеству между фрагментами. Использование одного диспетчера событий позволяет избежать много слушателей для всех разных коммуникаций. Иногда это очень выгодно.
Или я думаю, что более целесообразно использовать Activity или другие классы в Activity, для посредника для сотрудничества Fragments, потому что во время жизненного цикла и обработки и много чего происходит в Fragments. И он фокусирует всю эту логику в одном месте и делает ваш код более понятным.
Надеюсь, мои соображения помогут вам.
Ответ 2
Я делаю все возможное, чтобы ответить на стену текста здесь:)
Вопрос A:
Фрагменты предназначены для повторного использования модулей, которые могут быть подключены и воспроизводиться с любой деятельностью. Из-за этого единственным правильным способом взаимодействия с активностью является наследование наследования интерфейса, который понимает фрагмент.
public class MapFragment extends Fragment {
private MapFragmentInterface listener;
public interface MapFragmentInterface {
//All methods to interface with an activity
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// sending the event
if (listener != null) listener.anyMethodInTheAboveInterface();
}
}
Затем реализуем реализацию интерфейса
public class MainActivity extends Activity implement MapFragmentInterface{
//All methods need to be implemented here
}
Это позволяет использовать фрагмент с любым действием до тех пор, пока активность реализует этот интерфейс. Причина, по которой вам нужен этот интерфейс, заключается в том, что фрагмент можно использовать с любым действием. Вызов метода типа
((TestActivity) getActivity().myCustomMethod();
полагается на то, что ваш фрагмент может работать только в тестовой активности и, следовательно, "ломает" правила фрагментов.
Вопрос B и C:
Предполагая, что вы выполняете правильные рекомендации для фрагментов и что они являются независимыми модулями. Тогда у вас никогда не должно быть ситуации, когда фрагменты должны знать друг о друге. В 99% случаев, когда люди думают, что им нужны фрагменты, чтобы напрямую общаться, они могут перераспределить свою проблему в ситуации, которую я дал выше, используя MVC patten или что-то подобное. Попросите действия действовать как контроллер и сообщить фрагменты, когда им нужно обновить, а затем создать отдельное хранилище данных.