Puis-je utiliser un téléavertisseur avec des vues (pas avec des fragments)

131

J'utilise ViewPagerpour faire glisser entre les deux Fragments, mais puis-je utiliser ViewPagerpour passer d' Viewsune mise en page XML simple?

Voici ma page Adapterpour le ViewPager qui est utilisé pour glisser entre les fragments:

import java.util.List;

import com.app.name.fragments.TipsFragment;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.view.ViewGroup;

public class PageAdapter extends FragmentPagerAdapter {

    /**
     *
     */
    List<Fragment> fragments;
    public PageAdapter(FragmentManager fm,List<Fragment> frags) {
        super(fm);
        fragments = frags;

    }

    @Override
    public Fragment getItem(int arg0) {
        // TODO Auto-generated method stub
        return TipsFragment.newInstance(0, 0);
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return 4;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        FragmentManager manager = ((Fragment) object).getFragmentManager();
        FragmentTransaction trans = manager.beginTransaction();
        trans.remove((Fragment) object);
        trans.commit();

        super.destroyItem(container, position, object);
    }

}

Et voici mon fragment de pointe:

public class TipsFragment extends Fragment
{
    public static TipsFragment newInstance(int image,int content)
    {
        TipsFragment fragment = new TipsFragment();
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.tip_layout, null);
        return view;
    }
}

Comment puis-je modifier mon code pour qu'il fonctionne avec des vues au lieu de Fragment?

Ranjith
la source
jetez également un œil à cet exemple stackoverflow.com/a/37916222/3496570
Zar E Ahmer
Pourquoi ne pas utiliser de fragments? Qu'allons-nous réaliser ou perdre si nous utilisons ou n'utilisons pas de fragments?
Eftekhari
@Eftekhari Fragments => Cycle de vie complexe => Plus de bugs => Chaos
Harshil Pansare
1
@HarshilPansare Oui, j'ai traversé toutes ces catastrophes après avoir posé ces questions en février et je n'utiliserai plus de fragments dans mes projets. Je n'avais pas d'autre choix que de nettoyer tous les fragments du ViewPagersur onDestroyainsi sur l' onResumeactivité il n'y aura pas besoin de récupérer tous les 3 fragments qui ne peuvent plus disponibles. Je voulais juste mentionner l'un des problèmes.
Eftekhari
Vive la vie sans fragments!
Harshil Pansare

Réponses:

95

Vous devez remplacer ces deux méthodes plutôt que getItem():

@Override
public Object instantiateItem(ViewGroup collection, int position) {
    View v = layoutInflater.inflate(...);
    ...
    collection.addView(v,0);
    return v;
}

@Override
public void destroyItem(ViewGroup collection, int position, Object view) {
    collection.removeView((View) view);
}
Biraj Zalavadia
la source
5
gentil .. aidez-moi beaucoup ... j'ai étendu PageAdapter au lieu de FragmentPageAdapter ........ maintenant son travail très bien .....
ranjith
3
Pour un exemple de travail complet, consultez le code trouvé sur cette question: stackoverflow.com/q/7263291
Tiago
7
Par curiosité, pourquoi addView est nécessaire. L'adaptateur de fragment vient de lui demander de renvoyer la vue.
Amit Gupta
2
vous n'avez pas besoin de lancer du ViewPagertout car vous traitez avec l' ViewGroupinterface
Dori
2
@AmitGupta vous devez ajouter la vue au conteneur car vous ne retournez peut-être pas la vue ici. instantiateItem doit retourner un objet associé à cette vue, qui peut être un objet différent si vous le souhaitez; c'est une clé. Quoi que vous retourniez, assurez-vous simplement que votre implémentation de isViewFromObject peut correspondre aux deux clés à afficher. Cependant, l'implémentation la plus courante consiste simplement à renvoyer la vue en tant que clé. FragmentPageAdapter gère tous les éléments clé à afficher pour vous et vous demande donc simplement de créer un fragment à la place.
themightyjon
66

Utilisez cet exemple

Vous pouvez utiliser une seule mise en page XML imbriquant les vues enfants.

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <android.support.v4.view.ViewPager
            android:id="@+id/pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:id="@+id/page_one"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical" >
                        <TextView
                        android:text="PAGE ONE IN"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:textColor="#fff"
                        android:textSize="24dp"/>
            </LinearLayout>

            <LinearLayout
                android:id="@+id/page_two"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical" >
                        <TextView
                        android:text="PAGE TWO IN"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        android:textColor="#fff"
                        android:textSize="24dp"/>
            </LinearLayout>

    </android.support.v4.view.ViewPager>
</LinearLayout>

MAIS ... vous devez également gérer cela avec un adaptateur. Ici, nous renvoyons l'ID de vue trouvé sans gonfler aucune autre disposition.

class WizardPagerAdapter extends PagerAdapter {

    public Object instantiateItem(ViewGroup collection, int position) {

        int resId = 0;
        switch (position) {
        case 0:
            resId = R.id.page_one;
            break;
        case 1:
            resId = R.id.page_two;
            break;
        }
        return findViewById(resId);
    }

    @Override
    public int getCount() {
        return 2;
    }

    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
        return arg0 == arg1;
    }

    @Override public void destroyItem(ViewGroup container, int position, Object object) {
        // No super
    }
}

// Définir l'adaptateur ViewPager

WizardPagerAdapter adapter = new WizardPagerAdapter();
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(adapter);
Nicolas Betts
la source
2
@ user1672337 findViewById est accessible à partir de la classe Activity. Créez donc une classe interne (non statique) dans votre Actvitiy. Ou vous devez transmettre l'instance d'activité à l'adaptateur
ruX
7
Cela ne semble pas fonctionner si le ViewPager a plus de 2 vues enfants. Par exemple, si j'ai trois RelativeLayouts dans mon ViewPager et que j'essaye de faire glisser vers la troisième page, la troisième page apparaît comme vide. Une idée de ce qui donne?
Nathan Walters
15
@NathanWalters, j'ai eu le même problème aujourd'hui, je l'ai résolu en augmentant la propriété OffscreenPageLimit de ViewPager. Cela vaut probablement la peine de mettre à jour une réponse avec ces informations.
Mikhail
2
vous pouvez utiliser return collection.findViewById(resId);Si vous ne voulez pas passer une instance d'activité
Aksiom
3
Veuillez ajouter ce code@Override public void destroyItem(ViewGroup container, int position, Object object) {}
Volodymyr Kulyk
11

Nous avons construit une sous-classe très simple du ViewPagerque nous utilisons parfois.

/**
 * View pager used for a finite, low number of pages, where there is no need for
 * optimization.
 */
public class StaticViewPager extends ViewPager {

    /**
     * Initialize the view.
     *
     * @param context
     *            The application context.
     */
    public StaticViewPager(final Context context) {
        super(context);
    }

    /**
     * Initialize the view.
     *
     * @param context
     *            The application context.
     * @param attrs
     *            The requested attributes.
     */
    public StaticViewPager(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        // Make sure all are loaded at once
        final int childrenCount = getChildCount();
        setOffscreenPageLimit(childrenCount - 1);

        // Attach the adapter
        setAdapter(new PagerAdapter() {

            @Override
            public Object instantiateItem(final ViewGroup container, final int position) {
                return container.getChildAt(position);
            }

            @Override
            public boolean isViewFromObject(final View arg0, final Object arg1) {
                return arg0 == arg1;

            }

            @Override
            public int getCount() {
                return childrenCount;
            }

            @Override
            public void destroyItem(final View container, final int position, final Object object) {}
        });
    }

}

Cette classe n'a pas besoin d'un adaptateur car elle chargera les vues à partir de la mise en page. Afin de l'utiliser dans vos projets, utilisez-le simplement à la place du android.support.v4.view.ViewPager.

Tous les trucs sophistiqués fonctionneront toujours, mais vous n'avez pas besoin de vous soucier des adaptateurs.

Sabo
la source
1
Cela a presque fonctionné pour moi, mais j'ai dû changer onAttachedToWindow()pour onFinishInflate().
ehehhh
@Eftekhari C'est plus facile sans fragments et vous écrivez moins de code, ce qui signifie que c'est plus facile à lire et à comprendre plus tard et moins sujet aux bugs
Sabo
11

Sur la base des réponses précédentes, j'ai fait le cours suivant pour y parvenir de la manière la plus appropriée et la plus claire (j'espère):

public class MyViewPagerAdapter extends PagerAdapter {

    ArrayList<ViewGroup> views;
    LayoutInflater inflater;

    public MyViewPagerAdapter(ActionBarActivity ctx){
        inflater = LayoutInflater.from(ctx);
        //instantiate your views list
        views = new ArrayList<ViewGroup>(5);
    }

    /**
     * To be called by onStop
     * Clean the memory
     */
    public void release(){
     views.clear();
        views = null;
    }

    /**
     * Return the number of views available.
     */
    @Override
    public int getCount() {
        return 5;
    }

    /**
     * Create the page for the given position. The adapter is responsible
     * for adding the view to the container given here, although it only
     * must ensure this is done by the time it returns from
     * {@link #finishUpdate(ViewGroup)}.
     *
     * @param container The containing View in which the page will be shown.
     * @param position The page position to be instantiated.
     * @return Returns an Object representing the new page. This does not
     *         need to be a View, but can be some other container of
     *         the page.  ,container
     */
    public Object instantiateItem(ViewGroup container, int position) {
        ViewGroup currentView;
        Log.e("MyViewPagerAdapter", "instantiateItem for " + position);
        if(views.size()>position&&views.get(position) != null){
            Log.e("MyViewPagerAdapter",
                  "instantiateItem views.get(position) " +
                  views.get(position));
            currentView = views.get(position);
        }
        else{
            Log.e("MyViewPagerAdapter", "instantiateItem need to create the View");
            int rootLayout = R.layout.view_screen;
            currentView = (ViewGroup) inflater.inflate(rootLayout, container, false);

            ((TextView)currentView.findViewById(R.id.txvTitle)).setText("My Views " + position);
            ((TextView)currentView.findViewById(R.id.btnButton)).setText("Button");
            ((ImageView)currentView.findViewById(R.id.imvPicture)).setBackgroundColor(0xFF00FF00);
        }
        container.addView(currentView);
        return currentView;
    }

    /**
     * Remove a page for the given position. The adapter is responsible
     * for removing the view from its container, although it only must ensure
     * this is done by the time it returns from {@link #finishUpdate(ViewGroup)}.
     *
     * @param container The containing View from which the page will be removed.
     * @param position The page position to be removed.
     * @param object The same object that was returned by
     * {@link #instantiateItem(View, int)}.
     */
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View)object);

    }

    /**
     * Determines whether a page View is associated with a specific key object
     * as returned by {@link #instantiateItem(ViewGroup, int)}. This method is
     * required for a PagerAdapter to function properly.
     *
     * @param view   Page View to check for association with <code>object</code>
     * @param object Object to check for association with <code>view</code>
     * @return true if <code>view</code> is associated with the key object <code>object</code>
     */
    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view==((View)object);
    }
}

Et vous devez le paramétrer dans votre activité:

public class ActivityWithViewsPaged extends ActionBarActivity {

    /**
     * The page Adapter: Manage the list of views (in fact here, its fragments)
     * And send them to the ViewPager
     */
    private MyViewPagerAdapter pagerAdapter;

    /**
     * The ViewPager is a ViewGroup that manage the swipe from left
     * to right to left.
     * Like a listView with a gesture listener...
     */
    private ViewPager viewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_with_views);

        // Find the viewPager
        viewPager = (ViewPager) super.findViewById(R.id.viewpager);

        // Instantiate the PageAdapter
        pagerAdapter = new MyViewPagerAdapter(this);

        // Affectation de l'adapter au ViewPager
        viewPager.setAdapter(pagerAdapter);
        viewPager.setClipToPadding(false);
        viewPager.setPageMargin(12);

        // Add animation when the page are swiped
        // this instanciation only works with honeyComb and more
        // if you want it all version use AnimatorProxy of the nineoldAndroid lib
        //@see:http://stackoverflow.com/questions/15767729/backwards-compatible-pagetransformer
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
            viewPager.setPageTransformer(true, new PageTransformer());
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        pagerAdapter.release();
    }

Où les fichiers XML sont évidents view_screen.xml:

<xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/screen"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

 <TextView
        android:id="@+id/txvTitle"
        android:layout_width="wrap_content"
        android:layout_gravity="center"
        android:layout_height="wrap_content"
        android:layout_marginBottom="5dp"
        android:layout_marginTop="5dp"
        android:shadowColor="#FF00FF"
        android:shadowDx="10"
        android:shadowDy="10"
        android:shadowRadius="5"
        android:textSize="32dp"
        android:textStyle="italic"
        android:background="#FFFFF000"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FFFF00F0">
        <TextView
            android:id="@+id/txvLeft"
            android:layout_width="wrap_content"
            android:layout_gravity="left"
            android:layout_height="wrap_content"
            android:layout_marginBottom="5dp"
            android:layout_marginTop="5dp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"/>
        <TextView
            android:id="@+id/txvRight"
            android:layout_width="wrap_content"
            android:layout_gravity="right"
            android:layout_height="wrap_content"
            android:layout_marginBottom="5dp"
            android:layout_marginTop="5dp"/>
    </LinearLayout>
    <Button
        android:id="@+id/btnButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>
    <ImageView
        android:id="@+id/imvPicture"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"/>
</LinearLayout>

Et ActivtyMain a la disposition suivante:

<?xml version="1.0" encoding="utf-8"?>

<android.support.v4.view.ViewPager
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:paddingLeft="24dp"
    android:paddingRight="24dp"
    android:id="@+id/viewpager"
    android:background="#FF00F0F0">
</android.support.v4.view.ViewPager>

Un grand merci à Brian et Nicholas pour votre réponse, j'espère ajouter quelques informations plus claires et mettre en évidence quelques bonnes pratiques pour cette fonctionnalité.

Mathias Seguy Android2ee
la source
1
Pas très bien, vous enregistrez toutes les vues créées. Ce serait mieux s'il ne sauvegardait que les vues visibles. (similaire à convertview dans listview)
htafoya
4

Je voudrais développer la réponse @Nicholas, vous pouvez obtenir les vues par identifiant ou si elles sont ajoutées dynamiquement, obtenez simplement la vue en fonction de sa position

class WizardPagerAdapter extends PagerAdapter {

    public Object instantiateItem(View collection, int position) {

        View v = pager.getChildAt(position);

        return v;
    }

    @Override
    public int getCount() {
        return 3;
    }

    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {
        return arg0 == ((View) arg1);
    }
}
Brian
la source
4

Je voudrais ajouter ma solution ici. Étant donné que vous n'avez pas besoin de fragments d'utilisation, vous pouvez créer encore PagerAdapterqui se fixe au viewslieu de fragmentsla ViewPager.

Étendre PagerAdapterau lieu deFragmentPagerAdapter

public class CustomPagerAdapter extends PagerAdapter {

  private Context context;

  public CustomPagerAdapter(Context context) {
    super();
    this.context = context;
  }


  @Override
  public Object instantiateItem(ViewGroup collection, int position) {
    LayoutInflater inflater = LayoutInflater.from(context);
    View view = null;
    switch (position){
      case 0:
        view = MemoryView.getView(context, collection);
        break;
      case 1:
        view = NetworkView.getView(context, collection);
        break;
      case 2:
        view = CpuView.getView(context, collection);
        break;
    }

    collection.addView(view);
    return view;
  }

  @Override
  public int getCount() {
    return 3;
  }

  @Override
  public boolean isViewFromObject(View view, Object object) {
    return view==object;
  }

  @Override
  public void destroyItem(ViewGroup collection, int position, Object view) {
    collection.removeView((View) view);
  }
}

Vous devez maintenant définir trois classes qui renverront le viewsà gonfler dans le viewpager. Similaire à CpuViewvous aurez MemoryViewet des NetworkViewclasses. Chacun d'eux gonflera ses dispositions respectives.

public class CpuView {

public static View getView(Context context, ViewGroup collection) {

    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context
        .LAYOUT_INFLATER_SERVICE);
    return inflater.inflate(R.layout.debugger_cpu_layout, collection, false);
  }
}

Et enfin une mise en page qui sera gonflée dans chacune des vues

    <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#000000"
        android:text="CPU"/>
</LinearLayout>

PS: La raison pour laquelle j'ai écrit cette réponse est que toutes les solutions fournies ici semblent fonctionner correctement, mais elles gonflent les dispositions de la classe PagerAdapter elle-même. Pour les grands projets, il devient difficile de maintenir si leur code est beaucoup plus gonflé. Maintenant, dans cet exemple, toutes les vues ont des classes et des dispositions distinctes. Ainsi, le projet peut être facilement maintenu.

le passager noir
la source
OMI, les activités sont plus faciles à utiliser
Denny