您的位置:首页 > 其它

解读(四):分析主界面顶部Tab的实现

2016-04-28 00:55 399 查看

解读(四):分析主界面顶部Tab的实现

使用Tab管理类管理顶部Tab

上次分析到了利用Tabs的管理类来动态更新首页顶部tabs的显示

Fragment mTab = new BaseTabMainFragment() {
@Override
public void onSetupTabs() {
//添加要显示主界面Tabs项
addTab(getResources().getString(R.string.new_news), ListNewsFragment.class, NewsList.CATALOG_ALL); //最新资讯
addTab(getResources().getString(R.string.week_news), ListNewsFragment.class, NewsList.CATALOG_WEEK); //周度资讯
addTab(getResources().getString(R.string.month_news), ListNewsFragment.class, NewsList.CATALOG_MONTH); //月度资讯
}
};


可以发现继承关系 BaseTabMainFragment –> BaseTabFragment –> BaseFragment, 从父类开始分析

BaseFragment类

/**
* Fragment的基类
*/
public abstract class BaseFragment<P extends Presenter> extends NucleusFragment<P> {
public Context mContext;
public Resources resources;
public static final String BUNDLE_TYPE = "BUNDLE_TYPE";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getActivity(); //获得所附加的activity对象
resources = mContext.getResources(); //便于获取资源
}
}


BaseTabFragment类

/**
* Tab的Fragment基类
*/
public abstract class BaseTabFragment extends BaseFragment {
protected ViewPager mViewPager;
protected FragmentStatePagerAdapter mAdapter;
protected ArrayList<ViewPageInfo> mTabs;
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewPager = (ViewPager) view.findViewById(R.id.view_pager);
if (mAdapter == null) {
mTabs = new ArrayList<>();
//设置tab项
onSetupTabs();
//设置适配器, 这里有个疑问, 一直都没有为mAdapter赋值,那么每次是不是都会走 mAdapter==null的流程?
mViewPager.setAdapter(new FragmentStatePagerAdapter(getGenuineFragmentManager()) {
@Override
public Fragment getItem(int position) {
//获得ViewPagerInfo中管理的fragment
return mTabs.get(position).fragment;
}
@Override
public int getCount() {
return mTabs.size();
}
@Override
public CharSequence getPageTitle(int position) {
//获得标题, 这个tag也就是fragment的标题
return mTabs.get(position).tag;
}
});
} else {
mViewPager.setAdapter(mAdapter);
}
}
/**
* 导航元素, 也就是Tab页卡上的View
*/
public abstract View setupTabItemView(String title);
/**
* 设置Fragment, 由子类实现
*/
public abstract void onSetupTabs();
/**
* 获得FragmentManager, 这个封装也真是够了, 是为了增加方法数吗?
*/
public FragmentManager getGenuineFragmentManager() {
return getFragmentManager();
}
/**
* 添加Fragment对象到ViewPager
*/
public void addTab(String tag, Class<? extends Fragment> fragment, int catalog) {
Bundle bundle = new Bundle();
bundle.putInt(BUNDLE_TYPE, catalog);
mTabs.add(new ViewPageInfo(tag, Fragment.instantiate(getActivity(), fragment.getName(), bundle)));
}
public void addTab(String tag, Class<? extends Fragment> fragment) {
mTabs.add(new ViewPageInfo(tag, Fragment.instantiate(getActivity(), fragment.getName())));
}
public void addTab(String tag, Fragment fragment) {
mTabs.add(new ViewPageInfo(tag, fragment));
}
/**
* 设置ViewPager当前选中的item
*/
public void setCurrentItem(int index) {
mViewPager.setCurrentItem(index);
}
/**
* 获得ViewPager当前选中的item
*/
public int getCurrentItem() {
return mViewPager.getCurrentItem();
}
/**
* 获得tabs的数量
*/
public int getPageCount() {
return mTabs.size();
}
/**
* ViewPageInformation
* 将Fragment和tag封装成一个对象,形成一一对应关系
*/
public static class ViewPageInfo {
public String tag; //fragment绑定的tag
public View view; //这个View是为了自定义底部导航栏而设置的
public Fragment fragment; //内部持有一个Fragment
public ViewPageInfo(String tag, Fragment fragment) {
this.tag = tag;
this.fragment = fragment;
}
}
}


可以看到 addTab()是在BaseTabFragment类中, 就是将传入的getResources().getString(R.string.new_news)和ListNewsFragment 一一绑定起来. 这里要注意的是三个Tab都是使用的ListNewsFragment, 区分他们显示内容的是addTab()的第三个参数NewsList.CATALOG_ALL,NewsList.CATALOG_WEEK和NewsList.CATALOG_MONTH.

mTabs.add(new ViewPageInfo(tag, Fragment.instantiate(getActivity(), fragment.getName(), bundle)));


这里将三个类别使用Bundle封装, 携带到ListNewsFragment中, 可以通过getArguments()获得传入的Bundle对象.

这里使用的创建Fragment的方法是instantiate()创建一个指定类名的Fragment对象,相当于调用了Fragment的空参构造.

public static Fragment instantiate(Context context, String fname, @Nullable Bundle args){...}


上面留有疑问的地方是mAdapter一直都没有赋值, 那么每次都会走mAdapter==null的流程,每次创建一个新的适配器吗?

BaseTabMainFragment类

/**
* 主界面Tabs的fragment基类
**/
public abstract class BaseTabMainFragment extends BaseTabFragment {
@Bind(R.id.tab_nav)
TabLayout mTabLayout;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
//加载通用布局
return inflater.inflate(R.layout.fragment_universal_tab, container, false);
}
@SuppressWarnings("all")
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
//设置Z轴的高度,显示阴影效果
ViewCompat.setElevation(mTabLayout, 7);
if (mAdapter == null) {
//将TabLayout和ViewPager关联起来,只需要关联一次
mTabLayout.setupWithViewPager(mViewPager);
}
}
/**
* 设置Tab页卡上的View, 这里是由父类提供了抽象,子类提供实现,子类最后调用的是子类自己的该方法
*/
@Override
public TextView setupTabItemView(String tag) {
//设置一个TextView
RelativeLayout layout = (RelativeLayout) View.inflate(mContext, R.layout.view_tab_item, null);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT);
params.weight = 1; //设置权重
layout.setLayoutParams(params); //这里有点疑问? 为什么线性布局的params设置到了RelativeLayout中去了
TextView tabItemView = (TextView) layout.findViewById(R.id.pager_nav_item);
tabItemView.setText(tag); //设置显示为Fragment绑定的tag
return tabItemView;
}
}


代码中有个疑问: 为什么LinearLayout的布局参数可以设置到RelativeLayout中去? 目前没有想清白.

–> 之前是想错了, 现在过来更正. 这里的含义是将一个RelativeLayout作为子布局添加到一个LinearLayout的父布局中, 并设置RelativeLayout的layout_weight为1. [2016-04-28 update]

这里实现tab页卡的方式TableLayout+ViewPager. 从加载layout/fragment_universal_tab.xml可以看到.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--tab navigation-->
<android.support.design.widget.TabLayout
android:id="@+id/tab_nav"
android:background="?attr/colorPrimary"
android:layout_width="match_parent"
android:layout_height="40dp"
app:tabGravity="fill"
app:tabMode="fixed"
app:tabIndicatorColor="?attr/tab_item_underline"
app:tabSelectedTextColor="?attr/tab_item_selected"
app:tabTextColor="?attr/tab_item_unselected" />

<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>


具体实现方式可以参考这篇文章: ViewPager实现页卡的最新方法–简洁的TabLayout(谷歌支持包)

BaseTabNavFragment类

BaseTabNavFragment类也是继承自BaseTabFragment, 用于添加底部导航元素.

(这个在后面的自定义表情回复评论的Fragment中得以使用)

/**
* 自定义底部emotion导航元素的viewpager.
* 使用场景:表情选项卡,轮番等
**/
public abstract class BaseTabNavFragment extends BaseTabFragment implements ViewPager.OnPageChangeListener {
protected LinearLayout mNavLayout;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
//布局中预留了一个LinearLayout用来动态添加View
return inflater.inflate(R.layout.fragment_dot_nav, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
//获得底部tab的LinearLayout容器
mNavLayout = (LinearLayout) view.findViewById(R.id.tab_nav);
super.onViewCreated(view, savedInstanceState);
mViewPager.addOnPageChangeListener(this); //设置ViewPager的滑动监听
}
/**
* 重写父类的addTab()方法,用于添加底部元素
*/
@Override
public void addTab(String title, Class<? extends Fragment> fragment, int catalog) {
super.addTab(title, fragment, catalog);
View view = setupTabItemView(title);
mNavLayout.addView(view);//添加到底部元素
mTabs.get(mTabs.size() - 1).view = view; //设置到ViewPagerInfo中
}
@Override
public void addTab(String title, Class<? extends Fragment> fragment) {
super.addTab(title, fragment);
View view = setupTabItemView(title);
mNavLayout.addView(view);
mTabs.get(mTabs.size() - 1).view = view;
}
@Override
public void addTab(String title, Fragment fragment) {
super.addTab(title, fragment);
View view = setupTabItemView(title);
mNavLayout.addView(view);
mTabs.get(mTabs.size() - 1).view = view;
}
/**
* 设置当前选中的item
*/
public void setCurrentItem(int index) {
mViewPager.setCurrentItem(index);
mTabs.get(index).view.setSelected(true);//设置被tab被选中
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
/**
* 当页面被选中时回调
*/
@Override
public void onPageSelected(int position) {
//遍历改变选中状态
for (int i = 0; i < mTabs.size(); i++) {
if (position == i)
mTabs.get(i).view.setSelected(true);
else
mTabs.get(i).view.setSelected(false);
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
}


看完这一段, 我瞬间对作者抽取能力佩服极了. 抽象的BaseTabNavFragment类既能用于顶部的tab,也能用于底部添加view的tab. 而且tab是动态添加进去的. 这个设计确实可以. 点个赞!

时间有点晚了, 明天再战吧. to be continue….
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: