usar tutorial studio minsdkversion minima example como cambiar app android android-fragments android-support-library android-nested-fragment

tutorial - Práctica recomendada para fragmentos anidados en Android 4.0, 4.1(<4.2) sin utilizar la biblioteca de soporte



version app (5)

Estoy escribiendo una aplicación para tabletas 4.0 y 4.1, para la cual no quiero usar las bibliotecas de soporte (si no es necesario) pero la api 4.x solo por lo tanto.

Entonces mi plataforma de destino está muy bien definida como:> = 4.0 y <= 4.1

La aplicación tiene un diseño de paneles múltiples (dos fragmentos, uno pequeño a la izquierda, un fragmento de contenido a la derecha) y una barra de acciones con pestañas.

Similar a ésto:

Al hacer clic en una pestaña en la barra de acción se cambia el fragmento ''externo'', y el fragmento interno es un fragmento con dos fragmentos anidados (1. pequeño fragmento de la lista de la izquierda, 2. amplio fragmento de contenido).

Ahora me pregunto cuál es la mejor práctica para reemplazar fragmentos y especialmente fragmentos anidados. ViewPager es parte de la biblioteca de soporte, no hay una alternativa 4.x nativa para esta clase. Parece ser ''obsoleto'' en mi sentido. - http://developer.android.com/reference/android/support/v4/view/ViewPager.html

Luego, leí las notas de la versión para Android 4.2, en relación con ChildFragmentManager , que sería una buena ChildFragmentManager , pero me estoy centrando en 4.0 y 4.1, por lo que tampoco se puede usar.

ChildFragmentManager solo está disponible en 4.2

Lamentablemente, apenas existen ejemplos que muestren las mejores prácticas para el uso de fragmentos sin la biblioteca de soporte, incluso en las guías completas para desarrolladores de Android; y especialmente nada con respecto a los fragmentos anidados.

Entonces me pregunto: ¿simplemente no es posible escribir aplicaciones 4.1 con fragmentos anidados sin usar la biblioteca de soporte y todo lo que viene con eso? (¿Necesita usar FragmentActivity en lugar de Fragment, etc.?) ¿O cuál sería la mejor práctica?

El problema que estoy teniendo actualmente en el desarrollo es exactamente esta afirmación:

La Biblioteca de soporte de Android también ahora admite fragmentos anidados, por lo que puede implementar diseños de fragmentos anidados en Android 1.6 y versiones posteriores.

Nota: No puede inflar un diseño en un fragmento cuando ese diseño incluye un <fragment> . Los fragmentos anidados solo se admiten cuando se agregan a un fragmento de forma dinámica.

Porque puse definir los fragmentos anidados en XML, lo que aparentemente causa un error como:

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_

Por el momento, concluyo por mí mismo: incluso en 4.1, cuando ni siquiera quiero apuntarme a la plataforma 2.x, los fragmentos anidados como se muestra en la captura de pantalla no son posibles sin la biblioteca de soporte.

(Esto podría ser más una entrada wiki que una pregunta, pero tal vez alguien más lo haya manejado antes).

Actualizar:

Una respuesta útil es: Fragmento dentro del fragmento


Limitaciones

Por lo tanto, no es posible anidar fragmentos dentro de otro fragmento con xml, independientemente de la versión de FragmentManager que utilice.

Entonces, tiene que agregar fragmentos por código, esto puede parecer un problema, pero a la larga hace que sus diseños sean superflexibles.

Entonces anidando sin usar getChildFragmentManger ? La esencia detrás de childFragmentManager es que difiere la carga hasta que la transacción del fragmento anterior haya finalizado. Y, por supuesto, solo fue compatible de forma natural en 4.2 o la biblioteca de soporte.

Anidamiento sin ChildManager - Solución

Solución, seguro! He estado haciendo esto durante mucho tiempo, (desde que se anunció el ViewPager ).

Vea abajo; Este es un Fragment que aplaza la carga, por lo que los Fragment pueden cargarse dentro de él.

Es bastante simple, Handler es una clase muy útil, efectivamente el controlador espera un espacio para ejecutar en el hilo principal después de que la transacción actual del fragmento haya terminado de comprometerse (ya que los fragmentos interfieren con la UI que ejecutan en el hilo principal).

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

No lo consideraría ''mejor práctica'', pero tengo aplicaciones en vivo que usan este truco y todavía tengo problemas con él.

También uso este método para incrustar localizadores de visitas - https://gist.github.com/chrisjenx/3405429


Aunque el OP puede tener circunstancias especiales que le impidan usar la Biblioteca de soporte, la mayoría de la gente debería usarlo. La documentación de Android lo recomienda y pondrá su aplicación a disposición del público más amplio posible.

En mi respuesta más completa aquí hice un ejemplo que demuestra cómo usar fragmentos anidados con la biblioteca de soporte.


La mejor manera de hacer esto en pre-API 17 es no hacerlo en absoluto. Intentar implementar este comportamiento va a causar problemas. Sin embargo, eso no quiere decir que no se pueda falsificar convincentemente con la API actual 14. Lo que hice fue lo siguiente:

1 - mira la comunicación entre los fragmentos http://developer.android.com/training/basics/fragments/communicating.html

2 - mueva su diseño xml FrameLayout de su Fragmento existente al diseño de la Actividad y escóndalo dando una altura de 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 - Implementar la interfaz en el Fragmento padre

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 - Implementar la interfaz en la Actividad principal

clase pública YourActivity extends Activity implementa 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 - Disfruta, con este método obtienes la misma funcionalidad fluida que con la función getChildFragmentManager () en un envoronment pre-API 17. Como ya habrás notado, el fragmento hijo ya no es más un hijo del fragmento principal, pero ahora es un elemento secundario de la actividad, esto realmente no se puede evitar.


Sobre la base de la respuesta de @ Chris.Jenkins, esta es la solución que me ha funcionado bien, para eliminar fragmentos durante eventos del ciclo de vida (que tienen una tendencia a arrojar IllegalStateExceptions). Esto usa una combinación del enfoque del Manejador y una comprobación de Actividad.isAcabar () (de lo contrario arrojará un error de "No se puede realizar esta acción después de 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(); } } }); } }

Uso:

class MyFragment extends Fragment { @Override public void onDestroyView() { removeFragments(mFragment1, mFragment2, mFragment3); super.onDestroyView(); } }


Tuve que lidiar con este problema exacto debido a una combinación de NavigationDrawer, TabHost y ViewPager que tuvo complicaciones con el uso de la biblioteca de soporte debido a TabHost. Y luego también tuve que soportar la API mínima de JellyBean 4.1, por lo que el uso de fragmentos anidados con getChildFragmentManager no era una opción.

Entonces mi problema puede ser destilado para ...

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)

Mi solución fue crear la ilusión de fragmentos anidados sin anidar fragmentos. Hice esto haciendo que la actividad principal use TabHost Y ViewPager para administrar dos vistas hermanos cuya visibilidad se gestiona al alternar layout_weight entre 0 y 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);

De hecho, esto permitió que mi falso "Fragmento anidado" funcionara como una vista independiente siempre que gestionara manualmente los pesos de diseño relevantes.

Aquí está mi 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..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..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..app.NavigationDrawerFragment" tools:layout="@layout/fragment_navigation_drawer" /> </android.support.v4.widget.DrawerLayout>

Tenga en cuenta que "@ + id / pager" y "@ + id / container" son hermanos con ''android: layout_weight = "0.5"'' y ''android: layout_height = "0dp"''. Esto es para que pueda verlo en el previsualizador para cualquier tamaño de pantalla. Sus pesos se manipularán en código durante el tiempo de ejecución, de todos modos.