Android开发之漫漫长途Fragment番外篇——
TabLayout+ViewPage。。。
该⽂章是⼀个系列⽂章,是本⼈在Android开发的漫漫长途上的⼀点感想和记录,我会尽量按照先易后难的顺序进⾏编写该系列。该系列引⽤了《Android开发艺术探索》以及《深⼊理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相关知识,另外也借鉴了其他的优质博客,在此向各位⼤神表⽰感谢,膜拜
前⾔
上⼀篇⽂章中我们使⽤底部导航+Fragment的⽅式实现了Android主流App中⼤都存在的设计。并命名其为“Fragment最佳实践”,作为想到单独使⽤Fragment的⽤户来说,这个说法并不夸⼤,它解决了许多⽤户在使⽤Fragment时产⽣的这样那样可见或不可见的问题。不过Fragment还有其他的使⽤⽅式,就是我们本章要介绍的。(本来是介绍ListView的,等着ListView的读者不好意思了,我会很快更新的。)注:为什么临时插⼊这⼀章,因为有读者在上⼀篇⽂章中评论了,我觉得⼤有道理,感谢
这⾥我就不打码了,,哈哈哈哈
TabLayout
TabLayout的静态使⽤##
TabLayout是Android 5.0之后Google提供的⼀系列Material Design设计规范中的⼀个控件。我们在布局⽂件中可以这样使⽤
。
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:tabIndicatorHeight="0dp"
app:tabSelectedTextColor="@color/colorPrimary"
>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tab 1"/>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tab 2"/>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tab 3"/>
</android.support.design.widget.TabLayout>
TabLayout间接继承于ViewGroup,其内可包含0到n个TabItem,这个TabItem就是我们经常使⽤的标签,其是个⾃定义View
,这样我们就定义了⼀个包含3个标签页的TabLayout。其运⾏结果如下图:
TabLayout的动态使⽤##
在布局⽂件中我们可以很⽅便定义顶部/底部导航的布局。我们来看⼀下在代码中的使⽤
public class TabActivity extends AppCompatActivity {
@BindView(R.id.tab_layout)
TabLayout mTabLayout;
@BindView(R.id.view_pager)
ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
tContentView(R.layout.activity_tab);
ButterKnife.bind(this);
mTabLayout.wTab().tText("Tab 1"));
mTabLayout.wTab().tText("Tab 2"));
mTabLayout.wTab().tText("Tab 3"));
//为TabLayout添加Tab选择事件监听
mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {//当标签被选择时回调
}
@Override
public void onTabUnlected(TabLayout.Tab tab) {//当标签从选择变为⾮选择时回调
}
@Override
public void onTabRelected(TabLayout.Tab tab) {//当标签被重新选择时回调
}
});
}
}
关于运⾏结果我就不上图了,跟上⾯的运⾏结果是⼀样的。
TabLayout的更多属性##
关于TabLayout的更多属性以及使⽤的说明请查看其官⽅⽂档。在这⾥我们只关⼼TabLayout+ViewPager的化学反应,这个组合也是我们平常在开发中使⽤最多的。在此之前我们先介绍ViewPager
ViewPager
先看看官⽅对ViewPager的说明
/*
Layout manager that allows the ur to flip left and right
through pages of data. You supply an implementation of a
{@link PagerAdapter} to generate the pages that the view shows.
ViewPager is most often ud in conjunction with {@link android.app.Fragment}
There are standard adapters implemented for using fragments with the ViewPager,
which cover the most common u cas. The are
{@link android.support.v4.app.FragmentPagerAdapter} and
{@link android.support.v4.app.FragmentStatePagerAdapter};*/
public class ViewPager extends ViewGroup {
}
上⾯英⽂的⼤致意思是ViewPager是⼀个布局管理类,这个类呢允许⽤户左右翻转页⾯。你必须实现⼀个PagerAdapter来⽣成这些显⽰的页⾯。ViewPager经常和Fragment⼀起使⽤。⽽且呢Google⾮常贴⼼的提供了两个类FragmentPagerAdapter和FragmentStatePagerAdapter 来应付那些⼀般场景。
其实从ViewPager的说明中,我们基本上就能知道ViewPager是什么以及如何使⽤了。
PagerAdapter
ViewPager继承于ViewGroup,官⽅指导中就说了,你要⾃⼰实现PagerAdapter来⽣成显⽰的页⾯,那么我们来看看这个PagerAdapter
/
**
* Ba class providing the adapter to populate pages inside of
* a {@link ViewPager}. You will most likely want to u a more
* specific implementation of this, such as
* {@link android.support.v4.app.FragmentPagerAdapter} or
* {@link android.support.v4.app.FragmentStatePagerAdapter}.
*
* <p>When you implement a PagerAdapter, you must override the following methods
* at minimum:</p>
* <ul>
* <li>{@link #instantiateItem(ViewGroup, int)}</li>
* <li>{@link #destroyItem(ViewGroup, int, Object)}</li>
* <li>{@link #getCount()}</li>
* <li>{@link #isViewFromObject(View, Object)}</li>
* </ul>
* /
public abstract class PagerAdapter {
}
其实我们在看⼀个不太了解的类的时候,通过源码上的关于这个类的说明就可以知道很多信息了。关于PagerAdapter的说明就是如此。
先说了⼀下PagerAdapter的作⽤,是⼀个基类提供适配器给ViewPager中的页⾯,如果你想使⽤特定的实现类,那么你可以看两个类
FragmentPagerAdapter和FragmentStatePagerAdapter,这两个类继承了PagerAdapter,并实现了其
抽象⽅法。后⾯⼀段的意思是你如果想⾃定义你⾃⼰的PagerAdapter,那么你最少要实现这4个⽅法
1. instantiateItem(ViewGroup, int)
2. destroyItem(ViewGroup, int, Object)
3. getCount()
4. isViewFromObject(View, Object)
下⾯我们以代码的形式,说明这4个⽅法的含义以及如何使⽤
private class MyViewPagerAdapter extends PagerAdapter {
/**
* 获取View的总数
*
* @return View总数
*/
@Override
public int getCount() {
return 0;
}
/**
* 为给定的位置创建相应的View。创建View之后,需要在该⽅法中⾃⾏添加到container中。
*
* @param container ViewPager本⾝
* @param position 给定的位置
* @return 提交给ViewPager进⾏保存的实例对象
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
return super.instantiateItem(container, position);
}
/**
* 给定的位置移除相应的View。
*
* @param container ViewPager本⾝
* @param position 给定的位置
* @param object 在instantiateItem中提交给ViewPager进⾏保存的实例对象
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
}
/**
* 确认View与实例对象是否相互对应。ViewPager内部⽤于获取View对应的ItemInfo。
*
* @param view ViewPager显⽰的View内容
* @param object 在instantiateItem中提交给ViewPager进⾏保存的实例对象
* @return 是否相互对应
*/
@Override
public boolean isViewFromObject(View view, Object object) {
return fal;
}
}
这4个⽅法是必须的,,另外还有⼀些不是必须,但是可能会⽤到的
/**
* 当ViewPager的内容有所变化时,进⾏调⽤。
*
* @param container ViewPager本⾝
*/
@Override
public void startUpdate(ViewGroup container) {
super.startUpdate(container);
}
/**
* ViewPager调⽤该⽅法来通知PageAdapter当前ViewPager显⽰的主要项,提供给⽤户对主要项进⾏操作的⽅法。
*
* @param container ViewPager本⾝
* @param position 给定的位置
* @param object 在instantiateItem中提交给ViewPager进⾏保存的实例对象
*/
@Override
public void tPrimaryItem(ViewGroup container, int position, Object object) {
super.tPrimaryItem(container, position, object);
}
/**
* 较多的⽤于Design库中的TabLayout与ViewPager进⾏绑定时,提供显⽰的标题。
*
* @param position 给定的位置
* @return 显⽰的标题
*/
@Override
public CharSequence getPageTitle(int position) {
PageTitle(position);
}
FragmentPagerAdapter
上⾯呢只是列举说明了⼀下PagerAdapter,看起来有些枯燥,都是些说明,那么我们来看⼀下实践,ViewPager通畅跟Fragment⼀起使⽤,即其所管理的页⾯通畅是Fragment,所以Google提供了两个适配器FragmentPagerAdapter和FragmentStatePagerAdapter,我们这节分析FragmentPagerAdapter。
/**
*真是不看不知道,⼀看吓⼀跳。FragmentPagerAdapter也是个抽象类,
*
*/
public abstract class FragmentPagerAdapter extends PagerAdapter {
private static final String TAG = "FragmentPagerAdapter";
private static final boolean DEBUG = fal;
private final FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;
private Fragment mCurrentPrimaryItem = null;
public FragmentPagerAdapter(FragmentManager fm) {
mFragmentManager = fm;
}
/**
*抽象⽅法,看来这个函数要⼦类⾃⼰实现了
*
* @param position ViewPager中Item的位置
* @return 位置相关联的Fragment
*/
public abstract Fragment getItem(int position);
@Override
public void startUpdate(ViewGroup container) {
if (Id() == View.NO_ID) {
throw new IllegalStateException("ViewPager with adapter " + this
+ " requires a view id");
}
}
/**
* 为给定的位置创建相应的fragment。创建fragment之后,需要在该⽅法中⾃⾏添加到container中。
*
* @param container ViewPager本⾝
* @param position 给定的位置
* @return 提交给ViewPager进⾏保存的实例对象,这⾥是Fragment
*/
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);
String name = Id(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} el {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.Id(), fragment,
Id(), itemId));
}
if (fragment != mCurrentPrimaryItem) {
fragment.tMenuVisibility(fal);
fragment.tUrVisibleHint(fal);
}
return fragment;
}
/**
* 移除给定的位置相应的fragment。
*
* @param container ViewPager本⾝
* @param position 给定的位置
* @param object 在instantiateItem中提交给ViewPager进⾏保存的实例对象
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
+ " v=" + ((Fragment)object).getView());
mCurTransaction.detach((Fragment)object);
}
@Override
public void tPrimaryItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment)object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.tMenuVisibility(fal);
mCurrentPrimaryItem.tUrVisibleHint(fal);
}
if (fragment != null) {
fragment.tMenuVisibility(true);
fragment.tUrVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}
@Override
public void finishUpdate(ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction = null;
}
}
@Override
public boolean isViewFromObject(View view, Object object) {
return ((Fragment)object).getView() == view;
}
@Override
public Parcelable saveState() {
return null;
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
}
/**
* @param position ViewPager中Item的位置
* @return 唯⼀的ItemID
*/
public long getItemId(int position) {
return position;
}
private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
}
代码⽐较少,总共也就100多⾏,逻辑也⽐较清晰明了,我们来着重分析instantiateItem和destroyItem