解读(四):分析主界面顶部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….
相关文章推荐
- Python Cookbook - 1 - 数据结构和算法
- RDD执行延迟执行原理
- RTSPClient工具EasyRTSPClient支持H.265,支持海思等各种芯片平台
- RTSPClient工具EasyRTSPClient支持H.265,支持海思等各种芯片平台
- 机器学习笔记04:逻辑回归(Logistic regression)、分类(Classification)
- react -- 计时器
- sql执行顺序
- Spring 框架使用QQ邮箱发送邮件
- 硬盘存储原理
- CF初体验--Round #348,D
- Office2016 转换零售版为VOL版
- Java动态类加载与重载
- Lable文字自适应宽高
- Openstack小试牛刀之Keystone
- 使用Cydia Substrate 从Native Hook Android Java世界
- 基于C语言的面向对象编程
- 动态语言和静态语言的比较
- Android高效ImageLoader的实现
- Linux学习日志(三)
- DNS 服务