上一篇文章介绍了使用 FragmenttabHost 来使用 tab 导航;到 Android 5.0 的时候,又推出了 TabLayout。因此,有必要对tablayout 进行了解下。
首先我们来看MainActivity,看起来有点多,那是因为包含了 toolbar 的代码:
public class MainActivity extends AppCompatActivity { private TabLayout tabLayout; private ViewPager viewPager; private MyViewPagerAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); 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(); } }); tabLayout = (TabLayout) findViewById(R.id.tab_layout); viewPager = (ViewPager) findViewById(R.id.pager); int s = 0; if (viewPager != null) { setupViewPager(viewPager); } } private void setupViewPager(ViewPager viewPager) { adapter = new MyViewPagerAdapter(getSupportFragmentManager(), this); adapter.addFragment(new TabFragment1().newInstance("Page1"), "Tab 1"); adapter.addFragment(new TabFragment2().newInstance("Page2"), "Tab 2"); adapter.addFragment(new TabFragment3().newInstance("Page3"), "Tab 3"); viewPager.setAdapter(adapter); tabLayout.setupWithViewPager(viewPager); } @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); }
首先是初始化 tabLayout 和 viewPager,接着是建立一个 MyViewPagerAdapter 的实例,并向里面添加 fragment。
那我们就去看看神秘的 MyViewPagerAdapter。
public class MyViewPagerAdapter extends FragmentStatePagerAdapter { private final List<Fragment> myFragments = new ArrayList<>(); private final List<String> myFragmentTitles = new ArrayList<>(); private Context context; public MyViewPagerAdapter(FragmentManager fm, Context context) { super(fm); this.context = context; } public void addFragment(Fragment fragment, String title) { myFragments.add(fragment); myFragmentTitles.add(title); } @Override public Fragment getItem(int position) { return myFragments.get(position); } @Override public int getCount() { return myFragments.size(); } @Override public CharSequence getPageTitle(int position) { return myFragmentTitles.get(position); } }
这里我们重写了其中几个方法,分别是 getItem,getCount,getPageTitle。那这样我们的数量能够传进去嘛?
不用怕,当我们调用 setAdapt 的时候,其实就把 adapt 传进 ViewPager 了,当需要获取数量的时候,直接调用这个方法就可以了。
tabLayout.setupWithViewPager(viewPager) 则是把tab 与ViewPage 建立联系,这样在变动的时候一起变,在TabLaout 中,可以通过 viewPager.getAdapt 来获取我们刚刚穿进去的adapt;这样两者都有 adapt 这个变量了。
public void setupWithViewPager(ViewPager viewPager) { PagerAdapter adapter = viewPager.getAdapter(); if(adapter == null) { throw new IllegalArgumentException("ViewPager does not have a PagerAdapter set"); } else { this.setTabsFromPagerAdapter(adapter); viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(this)); this.setOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(viewPager)); } }
看源码可以发现 tablayout 和 viewpager 互相设置或者添加监听器,这样无论是滑动 viewPager,还是点击tab都可以实现 view 的切换,两者就可以同步了。
public static class ViewPagerOnTabSelectedListener implements TabLayout.OnTabSelectedListener { private final ViewPager mViewPager; public ViewPagerOnTabSelectedListener(ViewPager viewPager) { this.mViewPager = viewPager; } public void onTabSelected(TabLayout.Tab tab) { this.mViewPager.setCurrentItem(tab.getPosition()); } public void onTabUnselected(TabLayout.Tab tab) { } public void onTabReselected(TabLayout.Tab tab) { } }
我们可以具体看看 ViewPagerOnTabSelectedListener,发现其实现了 TabLayout.OnTabSelectedListener 的接口,在构造函数中,将 viewPager 变量赋值给内部变量,这样在 tab 选中的时候,获取选中 tab 的 position,然后再将设置显示 同样位置的 fragment 即可。
public static class TabLayoutOnPageChangeListener implements OnPageChangeListener { private final WeakReference<TabLayout> mTabLayoutRef; private int mScrollState; public TabLayoutOnPageChangeListener(TabLayout tabLayout) { this.mTabLayoutRef = new WeakReference(tabLayout); } public void onPageScrollStateChanged(int state) { this.mScrollState = state; } public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { TabLayout tabLayout = (TabLayout)this.mTabLayoutRef.get(); if(tabLayout != null) { tabLayout.setScrollPosition(position, positionOffset, this.mScrollState == 1); } } public void onPageSelected(int position) { TabLayout tabLayout = (TabLayout)this.mTabLayoutRef.get(); if(tabLayout != null) { tabLayout.getTabAt(position).select(); } } }
当滑动页面,回掉 onPageSelected,传入目前选中的 pager,这时候只要指定对应的 tab 即可。这样两者很好的进行同步了。
我们再来看看布局文件:
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout 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" android:fitsSystemWindows="true" tools:context=".MainActivity"> <android.support.design.widget.AppBarLayout android:layout_height="wrap_content" android:layout_width="match_parent" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" app:layout_scrollFlags="scroll|enterAlways" /> <android.support.design.widget.TabLayout android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#eeeeee" app:tabTextColor="#888888" app:tabSelectedTextColor="#000000" app:tabGravity="fill" app:tabMode="fixed"/> </android.support.design.widget.AppBarLayout> <android.support.v4.view.ViewPager android:id="@+id/pager" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"/> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" android:src="@android:drawable/ic_dialog_email" /> </android.support.design.widget.CoordinatorLayout>
因为有 toolbar ,所以布局文件看上去有些复杂,但其实很简单。
简化后的布局(id 不一定对得上,其他地方 copy 过来的):
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.design.widget.TabLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/tabLayout" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:background="#cdc36e" app:tabMode="fixed" app:tabGravity="fill"/> <android.support.v4.view.ViewPager android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/viewPager" android:layout_below="@+id/tabLayout" android:layout_centerHorizontal="true" /> </RelativeLayout>
至于 fragment 文件可以参考上一篇文章中的布局
或者直接查看源码所在地:
https://github.com/huanshen/Learn-Android/tree/master/TablayoutViewpager