Лучшие практики для вложенных фрагментов в Android 4.0, 4.1 (
Я пишу приложение для 4.0 и 4.1 таблетки, для которых Я не хочу использовать библиотеки поддержки (если не нужно) но 4.X api только поэтому.
поэтому моя целевая платформа очень хорошо определена как: > = 4.0 и
приложение имеет многопанельный макет (два фрагмента, один маленький слева, один фрагмент контента справа) и панель действий с вкладками.
примерно так:

щелчок a вкладка на панели действий изменяет "внешний" фрагмент, а внутренний фрагмент затем является фрагментом с двумя вложенными фрагментами (1. небольшой левый фрагмент списка, 2. широкий фрагмент контента).
теперь мне интересно, что лучше всего заменить фрагменты и особенно вложенные фрагменты.
ViewPager является частью библиотеки поддержки, нет родной 4.х альтернативных для данного класса. Кажется, "устарел" в моем смысле.
- http://developer.android.com/reference/android/support/v4/view/ViewPager.html
затем я прочитал заметки о выпуске для Android 4.2, в отношении ChildFragmentManager, что было бы неплохо, но я нацелен на 4.0 и 4.1, так что это не может быть использовано либо.
ChildFragmentManager доступно только в 4.2
- http://developer.android.com/about/versions/android-4.2.html#NestedFragments
- http://developer.android.com/reference/android/app/Fragment.html#getChildFragmentManager()
к сожалению, вряд ли есть хорошие примеры, которые показывают лучшие практики использования фрагментов без библиотеки поддержки, даже во всех руководствах для разработчиков Android; и особенно ничего не касается вложенные фрагменты.
поэтому мне интересно: просто невозможно написать приложения 4.1 с вложенными фрагментами без использования библиотеки поддержки и всего, что с ней связано? (нужно использовать FragmentActivity вместо Fragment и т. д.?)
Или что было бы лучшей практикой?
проблема, что я в настоящее время в развитии именно этого заявление:
библиотека поддержки Android также теперь поддерживает вложенные фрагменты, так что вы
можно реализовать вложенные проекты фрагментов на Android 1.6 и выше.
Примечание: Вы не можете надуть макет в фрагмент когда этот макет
включает в себя<fragment>. Вложенные фрагменты поддерживаются только при добавлении
к фрагменту динамично.
потому что я поставил определить вложенные фрагменты в XML, который, по-видимому, вызывает ошибку, как:
Caused by: java.lang.IllegalArgumentException: Binary XML file line #15: Duplicate id 0x7f090009, tag frgCustomerList, or parent id 0x7f090008 with another fragment for de.xyz.is.android.fragment.CustomerListFragment_
на данный момент я заключаю для себя: даже на 4.1, когда я даже не хочу нацеливаться на 2.X платформа, вложенные фрагменты, как показано на скриншоте, невозможны без поддержки библиотеки.
(на самом деле это может быть скорее запись Вики, чем вопрос, но, возможно, кто-то другой справился с этим до.)
обновление:
полезный ответ: фрагмент внутри фрагмента
5 ответов:
ограничения
таким образом, вложение фрагментов внутри другого фрагмента невозможно с помощью xml независимо от того, какая версия
FragmentManagerвы используете.таким образом, вы должны добавить фрагменты с помощью кода, это может показаться проблемой, но в долгосрочной перспективе делает ваши макеты сверхгибкими.
так вложенность без использования
getChildFragmentManger? Суть заchildFragmentManagerэто то, что он откладывает загрузку до завершения предыдущей транзакции фрагмента. И конечно это было только естественно, поддерживается в 4.2 или в библиотеке поддержки.вложенность без ChildManager-Solution
Решение, Конечно! Я делаю это уже давно, (так как
ViewPagerбыло объявлено).смотрите ниже; это
Fragmentэто откладывает загрузку, так чтоFragments можно загрузить внутри него.его довольно проста,
Handler- это действительно очень удобный класс, эффективно обработчик ждет пространство для выполнения в основном потоке после текущая транзакция фрагмента завершила фиксацию (поскольку фрагменты мешают пользовательскому интерфейсу, который они запускают в основном потоке).// Remember this is an example, you will need to modify to work with your code private final Handler handler = new Handler(); private Runnable runPager; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) return inflater.inflate(R.layout.frag_layout, container, false); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); runPager = new Runnable() { @Override public void run() { getFragmentManager().beginTransaction().addFragment(R.id.frag_container, MyFragment.newInstance()).commit(); } }; handler.post(runPager); } /** * @see android.support.v4.app.Fragment#onPause() */ @Override public void onPause() { super.onPause(); handler.removeCallbacks(runPager); }Я бы не считал это "лучшей практикой", но у меня есть живые приложения, использующие этот хак, и у меня еще не было никаких проблем с ним.
я также использую этот метод для встраивания просмотра пейджеров -https://gist.github.com/chrisjenx/3405429
лучший способ сделать это в pre-API 17-это вообще не делать этого. Попытка реализовать это поведение вызовет проблемы. Однако это не означает, что его нельзя подделать убедительно, используя текущий API 14. Я сделал следующее:
1-Посмотрите на связь между фрагментами http://developer.android.com/training/basics/fragments/communicating.html
2-переместить макет xml FrameLayout из существующего фрагмента в Макет активности и скрыть его, давая высоту 0:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent"> <FrameLayout android:id="@+id/content" android:layout_width="300dp" android:layout_height="match_parent" /> <FrameLayout android:id="@+id/lstResults" android:layout_width="300dp" android:layout_height="0dp" android:layout_below="@+id/content" tools:layout="@layout/treeview_list_content"/> <FrameLayout android:id="@+id/anomalies_fragment" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_toRightOf="@+id/content" />3 - реализовать интерфейс в Родительском фрагмент
OnListener mCallback; // Container Activity must implement this interface public interface OnListener { public void onDoSomethingToInitChildFrame(/*parameters*/); public void showResults(); public void hideResults(); } @Override public void onAttach(Activity activity) { super.onAttach(activity); // This makes sure that the container activity has implemented // the callback interface. If not, it throws an exception try { mCallback = (OnFilterAppliedListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnListener"); } } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mCallback.showResults(); } @Override public void onPause() { super.onPause(); mCallback.hideResults(); } public void onClickButton(View view) { // do click action here mCallback.onDoSomethingToInitChildFrame(/*parameters*/); }4 - реализовать интерфейс в родительской активности
публичный класс YourActivity расширяет действие реализует yourParentFragment.OnListener {
public void onDoSomethingToInitChildFrame(/*parameters*/) { FragmentTransaction ft = getFragmentManager().beginTransaction(); Fragment childFragment = getFragmentManager().findFragmentByTag("Results"); if(childFragment == null) { childFragment = new yourChildFragment(/*parameters*/); ft.add(R.id.lstResults, childFragment, "Results"); } else { ft.detach(childFragment); ((yourChildFragment)childFragment).ResetContent(/*parameters*/); ft.attach(childFragment); } ft.commit(); showResultsPane(); } public void showResults() { FragmentTransaction ft = getFragmentManager().beginTransaction(); Fragment childFragment = getFragmentManager().findFragmentByTag("Results"); if(childFragment != null) ft.attach(childFragment); ft.commit(); showResultsPane(); } public void showResultsPane() { //resize the elements to show the results pane findViewById(R.id.content).getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT; findViewById(R.id.lstResults).getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT; } public void hideResults() { //resize the elements to hide the results pane findViewById(R.id.content).getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT; findViewById(R.id.lstResults).getLayoutParams().height = 0; FragmentTransaction ft = getFragmentManager().beginTransaction(); Fragment childFragment = getFragmentManager().findFragmentByTag("Results"); if(childFragment != null) ft.detach(childFragment); ft.commit(); }}
5 - наслаждайтесь, с помощью этого метода вы получаете ту же функциональность жидкости, что и с функцией getChildFragmentManager() в a предварительно с API 17 средах.. Как вы, возможно, заметили, дочерний фрагмент больше не является ребенком родительского фрагмента, но теперь ребенок активности, этого действительно нельзя избежать.
мне пришлось иметь дело с этой точной проблемой из-за комбинации NavigationDrawer, TabHost и ViewPager, которые имели осложнения с использованием библиотеки поддержки из-за TabHost. И тогда мне также пришлось поддерживать min API JellyBean 4.1, поэтому использование вложенных фрагментов с getChildFragmentManager не было вариантом.
Так что моя проблема может быть дистиллированной, чтобы...
TabHost (for top level) + ViewPager (for just one of the top level tabbed fragments) = need for Nested Fragments (which JellyBean 4.1 won't support)мое решение состояло в том, чтобы создать иллюзию вложенных фрагментов без фактического вложения фрагментов. Я сделал это, имея основное действие использовать TabHost и ViewPager для управления двумя родственными видами, видимость которых управляется путем переключения layout_weight между 0 и 1.
//Hide the fragment used by TabHost by setting height and weight to 0 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 0); mTabHostedView.setLayoutParams(lp); //Show the fragment used by ViewPager by setting height to 0 but weight to 1 lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 1); mPagedView.setLayoutParams(lp);это эффективно позволило моему поддельному "вложенному фрагменту" работать как независимое представление, пока я вручную управлял соответствующими весами макета.
вот моя activity_main.XML-код:
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.ringofblades.stackoverflow.app.MainActivity"> <TabHost android:id="@android:id/tabhost" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:id="@android:id/tabcontent" android:background="@drawable/background_image" android:layout_width="match_parent" android:layout_weight="0.5" android:layout_height="0dp"/> <android.support.v4.view.ViewPager xmlns:tools="http://schemas.android.com/tools" android:id="@+id/pager" android:background="@drawable/background_image" android:layout_width="match_parent" android:layout_weight="0.5" android:layout_height="0dp" tools:context="com.ringofblades.stackoverflow.app.MainActivity"> <FrameLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" /> </android.support.v4.view.ViewPager> <TabWidget android:id="@android:id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> </TabHost> <fragment android:id="@+id/navigation_drawer" android:layout_width="@dimen/navigation_drawer_width" android:layout_height="match_parent" android:layout_gravity="start" android:name="com.ringofblades.stackoverflow.app.NavigationDrawerFragment" tools:layout="@layout/fragment_navigation_drawer" /> </android.support.v4.widget.DrawerLayout>обратите внимание, что "@ + id / пейджер " и "@ + id / контейнер" являются братьями и сестрами с "android: layout_weight= "0.5"' и на android:layout_height="0dp хиллингдон"'. Это так, что я могу видеть его в окне предварительного просмотра для любого размера экрана. Во всяком случае, их веса будут обрабатываться в коде во время выполнения.
опираясь на @Chris.Дженкинс ответ, это решение, которое хорошо работает для меня, для удаления фрагмента(ов) во время событий жизненного цикла (которые имеют тенденцию бросать IllegalStateExceptions). Это использует комбинацию подхода обработчика и действия.isfinishing () check (в противном случае он выдаст ошибку для "не может выполнить это действие после onSaveInstanceState).
import android.app.Activity; import android.os.Handler; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; public abstract class BaseFragment extends Fragment { private final Handler handler = new Handler(); /** * Removes the {@link Fragment} using {@link #getFragmentManager()}, wrapped in a {@link Handler} to * compensate for illegal states. * * @param fragment The {@link Fragment} to schedule for removal. */ protected void removeFragment(@Nullable final Fragment fragment) { if (fragment == null) return; final Activity activity = getActivity(); handler.post(new Runnable() { @Override public void run() { if (activity != null && !activity.isFinishing()) { getFragmentManager().beginTransaction() .remove(fragment) .commitAllowingStateLoss(); } } }); } /** * Removes each {@link Fragment} using {@link #getFragmentManager()}, wrapped in a {@link Handler} to * compensate for illegal states. * * @param fragments The {@link Fragment}s to schedule for removal. */ protected void removeFragments(final Fragment... fragments) { final FragmentManager fragmentManager = getFragmentManager(); final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); for (Fragment fragment : fragments) { if (fragment != null) { fragmentTransaction.remove(fragment); } } final Activity activity = getActivity(); handler.post(new Runnable() { @Override public void run() { if (activity != null && !activity.isFinishing()) { fragmentTransaction.commitAllowingStateLoss(); } } }); } }использование:
class MyFragment extends Fragment { @Override public void onDestroyView() { removeFragments(mFragment1, mFragment2, mFragment3); super.onDestroyView(); } }
хотя ОП может иметь особые обстоятельства, которые мешают ему использовать библиотеку поддержки, большинство людей должны использовать его. Документация Android рекомендует это, и это сделает ваше приложение доступным для самой широкой аудитории.
на мой более полный ответ здесь Я привел пример, демонстрирующий, как использовать вложенные фрагменты с библиотекой поддержки.

Comments