116 votes

Meilleures pratiques pour les fragments imbriqués dans Android 4.0, 4.1 (< 4.2) sans utiliser la bibliothèque de support

Je suis en train d'écrire une application pour la version 4.0 et 4.1 comprimés, pour laquelle je ne veux pas utiliser les bibliothèques de prise en charge (si nécessaire), mais le 4.x api uniquement donc.

Donc, mon objectif de la plateforme est très bien défini: >= 4.0 et <= 4.1

L'application dispose d'un multi-volet mise en page (deux fragments, l'un petit, sur la gauche, un fragment de contenu sur la droite) et une barre d'action avec des onglets.

Similaire à ceci:

enter image description here

En cliquant sur un onglet sur la barre d'action change la 'extérieur' fragment, et l'intérieur fragment est alors un fragment de imbriquées fragments (1. petite liste de gauche fragment, 2. large fragment de contenu).

Je suis maintenant vous vous demandez quelle est la meilleure pratique pour remplacer les fragments et surtout imbriquée fragments. Le ViewPager fait partie de la bibliothèque de prise en charge, il n'y a pas natif 4.x alternative pour cette classe. Semblent être "obsolète" à mon sens. - http://developer.android.com/reference/android/support/v4/view/ViewPager.html

Puis j'ai lu les notes de version pour Android 4.2, concernant l' ChildFragmentManager, ce qui serait un bon ajustement, mais je suis ciblage 4.0 et 4.1, donc cela ne peut pas être utilisé.

ChildFragmentManager n'est disponible que dans la version 4.2

Malheureusement, il n'existe pratiquement pas de bons exemples qui montrent les meilleures pratiques pour les fragments d'usages sans le soutien de la bibliothèque, même dans l'ensemble de développeur Android guides, et surtout rien de ce qui concerne imbriquée fragments.

Alors je me demande: est-il tout simplement pas possible d'écrire 4.1 applications avec imbriqué fragments sans l'aide de la bibliothèque de prise en charge et de tout ce qui vient avec elle? (besoin d'utiliser FragmentActivity au lieu de Fragment, etc.)? Ou quelle serait la meilleure pratique?


Le problème que je rencontre actuellement dans le développement est exactement cette déclaration:

Le Android Support Library supporte également imbriqués fragments, de sorte que vous peut mettre en œuvre imbriquée fragment de dessins sur Android 1.6 et supérieur.

Remarque: Vous ne pouvez pas gonfler une mise en page dans un fragment lors de la mise en comprend un <fragment>. Imbriqués les fragments ne sont pris en charge lorsqu'il est ajouté un fragment de façon dynamique.

Parce que j'ai mis définir la imbriquée des fragments XML, qui, apparemment, est la cause d'une erreur de ce type:

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_

Pour le moment, je termine pour moi-même: même sur 4.1, quand je ne veux pas même cible que le 2.x plate-forme, imbriqués les fragments, comme illustré dans la capture d'écran ne sont pas possibles sans le soutien de la bibliothèque.

(Ce pourrait être en réalité plus d'une entrée wiki n'est pas une question, mais peut-être que quelqu'un d'autre a réussi auparavant).

Mise à jour:

Une réponse utile est: Fragment Fragment à l'Intérieur

60voto

Chris.Jenkins Points 5261

Limitations

Alors nidification des fragments à l'intérieur d'un autre fragment n'est pas possible avec xml, indépendamment de la version d' FragmentManager vous utilisez.

Donc, vous devez ajouter des fragments via le code, cela peut sembler comme un problème, mais dans le long terme rend vos mises en page superflexible.

Alors nidification sans l'aide d' getChildFragmentManger? L'essence derrière l' childFragmentManager , c'est qu'il diffère de chargement jusqu'à ce que le fragment précédent de la transaction est terminée. Et bien sûr, il était seulement naturellement pris en charge dans la version 4.2, ou la bibliothèque de prise en charge.

L'imbrication sans ChildManager - Solution

La Solution, Bien Sûr! J'ai fait cela pendant un bon bout de temps (depuis l' ViewPager a été annoncé).

Voir ci-dessous; C'est un Fragment qui diffère de chargement, donc Fragments peut être chargé à l'intérieur d'elle.

Son assez simple, l' Handler est vraiment pratique de classe, le gestionnaire attend pour un espace à exécuter dans le thread principal de l'actuelle fragment de transaction a fini de s'engager en tant que fragments d'interférer avec l'INTERFACE utilisateur, ils s'exécutent sur le thread 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);
}

Je ne la considérons pas comme des "meilleures pratiques", mais j'ai des applications en utilisant ce hack, et je suis encore à avoir des problèmes avec elle.

J'utilise aussi cette méthode pour l'incorporation de vue téléavertisseurs - https://gist.github.com/chrisjenx/3405429

2voto

Alex Points 11

La meilleure façon de le faire lors de la pré-API 17 est de ne pas le faire du tout. En essayant de mettre en œuvre ce comportement va entraîner des problèmes. Cependant cela ne veut pas dire qu'il ne faut pas faire semblant de manière convaincante à l'aide de l'API actuelle 14. Ce que j'ai fait est le suivant:

1 - regardez la communication entre les fragments http://developer.android.com/training/basics/fragments/communicating.html

2 - déplacez votre mise en page xml FrameLayout de votre Fragment de l'Activité mise en page et de le cacher en donnant une hauteur 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 - mise en Œuvre de l'interface dans le Fragment parent

    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 - mettre en Œuvre l'interface de l'Activité parent

public class YourActivity s'étend de l'Activité met en œuvre 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 - Profitez, avec cette méthode, vous obtenez le même fluide fonctionnalités comme la getChildFragmentManager() la fonction de pré-API 17 envoronment. Comme vous l'avez peut-être remarqué que l'enfant fragment n'est plus vraiment un enfant de la mère fragment, mais maintenant, un enfant de l'activité, ce ne peut vraiment pas être évitée.

1voto

Kain Shin Points 11

J'ai eu à traiter ce problème précis en raison d'une combinaison de NavigationDrawer, TabHost, et ViewPager qui a eu des complications avec l'utilisation de la bibliothèque de prise en charge en raison de TabHost. Et puis, j'ai aussi eu à l'appui de min de l'API de JellyBean 4.1, donc utilisant des fragments avec getChildFragmentManager n'était pas une option.

Donc mon problème peut être distillé...

TabHost (de haut niveau)
+ ViewPager (pour juste un de plus haut niveau des onglets de fragments)
= besoin pour Imbriquée Fragments (qui JellyBean 4.1 ne le supportent pas)

Ma solution a été de créer l'illusion d'imbrication des fragments sans réellement l'imbrication des fragments. Je l'ai fait en ayant l'activité principale utilisation TabHost ET ViewPager à gérer les deux, de frère, de points de Vue dont la visibilité est géré par basculement layout_weight entre 0 et 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);

Effectivement cela m'a permis de faux "Imbriqués Fragment" de fonctionner comme un point de vue indépendant, tant que je gérés manuellement la disposition pertinente de poids.

Voici mon 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>

Notez que "@+id/pager" et "@+id/conteneur" sont des frères et sœurs avec " android:layout_weight="0.5" " et " android:layout_height="0dp"'. C'est ainsi que je peux le voir dans l'aperçu de la taille de l'écran. Leur poids va être manipulés dans le code lors de l'exécution, de toute façon.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X