Android destek kütüphanesi v4 ile yayınlanan ViewPager bize içine eklediğimiz viewlar arasında kullanıcı dostu geçiş sağlıyor. Ancak bu geçişlerde kullanacağımız viewları dinamik oluşturmamız gerekirse veya takvim uygulaması gibi bir örnekte sola ve sağa doğru sonsuz view eklemek isteseydik buna ViewSwitcher gibi tam bir destek vermiyor. Bu yazımda ViewPager içinde dinamik oluşturduğumuz viewlar arasında sola ve sağa sonsuz döngü kuracağız.

Öncelikle ViewPager içinde görüntüleceğimiz özelleştirilmiş view’ı hazırlıyoruz.

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

    <TextView
        android:id="@+id/text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:visibility="gone"/>

    <ProgressBar
        android:id="@+id/progress_bar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:indeterminateOnly="true"/>

</com.canyapan.dynamicviewpagertestapplication.CustomViewPagerContentView>
public class CustomViewPagerContentView extends RelativeLayout {
    private int mItemID;
    private TextView mTextView;
    private ProgressBar mProgressBar;
    private LoadDateAsyncTask mAsyncTask = null;

    public CustomViewPagerContentView(Context context) {
        this(context, null);
    }

    public CustomViewPagerContentView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomViewPagerContentView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

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

        mTextView = (TextView) findViewById(R.id.text_view);
        mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
    }

    public void setContent(int itemID) {
        // Cancel any old probably ongoing process.
        if (null != mAsyncTask) {
            mAsyncTask.cancel(true);
        }

        mItemID = itemID;

        mAsyncTask = new LoadDateAsyncTask();
        mAsyncTask.execute(Integer.toString(itemID));
    }

    public int getItemID() {
        return mItemID;
    }

    /**
     * Burada bir AsyncTask yardımı ile view'da görüntüleyeceğimiz datayı hazırlıyoruz.
     */
    protected class LoadDateAsyncTask extends AsyncTask<String, Integer, String> {
        @Override
        protected void onPreExecute() {
            mTextView.setVisibility(TextView.GONE);
            mProgressBar.setVisibility(ProgressBar.VISIBLE);
        }

        @Override
        protected String doInBackground(String... params) {
            try {
                // Bir veritabanı işlemi veya http get/post işlemi simule ediyoruz.
                Thread.sleep(200 + new Random().nextInt(800));

                return params[0];
            } catch (InterruptedException ignore) {
            }

            // Sonlanmamış işlem..
            return null;
        }

        @Override
        protected void onPostExecute(String result) {
            // İstediğimiz veri hazır olduğunda ilgili view'a yazabiliriz.
            if (null != result) {
                mProgressBar.setVisibility(ProgressBar.GONE);
                mTextView.setText(result);
                mTextView.setVisibility(TextView.VISIBLE);
            }
        }

        protected void onCancelled() {
            mTextView.setVisibility(TextView.GONE);
            mProgressBar.setVisibility(ProgressBar.VISIBLE);

            super.onCancelled();
        }
    }
}

Temelde ViewPager içine PagerAdapter ile 3 tane view ekleyip bunları sağa ve sola doğru kaydıracağız. Bu işlevi sağlayabilmek için PagerAdapter’ın birkaç metodunu Override etmemiz gerekecek. Bu metodlar instantiateItem, destroyItem, getItemPosition ve tabiki getCount ile isViewFromObject abstract metodları.

public class DynamicViewPagerAdapter extends PagerAdapter {
    private List<CustomViewPagerContentView> mViews;

    public DynamicViewPagerAdapter(Context context, int start) {
        final LayoutInflater inflater = LayoutInflater.from(context);

        // Adapter oluşturulurken bu ViewPager ile görüntülemek istediğimiz 3 view'ı hazırlıyoruz.
        mViews = new ArrayList<>(3);
        mViews.add(makeView(inflater, start - 1));
        mViews.add(makeView(inflater, start));
        mViews.add(makeView(inflater, start + 1));
    }

    private CustomViewPagerContentView makeView(LayoutInflater inflater, int id) {
        // Bu örnekte özelleştirilmiş bir view yaratıyoruz.
        CustomViewPagerContentView v = (CustomViewPagerContentView) inflater.inflate(R.layout.custom_view_pager_content_view, null);
        // View içeriğini ekliyoruz.
        v.setContent(id);

        return v;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View v = mViews.get(position);
        // Burada görüntülenecek olan view ViewPager'a ekleniyor.
        container.addView(v);

        return v;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        // Artık görüntülenemeyecek olan viewlar bu şeilde ViewPager'dan çıkarılıyor.
        container.removeView((View) object);
    }

    @Override
    public int getItemPosition(Object object) {
        if (mViews.contains(object)) {
            // ViewPager içinde döngü oluşturmak için view'ların sırasını değiştiriyoruz. Bu
            // değişimden sonra view'ın yeni pozisyonu burada alınıyor.
            return mViews.indexOf(object);
        }

        // Artık listede bulunmayan view'ların bu şekilde kaldırılması gerektiği işaret ediliyor.
        return POSITION_NONE;
    }

    @Override
    public int getCount() {
        return mViews.size();
    }

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

    public void move(int position) {
        CustomViewPagerContentView v;
        int newID;
        switch (position) {
            case 0:
                newID = mViews.get(0).getItemID() - 1;

                // Sondan çıkarılan view'ın içeriği değiştirilip başa ekleniyor.
                v = mViews.remove(2);
                v.setContent(newID);

                mViews.add(0, v);
                break;
            case 2:
                newID = mViews.get(2).getItemID() + 1;

                // Baştan çıkarılan view'ın içeriği değiştirilip sona ekleniyor.
                v = mViews.remove(0);
                v.setContent(newID);

                mViews.add(2, v);
                break;
            default:
                return;
        }

        // Adapter'a içeriğin değiştirildiği ve yeni eklenen, çıkarılan veya değiştirilen içerikleri
        // kontrol etmesini burada söylüyoruz.
        notifyDataSetChanged();
    }

    public int getId(int position) {
        return mViews.get(position).getItemID();
    }
}

Activity’e eklediğimiz ViewPager’a bu adapter’ı ekliyoruz. Ve sayfa geçişlerini dinleyeceğimiz ViewPager.OnPageChangeListener‘ı implement ediyoruz.

public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener {
    private final String KEY_CURRENT_ITEM_ID_INT = "ITEM_ID";
    private ViewPager mDynamicViewPager;
    private DynamicViewPagerAdapter mDynamicViewPagerAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        int itemID;
        if (savedInstanceState != null) {
            itemID = savedInstanceState.getInt(KEY_CURRENT_ITEM_ID_INT);
        } else {
            itemID = 0;
        }

        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

        // ViewPager'da kullanmak üzere yeni bir PageAdapter objesi yaratıyoruz.
        mDynamicViewPagerAdapter = new DynamicViewPagerAdapter(MainActivity.this, itemID);
        mDynamicViewPager = (ViewPager) findViewById(R.id.DynamicViewPager);
        mDynamicViewPager.setAdapter(mDynamicViewPagerAdapter);
        // Sayfa geçişlerinde çağırılacak olan dinleyiciyi ekliyoruz.
        mDynamicViewPager.addOnPageChangeListener(this);
        // İlk açılışta ortadaki view'ın açılması için.
        mDynamicViewPager.setCurrentItem(1, false);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        outState.putSerializable(KEY_CURRENT_ITEM_ID_INT,
                mDynamicViewPagerAdapter.getId(mDynamicViewPager.getCurrentItem()));
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        // NOT USED...
    }

    @Override
    public void onPageSelected(final int position) {
        // Sola ve sağa geçişte adapter'da yapılmasını istediğimiz kaydırma işlemini yapıyoruz.
        mDynamicViewPagerAdapter.move(position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        // NOT USED...
    }
}

Kaynak kodlar (GitHub)