简单仿TabLayout实现个性化Tab,让Tab展现多样化

转载请标明出处:
http://blog.csdn.net/iamzgx/article/details/52663783
本文出自:【iGoach的博客

前段时间,遇到一个问题,今日头条的Tab栏下面大致可以分为2种布局,一种是新闻列表,一种是视频列表。怎么实现这种效果呢?Tab栏有很多种实现方式,使用ViewPagerIndicator、TabLayout或者自己通过HorizontalScrollView写一个,这个没问题,遇到的问题是ViewPager搭配什么Adapter和Fragment结合更好呢,Tab比较少的情况下,个人平时习惯使用FragmentPagerAdapter,那么Tab很多的情况下,使用FragmentPagerAdapter就不太适合了,因为生成Fragment都会保存在内存中。还有没有类似的Adapter呢?显然是有的,那就是FragmentStatePagerAdapter,它会释放不可见Fragment的资源,进行内存优化。所以FragmentStatePagerAdapter更适合Tab多的情况下。所以这篇博客通过HorizontalScrollView+ViewPager+FragmentStatePagerAdapter+Fragment实现一个类似网易新闻客户端的Tab。

概述

有很多应用APP主页都是使用Tab来切换栏目的,比如今日头条,网易新闻客户端,知乎之类的App。仔细去体验这个功能,我们会发现以下几个特点:

  1. 根据栏目的数量分为可滚动和不可滚动
  2. 选中的Tab字体和其他Tab字体颜色和大小有区别
  3. Tab字体底部具有全宽或者和字体等宽的指示器
  4. ViewPager切换Tab栏会随着滚动
  5. Tab栏切换,只会在第一次初始化时请求数据。
  6. Tab栏可能具有红点 或者红点数字提醒
  7. Tab栏中间具有间隔分割

等等。

功能实现

下面通过HorizontalScrollView来实现上面几点,当然,TabLayout也可以实现上面几点,但很多API具有局限性,比如指示器宽度定义,红点提醒等等,很多也是需要自己来实现。所以使用HorizontalScrollView来实现更好点,自由度更大,哈哈。

首先,需要定义一个继承HorizontalScrollView的子视图ZTabLayout。那么我们就需要定义一些属性,仔细想想,可以得到下面一些属性。

 <declare-styleable name="ZTabLayout">
        <attr name="tab_normal_textSize" format="dimension"/>
        <attr name="tab_select_textSize" format="dimension"/>
        <attr name="tab_textColor" format="reference"/>
        <attr name="tab_indicatorColor" format="color"/>
        <attr name="tab_indicatorHeight" format="dimension"/>
        <attr name="tab_min_width" format="dimension"/>
        <attr name="tab_dividerWidth" format="dimension"/>
        <attr name="tab_dividerColor" format="color"/>
        <attr name="tab_dividerPadding" format="dimension"/>
        <attr name="tab_Padding" format="dimension"/>
        <attr name="tab_dividerShow" format="boolean"/>
    </declare-styleable>

上面的属性基本上是看名知意,这里就不多说了,下面就是一些属性的定义了

    //默认字体大小
    private final int DEFAULT_NORMAL_TEXT_SIZE_SP = AppUtils.sp2px(14);
    private int mNormalTextSize = DEFAULT_NORMAL_TEXT_SIZE_SP;
    //选中字体大小
    private final int DEFAULT_SELECT_TEXT_SIZE_SP = AppUtils.sp2px(16);
    private int mSelectTextSize = DEFAULT_SELECT_TEXT_SIZE_SP;
    //字体颜色
    private final int DEFAULT_NORMAL_TEXT_COLOR = Color.BLACK;
    private final int DEFAULT_SELECT_TEXT_COLOR = Color.RED;
    private ColorStateList mTextColor;
    //指示器高度
    private final int DEFAULT_INDICATOR_HEIGHT_DP = AppUtils.dp2px(2);
    private int mIndicatorHeight = DEFAULT_INDICATOR_HEIGHT_DP ;
    //指示器颜色
    private final int DEFAULT_INDICATOR_COLOR = Color.RED;
    private int mIndicatorColor = DEFAULT_INDICATOR_COLOR ;
    //中间线
    private final int DEFAULT_DIVIDER_WIDTH =AppUtils.dp2px(1);
    private int mDividerWidth = DEFAULT_DIVIDER_WIDTH;
    private final int DEFAULT_DIVIDER_COLOR = Color.GRAY;
    private int mDividerColor = DEFAULT_DIVIDER_COLOR;
    private Paint mDividerPaint;
    private int DEFAULT_DIVIDER_PADDING = AppUtils.dp2px(5);
    private int mDividerPadding = DEFAULT_DIVIDER_PADDING ;
    private boolean hasShowDivider = false ;
    //红点显示
    private final int DEFAULT_MSG_ROUND_COLOR = Color.RED;
    private int mMsgRoundColor = DEFAULT_MSG_ROUND_COLOR;
    private SparseBooleanArray mInitSetMap ;
    private SparseIntArray mMsgNumMap;
    private Paint mMsgPaint;
    private Paint mMsgNumPaint;
    private int mMsgNumColor = Color.WHITE;
    private int mMsgTextSizeSp = AppUtils.sp2px(8);
    private int mMsgPadding;
    //tab最小宽度
    private final int DEFAULT_TAB_MIN_WIDTH = AppUtils.dp2px(50);
    private int mMinTabWidth = DEFAULT_TAB_MIN_WIDTH;
    //tab之间的间距
    private int mTabPadding;
    //关联的viewpager
    private ViewPager mViewPager;
    //第一个子View
    private IndicationTabLayout mTabContainer;
    //Tab总数
    private int mTabCount;
    //当前选中的Tab
    private int mCurrentTabPosition;
    //当前切换Tab的偏移量
    private float mCurrentOffset;
    //数据源
    private List<String> mDataList;

属性大致是根据Tab字体属性,指示器属性,Tab之间间隔线属性、红点提醒属性以及Tab之间间距和大小属性和Tab显示的数据源等等。这里说下上面的mInitSetMap 和mMsgNumMap,mInitSetMap 主要是用来保存各个Tab是否要显示红点提醒,mMsgNumMap主要是保存各个Tab要显示红点提醒上的数量。

然后我们就要对一些属性进行初始化,

public ZTabLayout(Context context) {
        this(context,null);
    }
public ZTabLayout(Context context, AttributeSet attrs) {
     this(context, attrs,0);
     }
 public ZTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initStyle(context,attrs);
        /*让子View视图属性生效*/
        setFillViewport(true);
        /*不显示滚动条*/
        setHorizontalScrollBarEnabled(false);
        /*LinearLayout的子View,主要是处理指示器的作用*/
        mTabContainer = new IndicationTabLayout(context);
        /*设置指示器的颜色*/
        mTabContainer.setSelectedIndicatorColor(mIndicatorColor);
        /*设置指示器的高度*/
        mTabContainer.setSelectedIndicatorHeight(mIndicatorHeight);
        /*添加HorizontalScrollView的根View*/
        addView(mTabContainer,0, new HorizontalScrollView.LayoutParams(
                LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
        /*Tab栏要显示的数据*/
        mDataList = new ArrayList<>();
         /*创建指示器画笔*/
        mDividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        /*创建红点画笔*/
        mMsgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        /*创建红点数字画笔*/
        mMsgNumPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        /*保存Tab是否显示红点作用*/
        mInitSetMap = new SparseBooleanArray();
        /*保存Tab显示红点数量*/
        mMsgNumMap = new SparseIntArray();
    }
     /*一些自定义属性的初始化*/
    private void initStyle(Context context, AttributeSet attrs){
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs,R.styleable.ZTabLayout,0,0);
        mNormalTextSize = typedArray.getDimensionPixelSize(R.styleable.ZTabLayout_tab_normal_textSize,DEFAULT_NORMAL_TEXT_SIZE_SP);
        mSelectTextSize = typedArray.getDimensionPixelSize(R.styleable.ZTabLayout_tab_select_textSize,DEFAULT_SELECT_TEXT_SIZE_SP);
        mTextColor = typedArray.getColorStateList(R.styleable.ZTabLayout_tab_textColor);
        if(mTextColor==null)
            mTextColor = createDefaultTextColor();
        mIndicatorHeight = (int) typedArray.getDimension(R.styleable.ZTabLayout_tab_indicatorHeight,DEFAULT_INDICATOR_HEIGHT_DP);
        mIndicatorColor = typedArray.getColor(R.styleable.ZTabLayout_tab_indicatorColor,DEFAULT_INDICATOR_COLOR);
        mMinTabWidth = typedArray.getColor(R.styleable.ZTabLayout_tab_min_width,DEFAULT_TAB_MIN_WIDTH);
        mDividerColor = typedArray.getColor(R.styleable.ZTabLayout_tab_dividerColor,DEFAULT_DIVIDER_COLOR);
        mDividerWidth = (int) typedArray.getDimension(R.styleable.ZTabLayout_tab_dividerWidth,DEFAULT_DIVIDER_WIDTH);
        mDividerPadding = (int) typedArray.getDimension(R.styleable.ZTabLayout_tab_dividerPadding,DEFAULT_DIVIDER_PADDING);
        mTabPadding = (int) typedArray.getDimension(R.styleable.ZTabLayout_tab_Padding,0);
        hasShowDivider = typedArray.getBoolean(R.styleable.ZTabLayout_tab_dividerShow,false);
        typedArray.recycle();
    }
    private ColorStateList createDefaultTextColor(){
        ColorStateList colorStateList = new ColorStateList(new int[][]{{android.R.attr.state_selected}
                ,{0}}, new int[]{DEFAULT_SELECT_TEXT_COLOR,DEFAULT_NORMAL_TEXT_COLOR});
        return colorStateList;
    }

属性初始化完之后,自然我们就要把ViewPager和Tab栏数据源传入进来,然后根据ViewPager来添加Tab的View到mTabContainer线性布局里面,

    /*Tab要显示的数据*/
    public void setDataList(List<String> dataList){
        this.mDataList.clear();
        this.mDataList.addAll(dataList);
    }
    /*很ViewPager相关联,同时根据ViewPager添加Tab的View*/
    public void setupWithViewPager(ViewPager viewPager){
        this.mViewPager = viewPager ;
        if(viewPager == null)
            throw new IllegalArgumentException("viewpager not is null");
        PagerAdapter pagerAdapter = viewPager.getAdapter() ;
        if(pagerAdapter == null)
            throw new IllegalArgumentException("pagerAdapter not is null");
         /*添加ViewPager滚动监听*/
        this.mViewPager.addOnPageChangeListener(new TabPagerChanger());
        /*需要添加Tab的总数*/
        mTabCount = pagerAdapter.getCount();
        /*当前选中的Tab*/
        mCurrentTabPosition = viewPager.getCurrentItem();
        notifyDataSetChanged();
    }
    /*创建Tab的View,然后添加view*/
    public void notifyDataSetChanged(){
        mTabContainer.removeAllViews();
        for (int i = 0 ; i<mTabCount;i++) {
            final int currentPosition = i ;
            TextView tabTextView = createTextView() ;
            tabTextView.setPadding(mTabPadding,0,mTabPadding,0);
            tabTextView.setText(mDataList.get(i));
            tabTextView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View view) {
                    mViewPager.setCurrentItem(currentPosition);
                }
            });
            mTabContainer.addView(tabTextView,new LinearLayout.LayoutParams(0,LinearLayout.LayoutParams.MATCH_PARENT,1));
        }
         /*设置第一个选中的Tab的样式*/
        setSelectedTabView(mCurrentTabPosition);
    }
    /*基本创建TextView*/
    private TextView createTextView(){
        TextView textView = new TextView(getContext());
        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX,mNormalTextSize);
        textView.setTextColor(mTextColor);
        textView.setMinWidth(mMinTabWidth);
        textView.setTypeface(Typeface.DEFAULT_BOLD);
        textView.setGravity(Gravity.CENTER);
        return textView;
    }
   /*处理选中Tab的样式*/
  protected void setSelectedTabView(int position)
    {
        for (int i = 0; i < mTabCount; i++) {
            View view = mTabContainer.getChildAt(i);
            if (view instanceof TextView)
            {
                TextView textView = (TextView)view;
                textView.setSelected(position==i);
                textView.setTextSize(TypedValue.COMPLEX_UNIT_PX,position==i?mSelectTextSize:mNormalTextSize);
            }
        }
    }

接下来,我们就要根据ViewPager的监听事件来对ViewPager滚动时候Tab栏样式改变,

 /*ViewPager的监听事件*/
    private class TabPagerChanger implements ViewPager.OnPageChangeListener{

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        /*通过偏移量来设置指示器的滚动位置*/
            setScrollPosition(position,positionOffset);
        }

        @Override
        public void onPageSelected(int position) {
        /*设置选中Tab的样式*/
            setSelectedTabView(position);
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    }

  private void setScrollPosition(int position, float positionOffset){
        this.mCurrentTabPosition = position ;
        this.mCurrentOffset = positionOffset ;
        mTabContainer.setIndicatorPositionFromTabPosition(position, positionOffset);
        /*处理选择屏幕边界的Tab,HorizontalScrollView自动滚动到响应的位置*/
        scrollTo(calculateScrollXForTab(mCurrentTabPosition,mCurrentOffset), 0);
        /*测试红点功能作用*/
        if(position==3)
            hideMsg(position);
    }
    /*使用TabLayout的计算方式,主要的计算方式是当前选中Tab左边距+当前选中的Tab和下一个Tab宽度中间的中点到mTabContainer屏幕内宽度中点间距做为HorizontalScrollView的水平滚动位置*/
   private int calculateScrollXForTab(int position, float positionOffset) {
            final View selectedChild = mTabContainer.getChildAt(position);
            final View nextChild = position + 1 < mTabContainer.getChildCount()
                    ? mTabContainer.getChildAt(position + 1)
                    : null;
            final int selectedWidth = selectedChild != null ? selectedChild.getWidth() : 0;
            final int nextWidth = nextChild != null ? nextChild.getWidth() : 0;
            return selectedChild.getLeft()
                    + ((int) ((selectedWidth + nextWidth) * positionOffset * 0.5f))
                    + (selectedChild.getWidth() / 2)
                    - (getWidth() / 2);
    }

接下来我们就来绘制Tab之间可能要显示的间隔线和可能要显示的红点提醒,这个操作主要是在onDraw方法里面操作

@Override
protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /*Tab总数小于0,不处理*/
        if (isInEditMode() || mTabCount <= 0) {
            return;
        }
        int height = getHeight();
        int paddingLeft = getPaddingLeft();
        /*当间隔线宽度大于0并且属性定义为需要绘制的时候绘制Tab之间的间隔线*/
        if (mDividerWidth > 0&&hasShowDivider) {
            mDividerPaint.setStrokeWidth(mDividerWidth);
            mDividerPaint.setColor(mDividerColor);
            for (int i = 0; i < mTabCount - 1; i++) {
                View tab = mTabContainer.getChildAt(i);
                /*间隔线绘制在Tab最右边的位置上,mDividerPadding做为顶部和底部的间距*/
                canvas.drawLine(paddingLeft + tab.getRight(), mDividerPadding, paddingLeft + tab.getRight(), height - mDividerPadding, mDividerPaint);
            }
        }
         /*绘制提示消息*/
        for (int i = 0; i < mTabCount - 1; i++) {
            if (mInitSetMap.get(i)) {
                updateMsgPosition(canvas,mTabContainer.getChildAt(i),mMsgNumMap.get(i));
            }
        }
    }
    /*计算绘制红点的位置*/
    private void updateMsgPosition(final Canvas canvas,final View updateView,final int msgNum) {
        if(updateView == null)
            return;
        int circleX, circleY;
        if (updateView.getWidth() > 0) {
            int selectTextPadding = (int) ((updateView.getWidth()-measureTextLength(updateView))/2+0.5f);
            circleX = updateView.getRight()-selectTextPadding+mMsgPadding;
            circleY = (int) ((mTabContainer.getHeight() - measureTextHeight(updateView))/2 -mMsgPadding);
            drawMsg(canvas,circleX,circleY,msgNum);
        }
    }
    /*通过相应画笔绘制红点以及红点上数量大于0的时候绘制红点数量*/
    private void drawMsg(Canvas canvas,int mMsgCircleX,int mMsgCircleY,int mMsgNum){
        mMsgPaint.setStyle(Paint.Style.FILL);
        mMsgPaint.setColor(mMsgRoundColor);
        if(mMsgNum>0){
        /*数量大于0,进行红点和数量绘制*/
            mMsgNumPaint.setTextSize(mMsgTextSizeSp);
            mMsgNumPaint.setColor(mMsgNumColor);
            mMsgNumPaint.setTextAlign(Paint.Align.CENTER);
            String showTxt = mMsgNum>99?"99+":String.valueOf(mMsgNum);
            int mMsgNumRadius = (int) Math.max(mMsgNumPaint.descent()-mMsgNumPaint.ascent(),
                    mMsgNumPaint.measureText(showTxt))/2+AppUtils.dp2px(2);
            canvas.drawCircle(mMsgCircleX+mMsgNumRadius,mMsgCircleY,mMsgNumRadius, mMsgPaint);
            Paint.FontMetricsInt fontMetrics = mMsgNumPaint.getFontMetricsInt();
             /*字体基线的计算*/
            int baseline = (int) ((2*mMsgCircleY - (fontMetrics.descent- fontMetrics.ascent))/2 - fontMetrics.ascent+0.5f);
            canvas.drawText(showTxt, mMsgCircleX+mMsgNumRadius,
                    baseline,mMsgNumPaint);
        }else{
            /*数量大于0,进行小红点绘制*/
           canvas.drawCircle(mMsgCircleX+AppUtils.dp2px(2),mMsgCircleY,AppUtils.dp2px(2), mMsgPaint);
        }
    }
     /*需要显示红点调用方法*/
    public void showMsg(int msgPosition,int msgNum,int msgPadding) {
        mInitSetMap.put(msgPosition,true);
        this.mMsgNumMap.put(msgPosition,msgNum);
        mMsgPadding = msgPadding;
        ViewCompat.postInvalidateOnAnimation(this);
    }
    /*需要隐藏红点调用方法*/
    public void hideMsg(int msgPosition) {
        mInitSetMap.put(msgPosition,false);
        this.mMsgNumMap.delete(msgPosition);
        ViewCompat.postInvalidateOnAnimation(this);
    }

    private float measureTextLength(View measureView){
        if(measureView instanceof TextView){
            TextView textView = ((TextView)measureView);
            String text =textView .getText().toString();
            return textView.getPaint().measureText(text);
        }
        return 0;
    }
    private float measureTextHeight(View measureView){
        if(measureView instanceof TextView){
            TextView textView = ((TextView)measureView);
            Paint textPaint =textView.getPaint();
            return textPaint.descent()-textPaint.ascent();
        }
        return 0;
    }

实现指示器

我们都知道TabLayout是通过SlidingTabStrip来实现指示器处理的。我们也可以通过同样的方法来实现指示器的处理,下面通过IndicationTabLayout来实现指示器

public class IndicationTabLayout extends LinearLayout {
     /*指示器高度*/
    private int mSelectedIndicatorHeight;
    /*指示器画笔*/
    private Paint mSelectedIndicatorPaint;
    /*当前选中的Tab位置*/
    private int mSelectedPosition = -1;
    /*ViewPager滚动的偏移量*/
    private float mSelectionOffset;
    /*指示器左边位置*/
    private int mIndicatorLeft = -1;
    /*指示器右边位置*/
    private int mIndicatorRight = -1;




    public IndicationTabLayout(Context context) {
        this(context,null);
    }

    public IndicationTabLayout(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public IndicationTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setWillNotDraw(false);
        setOrientation(LinearLayout.HORIZONTAL);
        setGravity(Gravity.CENTER_HORIZONTAL);
        mSelectedIndicatorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    }
    /*设置指示器颜色*/
    public void setSelectedIndicatorColor(int color) {
        if (mSelectedIndicatorPaint.getColor() != color) {
            mSelectedIndicatorPaint.setColor(color);
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }
    /*设置指示器高度*/
    public void setSelectedIndicatorHeight(int height) {
        if (mSelectedIndicatorHeight != height) {
            mSelectedIndicatorHeight = height;
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }
    /*外部调用,来移动指示器*/
    public void setIndicatorPositionFromTabPosition(int position, float positionOffset) {
        mSelectedPosition = position;
        mSelectionOffset = positionOffset;
        updateIndicatorPosition();
    }
    private void updateIndicatorPosition() {
        final View selectedTitle = getChildAt(mSelectedPosition);
        int left, right;

        if (selectedTitle != null && selectedTitle.getWidth() > 0) {
        /*字体离最左边位置间距*/
            int selectTextPadding = (int) ((selectedTitle.getWidth()-measureTextLength(selectedTitle))/2+0.5f);
            left = selectedTitle.getLeft()+selectTextPadding;
            right = selectedTitle.getRight()-selectTextPadding;

            if (mSelectionOffset > 0f && mSelectedPosition < getChildCount() - 1) {
                View nextTitle = getChildAt(mSelectedPosition + 1);
                int textPadding = (int) ((nextTitle.getWidth()-measureTextLength(nextTitle))/2+0.5f);
                /*移动到下一个Tab左边需要移动的距离*/
                int moveLeft = nextTitle.getLeft()+textPadding-left;
                 /*移动到下一个Tab右边需要移动的距离*/
                int moveRight = nextTitle.getRight()-textPadding-right;
                left = (int) (left + moveLeft * mSelectionOffset);
                right = (int) (right + moveRight * mSelectionOffset);
            }
        } else {
            left = right = -1;
        }
        setIndicatorPosition(left, right);
    }
    /*判断位置是否相同,不相同才进行绘制*/
    private void setIndicatorPosition(int left, int right) {
        if (left != mIndicatorLeft || right != mIndicatorRight) {
            mIndicatorLeft = left;
            mIndicatorRight = right;
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    /*测量字体宽度*/
    private float measureTextLength(View measureView){
        if(measureView instanceof TextView){
            TextView textView = ((TextView)measureView);
            String text =textView .getText().toString();
            return textView.getPaint().measureText(text);
        }
        return 0;
    }
    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        /*绘制指示器*/
        if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) {
            canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,
                    mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
        }
    }
}

简单使用

功能基本上实现了,下面就来简单使用下
基本布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:orientation="horizontal"
        android:gravity="center_vertical">
        <com.goach.tabdemo.view.ZTabLayout
            android:id="@+id/id_tab_pager_indicator"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1" />
        <ImageView
            android:id="@+id/id_add_channel_entry_iv"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:src="@drawable/add_channel_icon"
            android:paddingLeft="10dp"
            android:paddingRight="10dp"/>
    </LinearLayout>

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

然后就是MainActivity和两个Fragment

public class MainActivity extends AppCompatActivity{
    private ZTabLayout tabLayout;
    private ViewPager mViewPager;
    private MainActivity.MyViewPager myViewPagerAdapter;
    private List<String> mDataList = Arrays.asList("推荐","热点","军事","图片","社会","娱乐","科技","体育","深圳","财经");
     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_simple_tab);
        initView();
        initData();
    }
    private void initView(){
        tabLayout = (ZTabLayout) findViewById(R.id.id_tab_pager_indicator);
        mViewPager = (ViewPager) findViewById(R.id.id_view_Pager);
    }
    private void initData(){
        myViewPagerAdapter = new MainActivity.MyViewPager(getSupportFragmentManager(),mDataList);
        tabLayout.setDataList(mDataList);
        mViewPager.setAdapter(myViewPagerAdapter);
        tabLayout.setupWithViewPager(mViewPager);
        /*显示红点作用*/
        tabLayout.showMsg(3,100,0);
        tabLayout.showMsg(2,0,0);
    }
    /*使用FragmentStatePagerAdapter来实现各个Tab栏目布局*/
    public class MyViewPager extends FragmentStatePagerAdapter {
        private List<String> mDataList;
        private boolean[] mInit ;
        private Map<Integer,LazyFragment> baseFragmentMap = new HashMap<>();
        public MyViewPager(FragmentManager fm, List<String> list) {
            super(fm);
            mDataList = list ;
            mInit = new boolean[mDataList.size()];
        }

        @Override
        public Fragment getItem(int position) {
            LazyFragment fragment = baseFragmentMap.get(position);
            if(fragment==null){
                if(position%2==0)
                    fragment =  OneFragment.newInstance();
                else
                    fragment =  TwoFragment.newInstance(mDataList.get(position));
                baseFragmentMap.put(position,fragment);
            }
            return fragment;
        }

        @Override
        public int getCount() {
            return mDataList.size();
        }
        /*主要处理每个Tab只网络请求一次*/
        @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            super.setPrimaryItem(container, position, object);
            if (!mInit[position]) {
                LazyFragment lazyFragment = (LazyFragment) object;
                if (lazyFragment.getTargetView() != null) {
                    mInit[position] = true;
                    lazyFragment.initNet();
                }
            }
        }
    }
}

抽象出一个方法initNet进行初始化网络请求

public abstract class LazyFragment extends Fragment{

    public abstract void initNet();
    public View getTargetView(){
        return getView() ;
    }

}

然后两个Fragment继承LazyFragment

public class OneFragment extends LazyFragment {
    private View mContainerView;
    public  static OneFragment  newInstance(){
        OneFragment oneFragment = new OneFragment();
        return oneFragment;
    }
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        mContainerView = inflater.inflate(R.layout.fragment_one,container,false);
        return mContainerView;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }

    @Override
    public void initNet() {
    }
}
public class TwoFragment extends LazyFragment {
    private String tabName;
    private TextView mTabNameTv;
    private View mContainerView;
    public static TwoFragment newInstance(String tabTitle){
        TwoFragment twoFragment = new TwoFragment();
        Bundle bundle  = new Bundle();
        bundle.putString("tabName",tabTitle);
        twoFragment.setArguments(bundle);
        return twoFragment;
    }
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        tabName = getArguments().getString("tabName");
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        mContainerView = inflater.inflate(R.layout.fragment_two,container,false);
        return mContainerView;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mTabNameTv = (TextView) mContainerView.findViewById(R.id.id_fragment_two);
        mTabNameTv.setText(String.valueOf("布局2"+tabName));
        initNet();
    }

    @Override
    public void initNet() {

    }
}

接下来测试下,得到结果

这里写图片描述

又或者我们再定义下自定义的那些属性起效没,改变下布局看下效果

<com.goach.tabdemo.view.ZTabLayout
            android:id="@+id/id_tab_pager_indicator"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            app:tab_Padding="25dp"
            app:tab_dividerShow="true"
            app:tab_normal_textSize="14sp"
            app:tab_select_textSize="16sp"
            app:tab_dividerPadding="5dp"
            app:tab_textColor="@color/tab_text_color"
            app:tab_dividerWidth="1dp"
            app:tab_dividerColor="@android:color/holo_blue_light"
            app:tab_indicatorHeight="2dp"
            app:tab_indicatorColor="#FF0000"/>

得到的效果就是

这里写图片描述

最右边的icon主要是想实现今日头部编辑栏目的效果,还没实现完,这里就不说了。

源码下载

发布了56 篇原创文章 · 获赞 50 · 访问量 18万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章