Kann ich View Pager mit Ansichten verwenden (nicht mit Fragmenten)?

131

Ich verwende das ViewPagerWischen zwischen Fragments, aber kann ich ViewPagerzwischen Viewseinfachen XML-Layouts wechseln ?

Dies ist meine Seite Adapterfür den ViewPager, mit der zwischen Fragmenten gewechselt wird:

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

}

Und das ist mein Tippfragment:

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

Wie kann ich meinen Code so ändern, dass er mit Ansichten anstelle von Fragment funktioniert?

Ranjith
quelle
Schauen Sie sich auch dieses Beispiel an stackoverflow.com/a/37916222/3496570
Zar E Ahmer
Warum keine Fragmente verwenden? Was werden wir erreichen oder verlieren, wenn wir Fragmente verwenden oder nicht?
Eftekhari
@Eftekhari Fragmente => Komplexer Lebenszyklus => Weitere Fehler => Chaos
Harshil Pansare
1
@HarshilPansare Ja, ich habe all diese Katastrophen durchgemacht, nachdem ich diese Fragen im Februar gestellt habe, und ich werde keine Fragmente mehr in meinen Projekten verwenden. Ich hatte keine andere Wahl, als alle Fragmente von der ViewPagerOn- onDestroyOn- onResumeAktivität zu entfernen. Es ist also nicht erforderlich, alle 3 Fragmente abzurufen, die möglicherweise nicht mehr verfügbar sind. Ich wollte nur eines der Probleme erwähnen.
Eftekhari
Prost auf das fragmentfreie Leben!
Harshil Pansare

Antworten:

95

Sie müssen diese beiden Methoden überschreiben, anstatt 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
quelle
5
nett .. hilf mir sehr ... ich habe PageAdapter anstelle von FragmentPageAdapter erweitert ........ jetzt funktioniert es gut .....
ranjith
3
Ein vollständiges Arbeitsbeispiel finden Sie im Code zu dieser Frage: stackoverflow.com/q/7263291
Tiago
7
Aus Neugier, warum addView erforderlich ist. Der soeben angeforderte Fragmentadapter gibt die Ansicht zurück.
Amit Gupta
2
Sie müssen überhaupt nicht besetzen, ViewPagerda Sie mit der ViewGroupSchnittstelle zu tun haben
Dori
2
@AmitGupta Sie müssen die Ansicht zum Container hinzufügen, da Sie die Ansicht hier möglicherweise nicht zurückgeben. instantiateItem muss ein dieser Ansicht zugeordnetes Objekt zurückgeben, das bei Bedarf ein anderes Objekt sein kann. Es ist ein Schlüssel. Was auch immer Sie zurückgeben, stellen Sie einfach sicher, dass Ihre Implementierung von isViewFromObject mit den beiden übereinstimmenden Schlüsseln übereinstimmen kann. Die häufigste Implementierung besteht jedoch darin, nur die Ansicht als Schlüssel zurückzugeben. FragmentPageAdapter verwaltet alle wichtigen Informationen für Sie und fordert Sie daher lediglich auf, stattdessen ein Fragment zu erstellen.
Themightyjon
66

Verwenden Sie dieses Beispiel

Sie können ein einzelnes XML-Layout verwenden, in dem die untergeordneten Ansichten verschachtelt sind.

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

ABER ... Sie müssen dies auch mit einem Adapter behandeln. Hier geben wir die gefundene Ansichts-ID zurück, ohne ein anderes Layout aufzublasen.

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
    }
}

// Setze den ViewPager Adapter

WizardPagerAdapter adapter = new WizardPagerAdapter();
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(adapter);
Nicholas Betts
quelle
2
@ user1672337 findViewById ist über die Aktivitätsklasse zugänglich. Machen Sie also eine interne (nicht statische) Klasse in Ihrem Actvitiy. Oder Sie müssen die Aktivitätsinstanz an den Adapter übergeben
ruX
7
Dies scheint nicht zu funktionieren, wenn der ViewPager mehr als 2 untergeordnete Ansichten hat. Wenn ich beispielsweise drei RelativeLayouts in meinem ViewPager habe und dann versuche, zur dritten Seite zu wischen, wird die dritte Seite als leer angezeigt. Irgendeine Idee was gibt?
Nathan Walters
15
@NathanWalters, ich hatte heute das gleiche Problem, habe es behoben und die OffscreenPageLimit-Eigenschaft von ViewPager erhöht. Wahrscheinlich lohnt es sich, eine Antwort mit diesen Informationen zu aktualisieren.
Mikhail
2
Sie können verwenden, return collection.findViewById(resId);wenn Sie keine Aktivitätsinstanz übergeben möchten
Aksiom
3
Bitte fügen Sie diesen Code hinzu@Override public void destroyItem(ViewGroup container, int position, Object object) {}
Volodymyr Kulyk
11

Wir haben eine sehr einfache Unterklasse von der erstellt ViewPager, die wir manchmal verwenden.

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

}

Diese Klasse benötigt keinen Adapter, da sie die Ansichten aus dem Layout lädt. Um es für Ihre Projekte zu verwenden, verwenden Sie es einfach anstelle von android.support.v4.view.ViewPager.

Das ganze schicke Zeug wird immer noch funktionieren, aber Sie müssen sich nicht um die Adapter kümmern.

Sabo
quelle
1
Diese fast für mich gearbeitet, aber ich musste sich ändern onAttachedToWindow()zu onFinishInflate().
ehehhh
@Eftekhari Es ist einfacher ohne Fragmente und Sie schreiben weniger Code, was bedeutet, dass das später leichter zu lesen und zu verstehen ist und weniger anfällig für Fehler
Sabo
11

Basierend auf den vorherigen Antworten habe ich die folgende Klasse gemacht, um dies auf die richtige und klarste Weise zu erreichen (ich hoffe):

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

Und Sie müssen es in Ihrer Aktivität festlegen:

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

Wo die XML-Dateien offensichtlich sind 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>

Und ActivtyMain hat das folgende Layout:

<?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>

Vielen Dank an Brian und Nicholas für Ihre Antwort. Ich hoffe, ich füge einige klarste Informationen hinzu und unterstreiche einige bewährte Methoden für diese Funktion.

Mathias Seguy Android2ee
quelle
1
Nicht sehr gut, Sie speichern alle erstellten Ansichten. Es wäre besser, wenn nur die sichtbaren Ansichten gespeichert würden. (ähnlich wie Convertview in Listview)
Htafoya
4

Ich möchte die Antwort von @Nicholas näher erläutern. Sie können die Ansichten anhand der ID abrufen oder, wenn sie dynamisch hinzugefügt werden, die Ansicht direkt anhand ihrer Position abrufen

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
quelle
4

Ich möchte hier meine Lösung hinzufügen. Vorausgesetzt , dass Sie zu verwenden Fragmente nicht brauchen, können Sie immer noch ein erstellen , PagerAdapterdie legt viewsstatt fragmentsauf die ViewPager.

Erweitern PagerAdapterstattFragmentPagerAdapter

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

Nun müssen Sie drei Klassen definieren , die das wird wieder viewsin die aufgeblasen werden viewpager. Ähnlich wie CpuViewSie haben MemoryViewund NetworkViewKlassen. Jeder von ihnen wird seine jeweiligen Layouts aufblasen.

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

Und schließlich ein Layout, das in jeder Ansicht aufgeblasen wird

    <?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: Der Grund, warum ich diese Antwort geschrieben habe, ist, dass alle hier bereitgestellten Lösungen gut zu funktionieren scheinen, aber die Layouts in der PagerAdapter-Klasse selbst aufblasen. Bei großen Projekten ist es schwierig zu warten, wenn viel Code in Bezug auf die aufgeblasenen Layouts vorhanden ist. In diesem Beispiel haben alle Ansichten separate Klassen und separate Layouts. So kann das Projekt einfach gewartet werden.

der Passagier
quelle
IMO, Aktivitäten sind einfacher zu bedienen
Denny