簡單仿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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章