Šta je ViewPager?
ViewPager je Layout Manager koji omogućava korisniku da se kreće kroz stranice podataka pokretima na levo ili desno, dok se pri promeni stranica izvršava i ugradjena animacija. Najčešće su te strane sa podacima su fragmenti, mada mogu da budu i nešto drugo kao npr. slike koje se slajduju…
ViewPager se popunjava podacima koristeći svoj adapter tzv. PagerAdapter. Ukoliko koristimo fragmente za njih se koriste specijalizovani adapteri:
- FragmentPagerAdapter (kešira fragmente pa se koristi za manji broj fragmenata obično u saradnji sa tab-ovima)
- FragmentStatePagerAdapter (koristi se kod većeg broja fragmenata)
ViewPager se ubacuje u layout kao kontejner u kome će se prikazivati sve stranice.
1 2 3 4 5 6 7 8 |
<android.support.v4.view.ViewPager android:id="@+id/vpPagerContainer" android:layout_width="match_parent" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tabs" /> |
TabLayout
ViewPager se najčešće integriše u layout zajedno sa TabLayout-om, koji predstavlja navigaciju.
Pre ubacivanja TabLayouta potrebno je da se doda novi dependecies “design”:
implementation 'com.android.support:design:28.0.0'
Tek nakon ubacivanja dependencies možemo da dodamo novi widget TabLayout sa TabItem-ima:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <android.support.design.widget.TabItem android:id="@+id/tabItem" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TAB 1" /> <android.support.design.widget.TabItem android:id="@+id/tabItem2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TAB 2" /> <android.support.design.widget.TabItem android:id="@+id/tabItem3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TAB 3" /> </android.support.design.widget.TabLayout> |
Korišćenje PagerAdapter-a
PagerAdapter se koristi da popuni ViewPager kontejner sa odgovarajućim stranicama. U ovom članku stranice će predstavljati fragmenti, te je pre svega potrebno kreirati tri različita fragmenta (FragmentA, FragmentB, FragmentC).
a) Kreiranje adapter klase
Kada pravimo naš custom adapter potrebno je da ekstendujemo našu klasu ili sa PagerAdapter klasom ili sa jednom od već pomenutih klasa: FragmentPagerAdapter ili FragmentStatePagerAdapter koje se koriste za rad sa fragmenti-ma.
MojViewPagerAdapter.java
Pošto u ovome primeru imamo mali broj fragmenta, koristićemo klasu koja kešira fragmente (brza ali dobra samo sa malim brojem fragmenata):
1 |
public class MojViewPagerAdapter extends FragmentPagerAdapter { } |
Čim ekstendujemo klasu AndroidStudio zahteva da se generiše konstruktor i implementriraju dve metode:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class MojViewPagerAdapter extends FragmentPagerAdapter { public MojViewPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int i) { return null; } @Override public int getCount() { return 0; } } |
Metoda getItem(int position) vraća odgovarajući fragment u zavisnosti od pozicije (tj. int parametra koji predstavlja poziciju fragmenta):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@Override public Fragment getItem(int position) { Fragment fragment = null; switch (position) { case 0: fragment = new FragmentA(); break; case 1: fragment = new FragmentB(); break; case 2: fragment = new FragmentC(); break; } return fragment; } |
Metoda getCount() je planirana da vraća ukupan broj stranica koji treba da budu prikazani:
1 2 3 4 |
@Override public int getCount() { return 3; } |
b) Instanciranje adaptera u aktivnosti
Kada smo napravili Adpter klasu potrebno je da u okviru aktivnosti napravimo njegovu instancu. Nova instanca se pravi koristeći konstruktorsku funkciju kojoj se prosledjuje instanca framentManager-a:
Aktivnost
1 |
mojViewPagerAdapter = new MojViewPagerAdapter(getSupportFragmentManager()); |
c) Povezivanje adaptera i ViewPager kontejnera
Kada targetiramo ViewPager onda jednostavno možemo da ga povežemo sa adapterom koristeći metodu setAdapter():
Aktivnost
1 2 |
pagerContainer = findViewById(R.id.vpPagerContainer); pagerContainer.setAdapter(mojViewPagerAdapter); |
d) Sinhronizovanje ViewPager-a i TabLayout-a
Već sada je ViewPagerAdapter popunio ViewPager sa fragmentima, tako da možemo slajdovati fragmenate jednostavnim “swipe” pokretima levo ili desno. Medjutim iako se uspešno menjaju fragmenti, ne dolazi do promena kod TabLayout-a. Potrebno je povezati swipe dogadjaj sa promenom u Tablayoutu i obrnuto. Za interakciju izmedju ova dva View-a se koriste listeneri i tzv. listener pattern (više o listener pattern-u možete pogledati ovde).
ViewPager listener
ViewPager ima interfejs “ViewPager.OnPageChangeListener” čije se callback metode (onPageScrollStateChanged, onPageScrolled i onPageSelected) pozivaju kada dodje do promene strane slajdovanjem u ViewPager-u. Da bi listener radio svoj posao potrebno je da setujemo osluškivač dogadjaja, što se vrši sa setter metodom addOnPageChangeListener(). Ovu metodu poziva objekat koji implementira ovaj interfejs a to je bilo koji ViewPager.
1 |
ViewPager.addOnPageChangeListener(objekatKojiTrebaDaOsluškuje) |
Objekat koji je zainteresovan da postane osluškivač promene stranica u ViewPager-u je ustvari TabLayout objekat. Za ovu ulogu u androidu postoji specifična klasa koja implementira sve callback metode i tako sinhronizuje TabLayout nakon promene stranice u ViewPager-u pod nazivom “TabLayout.TabLayoutOnPageChangeListener”.
Ova klasa u konstruktoru prihvata TabLayout kao parametar, pa ćemo nju iskoristiti da napravimo “on the fly” kao anonimni objekat :
1 |
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout)); |
TabLayout listener
TabLayout ima interfejs “TabLayout.OnTabSelectedListener” čije se callback metode ( onTabSelected(), onTabReselected(), onTabUnselected()) pozivaju kada dodje do promene u selekciji tabova.
Da bi listener patern radio svoj posao potrebno je da setujemo osluškivača ovih dogadjaja, a to se vrši sa setter metodom addOnTabSelectedListener(). Ovu metodu poziva objekat koji implementira ovaj interfejs a to je bilo koji tabLayout.
1 |
tabLayout.addOnTabSelectedListener(objekatKojiTrebaDaOsluškuje); |
Objekat koji je zainteresovan da postane osluškivač promene tabova je ustvari ViewPager objekat. Za ovu ulogu u androidu postoji specifična klasa koja implementira sve callback metode i tako sinhronizuje ViewPager nakon promene tabova pod nazivom “TabLayout.OnTabSelectedListener”.
Ova klasa u konstruktoru prihvata ViewPager kao parametar, pa ćemo nju iskoristiti da napravimo “on the fly” kao anonimni objekat :
1 |
mTabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager)); |
Pogledajte ceo kod
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class MainActivity extends AppCompatActivity { MojViewPagerAdapter mojViewPagerAdapter; ViewPager pagerContainer; TabLayout mojTabLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); pagerContainer = findViewById(R.id.vpPagerContainer); mojTabLayout = findViewById(R.id.tabs); //adapter mojViewPagerAdapter = new MojViewPagerAdapter(getSupportFragmentManager()); pagerContainer.setAdapter(mojViewPagerAdapter); //Listeneri pagerContainer.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mojTabLayout)); mojTabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(pagerContainer)); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class MojViewPagerAdapter extends FragmentPagerAdapter { private static int NUM_ITEMS = 3; public MojViewPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { Fragment fragment = null; switch (position) { case 0: fragment = new FragmentA(); break; case 1: fragment = new FragmentB(); break; case 2: fragment = new FragmentC(); break; } return fragment; } @Override public int getCount() { return NUM_ITEMS; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <android.support.design.widget.TabLayout android:id="@+id/tabs" android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> <android.support.design.widget.TabItem android:id="@+id/tabItem" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TAB 1" /> <android.support.design.widget.TabItem android:id="@+id/tabItem2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TAB 2" /> <android.support.design.widget.TabItem android:id="@+id/tabItem3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TAB 3" /> </android.support.design.widget.TabLayout> <android.support.v4.view.ViewPager android:id="@+id/vpPagerContainer" android:layout_width="match_parent" android:layout_height="0dp" android:background="@android:color/darker_gray" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/tabs" /> </android.support.constraint.ConstraintLayout> |
NAPOMENA:
Za brzu integraciju TabLayout-a i ViewPager-a možemo koristi već predvidjenu “Tabbed Activity” sa ugradjenom navigacijom (Actions bar Tabs with ViewPager).
Sa ovom aktivnosti dolazi već odradjen veći deo posla, pa uz par manjih izmena sve može da se pripremi veoma brzo.
Pre svega potrebno je napraviti par fragment layouta (npr. fragment_a.xml, fragment_b.xml, fragment_c.xml) i prilagoditi getItem() metodu:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@Override public Fragment getItem(int position) { Fragment fragment = null; switch (position) { case 0: fragment = new FragmentA(); break; case 1: fragment = new FragmentB(); break; case 2: fragment = new FragmentC(); break; } return fragment; } |
Deo koji sa sigurnošću možemo da obrišemo je vezan za privremeni placeholder fragment :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public static class PlaceholderFragment extends Fragment { private static final String ARG_SECTION_NUMBER = "section_number"; public PlaceholderFragment() { } public static PlaceholderFragment newInstance(int sectionNumber) { PlaceholderFragment fragment = new PlaceholderFragment(); Bundle args = new Bundle(); args.putInt(ARG_SECTION_NUMBER, sectionNumber); fragment.setArguments(args); return fragment; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_main, container, false); TextView textView = (TextView) rootView.findViewById(R.id.section_label); textView.setText(getString(R.string.section_format, getArguments().getInt(ARG_SECTION_NUMBER))); return rootView; } } |
Ukoliko nam ne treba FloatingActionButton onda možemo obrisati njegov deo koda:
1 2 3 4 5 6 7 8 |
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(); } }); |
Ako nam ne treba “options meni” onda možemo da obrišemo deo vezan za to:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
@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); } |
Ceo kod “pročišćene” aktivnosti možete da pogledate ovde.
Aktivnost
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
public class MainActivity extends AppCompatActivity { private SectionsPagerAdapter mSectionsPagerAdapter; private ViewPager mViewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); TabLayout tabLayout = findViewById(R.id.tabs); mViewPager = findViewById(R.id.container); // Create the adapter that will return a fragment for each of the three primary sections of the activity. mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager()); // Set up the ViewPager with the sections adapter. mViewPager.setAdapter(mSectionsPagerAdapter); // Listeners tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager)); mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout)); } // PagerAdapter public class SectionsPagerAdapter extends FragmentPagerAdapter { public SectionsPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { Fragment fragment = null; switch (position) { case 0: fragment = new FragmentA(); break; case 1: fragment = new FragmentB(); break; case 2: fragment = new FragmentC(); break; } return fragment; } @Override public int getCount() { return 3; } } } |