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

解读(四):分析主界面顶部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….

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值