Dapatkah saya menggunakan pager tampilan dengan tampilan (bukan dengan fragmen)

131

Saya menggunakan ViewPageruntuk menggesek di antara Fragments, tetapi dapatkah saya menggunakan ViewPageruntuk menggesek antara Viewstata letak XML sederhana?

Ini adalah halaman saya Adapteruntuk ViewPager yang digunakan untuk menggesek antar Fragmen:

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

}

Dan ini adalah fragmen tip saya:

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

Bagaimana saya bisa memodifikasi kode saya agar berfungsi dengan Tampilan alih-alih Fragmen?

Ranjith
sumber
juga lihat contoh ini stackoverflow.com/a/37916222/3496570
Zar E Ahmer
Mengapa tidak menggunakan fragmen? Apa yang akan kita capai atau kehilangan jika kita menggunakan atau tidak menggunakan fragmen?
Eftekhari
@Eftekhari Fragments => Complex LifeCycle => Lebih Banyak Bug => Kekacauan
Harshil Pansare
1
@HarshilPansare Ya, saya mengalami semua bencana ini setelah saya mengajukan pertanyaan ini pada bulan Februari dan saya tidak akan menggunakan fragmen dalam proyek saya lagi. Saya tidak punya pilihan selain membersihkan semua fragmen dari ViewPageratas onDestroysehingga pada onResumeaktivitas tidak perlu mengambil semua 3 fragmen yang mungkin tidak lagi tersedia. Hanya ingin menyebutkan salah satu masalah.
Eftekhari
Sorakan untuk kehidupan yang bebas fragmen!
Harshil Pansare

Jawaban:

95

Anda perlu mengganti dua metode ini daripada 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
sumber
5
bagus .. banyak membantu saya ... saya memperpanjang PageAdapter bukan dari FragmentPageAdapter ........ sekarang berfungsi dengan baik .....
ranjith
3
Untuk contoh kerja yang lengkap, lihat kode yang ditemukan pada pertanyaan ini: stackoverflow.com/q/7263291
Tiago
7
Karena penasaran, mengapa addView diperlukan. Adaptor fragmen hanya meminta untuk mengembalikan tampilan.
Amit Gupta
2
Anda tidak perlu melakukan cast ViewPagersama sekali karena Anda berurusan dengan ViewGroupantarmuka
Dori
2
@AmitGupta Anda harus menambahkan tampilan ke wadah karena Anda mungkin tidak mengembalikan tampilan di sini. instantiateItem harus mengembalikan objek yang terkait dengan tampilan itu, yang bisa menjadi objek yang berbeda jika Anda suka; itu kuncinya. Apa pun yang Anda kembali, Anda hanya perlu memastikan bahwa implementasi isViewFromObject Anda dapat mencocokkan keduanya, kunci untuk melihatnya. Namun, implementasi yang paling umum adalah hanya mengembalikan tampilan sebagai kuncinya juga. FragmentPageAdapter menangani semua hal kunci untuk dilihat untuk Anda dan hanya meminta Anda untuk membuat sebuah fragmen.
themightyjon
66

Gunakan contoh ini

Anda dapat menggunakan tata letak XML tunggal untuk menampilkan pandangan anak-anak.

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

TAPI ... Anda perlu menangani ini dengan adaptor juga. Di sini kita mengembalikan ID tampilan yang ditemukan tanpa mengembang tata letak lainnya.

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

// Tetapkan adaptor ViewPager

WizardPagerAdapter adapter = new WizardPagerAdapter();
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(adapter);
Nicholas Betts
sumber
2
@ user1672337 findViewById dapat diakses dari kelas Activity. Jadi buatlah kelas internal (non-statis) di Actvitiy Anda. Atau Anda harus memberikan instance aktivitas ke adaptor
ruX
7
Ini tampaknya tidak berfungsi jika ViewPager memiliki lebih dari 2 tampilan anak. Misalnya, jika saya memiliki tiga RelativeLayouts di ViewPager saya dan kemudian mencoba menggesek ke halaman ketiga, halaman ketiga muncul sebagai kosong. Adakah yang tahu apa yang terjadi?
Nathan Walters
15
@NathanWalters, saya memiliki masalah yang sama hari ini, telah menyelesaikannya meningkatkan properti OffscreenPageLimit dari ViewPager. Mungkin perlu memperbarui jawaban dengan informasi ini.
Mikhail
2
Anda dapat menggunakan return collection.findViewById(resId);Jika Anda tidak ingin memberikan contoh aktivitas
Aksiom
3
Silakan tambahkan kode ini@Override public void destroyItem(ViewGroup container, int position, Object object) {}
Volodymyr Kulyk
11

Kami telah membangun subclass yang sangat sederhana dari ViewPageryang kami gunakan kadang-kadang.

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

}

Kelas ini tidak memerlukan adaptor karena akan memuat tampilan dari tata letak. Untuk menggunakannya, proyek Anda, gunakan saja bukan android.support.v4.view.ViewPager.

Semua barang mewah masih akan berfungsi, tetapi Anda tidak perlu repot dengan adaptor.

Sabo
sumber
1
Ini hampir bekerja untuk saya, tapi saya harus mengubah onAttachedToWindow()ke onFinishInflate().
ehehhh
@Eftekhari Lebih mudah tanpa fragmen dan Anda menulis lebih sedikit kode, yang berarti lebih mudah untuk membaca dan memahami nanti dan kurang rentan terhadap bug
Sabo
11

Berdasarkan jawaban sebelumnya, saya membuat kelas berikut untuk mencapainya dengan cara yang benar dan jelas (saya harap):

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

Dan Anda harus mengaturnya dalam aktivitas Anda:

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

Di mana file XML jelas 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>

Dan ActivtyMain memiliki tata letak berikut:

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

Terima kasih banyak untuk Brian dan Nicholas atas jawaban Anda, saya harap saya menambahkan beberapa informasi yang paling jelas dan menyoroti beberapa praktik yang baik untuk fitur ini.

Mathias Seguy Android2ee
sumber
1
Tidak terlalu bagus, Anda menyimpan semua tampilan yang dibuat. Akan lebih baik jika hanya menyimpan tampilan yang terlihat. (mirip dengan convertview in listview)
htafoya
4

Saya ingin menguraikan jawaban @Nicholas, Anda bisa mendapatkan pandangan dengan id atau jika mereka ditambahkan secara dinamis, dapatkan langsung melihat posisi yang diberikan.

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

Saya ingin menambahkan solusi saya di sini. Mengingat bahwa Anda tidak perlu menggunakan fragmen, Anda masih dapat membuat PagerAdapterlampiran yang viewsbukan fragmentske ViewPager.

Perpanjang PagerAdapterbukannyaFragmentPagerAdapter

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

Sekarang Anda perlu mendefinisikan tiga kelas yang akan mengembalikan viewsuntuk meningkat dalam viewpager. Mirip dengan CpuViewAnda akan memiliki MemoryViewdan NetworkViewkelas. Masing-masing dari mereka akan mengembang tata letak masing-masing.

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

Dan akhirnya tata letak yang akan meningkat di setiap tampilan

    <?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: Alasan saya menulis jawaban ini adalah karena semua solusi yang disediakan di sini tampaknya bekerja dengan baik, tetapi mereka menggelembungkan tata letak di kelas PagerAdapter itu sendiri. Untuk proyek besar menjadi sulit untuk mempertahankan jika mereka banyak kode yang terkait dengan tata letak yang meningkat. Sekarang dalam contoh ini semua tampilan memiliki kelas dan tata letak yang terpisah. Sehingga proyek dapat dengan mudah dipelihara.

penumpang utama
sumber
IMO, kegiatan lebih mudah digunakan
Denny