¿Puedo usar el buscapersonas con vistas (no con fragmentos)

131

Estoy usando ViewPagerpara deslizar entre Fragments, pero ¿puedo usar ViewPagerpara deslizar entre Viewsun diseño XML simple?

Esta es mi página Adapterpara ViewPager que se usa para deslizar entre Fragmentos:

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

}

Y este es mi fragmento de punta:

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

¿Cómo puedo modificar mi código para que funcione con Vistas en lugar de Fragmento?

ranjith
fuente
también eche un vistazo a este ejemplo stackoverflow.com/a/37916222/3496570
Zar E Ahmer
¿Por qué no usar fragmentos? ¿Qué lograremos o perderemos si usamos o no fragmentos?
Eftekhari
@Eftekhari Fragments => Complex LifeCycle => More Bugs => Chaos
Harshil Pansare
1
@HarshilPansare Sí, pasé por todos estos desastres después de hacer estas preguntas en febrero y ya no usaré fragmentos en mis proyectos. No tuve más remedio que limpiar todos los fragmentos de la actividad activada, ViewPagerpor lo onDestroytanto onResume, no habrá necesidad de recuperar los 3 fragmentos que ya no están disponibles. Solo quería mencionar uno de los problemas.
Eftekhari
¡Salud a la vida sin fragmentos!
Harshil Pansare

Respuestas:

95

Debe anular estos dos métodos en lugar de 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
fuente
55
bonita .. me ayudan mucho ... me ampliado PageAdapter en lugar de FragmentPageAdapter ........ ahora su excelente trabajo .....
ranjith
3
Para ver un ejemplo de trabajo completo, consulte el código que se encuentra en esta pregunta: stackoverflow.com/q/7263291
Tiago
77
Por curiosidad, por qué se requiere addView. El adaptador de fragmentos acaba de pedir que devuelva la vista.
Amit Gupta
2
no necesita emitir ViewPagernada ya que se trata de la ViewGroupinterfaz
Dori
2
@AmitGupta, debe agregar la vista al contenedor porque es posible que no esté devolviendo la vista aquí. instantiateItem debe devolver un objeto asociado con esa vista, que puede ser un objeto diferente si lo desea; Es una llave. Lo que devuelva, solo asegúrese de que su implementación de isViewFromObject pueda coincidir con los dos, clave para ver. Sin embargo, la implementación más común es simplemente devolver la vista como la clave también. FragmentPageAdapter maneja todos los elementos clave para ver por usted y, por lo tanto, solo le pide que cree un fragmento.
themightyjon
66

Usa este ejemplo

Puede usar un diseño XML único que anida las vistas secundarias.

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

PERO ... necesita manejar esto con un adaptador también. Aquí devolvemos el ID de la vista encontrada sin inflar ningún otro diseño.

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

// Establecer el adaptador ViewPager

WizardPagerAdapter adapter = new WizardPagerAdapter();
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(adapter);
Nicholas Betts
fuente
2
@ user1672337 findViewById es accesible desde la clase Activity. Entonces, haga una clase interna (no estática) en su Actvitiy. O debe pasar la instancia de actividad al adaptador
ruX
77
Esto no parece funcionar si ViewPager tiene más de 2 vistas secundarias. Por ejemplo, si tengo tres RelativeLayouts en mi ViewPager y luego trato de pasar a la tercera página, la tercera página se muestra en blanco. ¿Alguna idea de lo que da?
Nathan Walters
15
@NathanWalters, tuve el mismo problema hoy, lo resolví aumentando la propiedad OffscreenPageLimit de ViewPager. Probablemente valga la pena actualizar una respuesta con esta información.
Mikhail
2
puede usar return collection.findViewById(resId);Si no desea pasar una instancia de actividad
Aksiom
3
Agregue este código@Override public void destroyItem(ViewGroup container, int position, Object object) {}
Volodymyr Kulyk
11

Hemos construido una subclase muy simple de la ViewPagerque usamos a veces.

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

}

Esta clase no necesita un adaptador, ya que cargará las vistas desde el diseño. Para usarlo en tus proyectos, solo úsalo en lugar de android.support.v4.view.ViewPager.

Todas las cosas elegantes seguirán funcionando, pero no necesita preocuparse por los adaptadores.

Sabo
fuente
1
Esto casi trabajado para mí, pero tuve que cambiar onAttachedToWindow()a onFinishInflate().
ehehhh
@Eftekhari Es más fácil sin fragmentos y escribe menos código, lo que significa que es más fácil de leer y comprender más tarde y menos propenso a errores
Sabo
11

Basado en las respuestas anteriores, hice la siguiente clase para lograr eso de una manera adecuada y más clara (espero):

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

Y tienes que configurarlo en tu actividad:

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

Donde los archivos XML son obvios 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>

Y ActivtyMain tiene el siguiente diseño:

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

Muchas gracias a Brian y Nicholas por su respuesta, espero agregar información más clara y resaltar algunas buenas prácticas para esta función.

Mathias Seguy Android2ee
fuente
1
No muy bien, está guardando todas las vistas creadas. Sería mejor si solo guardara las vistas visibles. (similar a convertview en listview)
htafoya
4

Me gustaría dar más detalles sobre la respuesta de @Nicholas, puede obtener las vistas por id o si se agregan dinámicamente solo obtenga la vista directamente dada su posición

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

Me gustaría agregar mi solución aquí. Dado que no necesita usar fragmentos, aún puede crear uno PagerAdapterque se adjunte en viewslugar de fragmentsal ViewPager.

Extender en PagerAdapterlugar 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);
  }
}

Ahora debe definir tres clases que devolverán el valor viewsinflado en viewpager. Similar a lo CpuViewque tendrá MemoryViewy NetworkViewclases. Cada uno de ellos inflará sus respectivos diseños.

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

Y finalmente un diseño que se inflará en cada una de las vistas.

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

PD: La razón por la que escribí esta respuesta es porque todas las soluciones proporcionadas aquí parecen estar funcionando bien, pero están inflando los diseños en la clase PagerAdapter. Para proyectos grandes, se hace difícil mantener si hay mucho código relacionado con los diseños inflados. Ahora, en este ejemplo, todas las vistas tienen clases separadas y diseños separados. Por lo tanto, el proyecto se puede mantener fácilmente.

thedarkpassenger
fuente
OMI, las actividades son más fáciles de usar
Denny