Чи можу я використовувати пейджер перегляду з видами (не з фрагментами)


131

Я використовую ViewPagerдля переходу між ними Fragments, але чи можу я ViewPagerпереходити між Viewsпростим макетом XML?

Це моя сторінка Adapterдля ViewPager, яка використовується для переходу між фрагментами:

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

}

І ось мій фрагмент підказки:

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

Як я можу змінити код, щоб він працював із Views, а не з Fragment?


Крім того , подивіться на цьому прикладі stackoverflow.com/a/37916222/3496570
Зар E Ahmer

Чому б не використовувати фрагменти? Чого ми досягнемо чи втратимо, якщо будемо використовувати чи не використовувати фрагменти?
Ефтехарі

@Eftekhari Fragments => Complex LifeCycle => More Bugs => Хаос
Harshil Pansare

1
@HarshilPansare Так, я пережив усі ці катастрофи після того, як у лютому я задав це питання, і більше не буду використовувати фрагменти у своїх проектах. У мене не було вибору , окрім як очистити всі фрагменти з ViewPagerна , onDestroyтаким чином , на onResumeдіяльності там не буде ніякої необхідності , щоб отримати всі 3 фрагментів , які можуть більше не доступні. Просто хотілося згадати одну з проблем.
Ефтехарі

Привіт, життя без фрагментів!
Харшил Пансаре

Відповіді:


95

Вам потрібно перекрити ці два методи, а не 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);
}

5
приємний .. допоможи мені багато ... я розширив PageAdapter замість FragmentPageAdapter ........ тепер його робота чудово .....
ranjith

3
Для повного робочого прикладу ознайомтеся з кодом, знайденим з цього питання: stackoverflow.com/q/7263291
Tiago

7
Чому цікаво, чому потрібен addView. Щойно попросив адаптер фрагмента повернути вигляд.
Аміт Гупта

2
тобі ViewPagerвзагалі не потрібно звертатися, коли ти маєш справу з ViewGroupінтерфейсом
Дорі,

2
@AmitGupta, вам потрібно буде додати перегляд до контейнера, оскільки ви, можливо, не повертаєте його. instantiateItem повинен повернути об'єкт, пов’язаний із цим представленням, який може бути іншим об'єктом, якщо вам подобається; це ключ. Що б ви не повернули, ви просто переконайтеся, що ваша реалізація isViewFromObject може збігатися з двома, ключовими для перегляду. Однак найпоширеніша реалізація - це просто повернути погляд як ключовий. FragmentPageAdapter обробляє всі матеріали для перегляду ключових даних для вас, і, таким чином, просто просить створити фрагмент замість цього.
themightyjon

66

Скористайтеся цим прикладом

Ви можете використовувати один XML-макет, який вкладає дитячі подання.

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

АЛЕ ... вам також потрібно впоратися з цим адаптером. Тут ми повертаємо знайдений ідентифікатор подання без надування будь-якого іншого макета.

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

// Встановіть адаптер ViewPager

WizardPagerAdapter adapter = new WizardPagerAdapter();
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(adapter);

2
@ user1672337 findViewById доступний з класу Діяльність. Тож складіть внутрішній (нестатичний) клас у своєму Actvitiy. Або вам доведеться передати екземпляр діяльності адаптеру
ruX

7
Схоже, це не працює, якщо ViewPager має більше, ніж 2 перегляди дитини. Наприклад, якщо у мене в ViewPager є три RelativeLayouts, а потім спробувати перейти на третю сторінку, третя сторінка відображається як порожня. Будь-яка ідея, що дає?
Натан Уолтерс

15
@NathanWalters, у мене була та сама проблема сьогодні, я вирішив її, збільшивши властивість ViewPager OffscreenPageLimit. Напевно, варто оновити відповідь на цю інформацію.
Михайло

2
ви можете використовувати return collection.findViewById(resId);Якщо ви не хочете передавати екземпляр діяльності
Аксіома

3
Будь ласка, додайте цей код@Override public void destroyItem(ViewGroup container, int position, Object object) {}
Володимир Кулик

11

Ми створили дуже простий підклас того, ViewPagerяким ми іноді користуємося.

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

}

Для цього класу не потрібен адаптер, оскільки він буде завантажувати види з макета. Для того, щоб використовувати його у своїх проектах, просто використовуйте його замість android.support.v4.view.ViewPager.

Всі фантазійні речі все одно працюватимуть, але вам не потрібно зациклюватися на адаптерах.


1
Це майже працював для мене, але я повинен був змінити onAttachedToWindow()до onFinishInflate().
ehehhh

@Eftekhari Легше без фрагментів, і ви пишете менше коду, а значить, простіше читати та розуміти пізніше і менш схильні до помилок
Сабо,

11

Спираючись на попередні відповіді, я склав наступний клас, щоб досягти цього належним і чітким способом (сподіваюся):

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

І ви повинні встановити це у своїй діяльності:

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

Де файли XML очевидні 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>

А ActivtyMain має такий макет:

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

Велика подяка Брайану та Ніколасу за вашу відповідь, я сподіваюся, що я додаю трохи чіткішої інформації та підкреслюю кілька хороших практик цієї функції.


1
Не дуже добре, ви зберігаєте всі створені перегляди. Було б краще, якби вона зберегла лише видимі види. (схоже на convertview в listview)
htafoya

4

Я хотів би детальніше ознайомитись з відповіддю @Nicholas, ви можете отримати перегляди за id або, якщо вони динамічно додані, просто отримайте перегляд безпосередньо з урахуванням його позиції

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

4

Я хотів би додати тут своє рішення. З огляду на , що вам не потрібно фрагментів використання, ви можете створити PagerAdapterякий прив'язується viewsзамість fragmentsдо ViewPager.

Розширити PagerAdapterзамістьFragmentPagerAdapter

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

Тепер вам потрібно визначити три класи, які повернуть viewsнадутий у viewpager. Подібними у CpuViewвас будуть MemoryViewі NetworkViewзаняття. Кожен з них надує свої макети.

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

І нарешті макет, який буде завищений у кожному з переглядів

    <?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: Я написав цю відповідь, тому що всі запропоновані тут рішення виглядають нормально, але вони надувають макети в самому класі PagerAdapter. Для великих проектів важко підтримувати, якщо їх багато коду, пов'язаного із завищеними макетами. Тепер у цьому прикладі всі представлення мають окремі класи та окремі макети. Таким чином проект можна легко підтримувати.


IMO, активніше використовувати
Денні

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.